vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
208.09k stars 33.68k forks source link

Focus lost in a list rendered component when simultaneously removing first ellement and appending a new one #6966

Closed rasmushaglund closed 7 years ago

rasmushaglund commented 7 years ago

Version

2.5.2

Reproduction link

https://codepen.io/spenspazmer/pen/boZeyz?editors=1011

Steps to reproduce

Focus one of the letters, press key down/up. Observe the difference in behaviour.

What is expected?

Focus should be retained when adding/removing components in the list both when pressing up and down.

What is actually happening?

Pressing key down forces a re-rendering and focus is lost. Pressing key up still works as expected.


This is related to https://github.com/vuejs/vue/issues/6857 and preventing me from making https://github.com/Akryum/vue-virtual-scroller/ work with keyboard navigation.

posva commented 7 years ago

Thanks for showcasing the focus issue. To me, this is still the same problem as #4362 because refocusing arbitrary elements at every dom update could be expensive and will probably have many edge cases. The problem comes from the fact that all elements are re-rendered instead of what actually changed. But, I don't actually see this issue in your repro because the focused element disappears, so if you want to focus the first one, you'll have to do that manually: https://codepen.io/anon/pen/dZPZNV?editors=1011

rasmushaglund commented 7 years ago

Yeah exactly @posva it's possible to make this work pretty decent, but imagine we use an external library (which in it self maybe does a setTimeout or a nextTick, in this case vue-virtual-scroll) for handling the updating of the dom and we don't have a callback for the update. In that case we can't be sure of when it's safe to focus the correct element, and even if we were, that would probably need to be done in a nextTick and thus increasing latency.

The desired functionality here is that pressing the down key should focus the next element, from what I can see your example tries to set focus on the first element again?

I've played around with this issue quite a bit and I've haven't gotten it to work without major timing issues. For example imagine holding down the down key and constantly focusing on the next element (maybe while scrolling) and scrolling just so that the last visible element is focused. In that case the timing issues will sometimes make the style of the focused element and scroll position out of sync and result in a chunky experience.

When I compare this experience with virtual scrollers made using other frameworks like React or Angular, I notice that this is not impossible to achieve.

You can get around this by not using focus at all, but that removes some built in functionality that one would need to re-implement.

And I agree, the root cause for this is that all elements are re-rendered.

Edit: The main problem for me is illustrated in the flow below:

  1. Determine what item should be selected
  2. Remove items in front/after
  3. Put focus on the element and set correct style
  4. Elements before/after are removed
  5. A complete re-render is triggered
  6. Focus is lost
  7. Put focus on element again

When looking at that flow you could guess that focus and updated style for the elements would run in the same tick, but because of the re-rendering it will occur 1-2 ticks later. This could be a big problem if we want style and focus to be synchronized for example.

yyx990803 commented 7 years ago

Please don't reopen an issue that has been clearly marked as a duplicate. This is a known issue.