Vue: <input> rerenders while changes affects only sibling elements

Created on 26 Oct 2017  Â·  15Comments  Â·  Source: vuejs/vue

Version

2.5.2

Reproduction link

https://codepen.io/anon/pen/OxKqbL?editors=1111

Steps to reproduce

Please open codepen example and type something into input

What is expected?

Input should not lose focus

What is actually happening?

input rerenders and loses focus


happens only if there is such conditional elements exists before and after input. Workaround is to use v-show instead of v-if in such cases.

bug has workaround

Most helpful comment

Actually, it is the same input element, but first remove from the document then move it to other places, for example:

it first renders like

 <!----> // A
<input />
 <!----> // B

then you focus input and input some words, then virtual dom insert <span>before</span>, and it then parent.insertBefore(input, A), it moves the input element before the A comment element, so it will eventually lose focus, then it will delete the comment node.

so, I think you could use v-show better to avoid the insertBefore move problem(remove from the document then insert before the reference node, in this case, which is comment A ). This is the default action for virtual dom of vue which doesn't replace comment node.

All 15 comments

You can also simply give the <input> a key to ensure it is not replaced.

i tried your solution in codepen and still cant get it working.

i tried this
<input type="text" v-model="inputVal" :key="1">

U need to have same DOM structure before your input. Look this. It can be after update your DOM before your input tag

Actually, it is the same input element, but first remove from the document then move it to other places, for example:

it first renders like

 <!----> // A
<input />
 <!----> // B

then you focus input and input some words, then virtual dom insert <span>before</span>, and it then parent.insertBefore(input, A), it moves the input element before the A comment element, so it will eventually lose focus, then it will delete the comment node.

so, I think you could use v-show better to avoid the insertBefore move problem(remove from the document then insert before the reference node, in this case, which is comment A ). This is the default action for virtual dom of vue which doesn't replace comment node.

You can also simply give the <input> a key to ensure it is not replaced.

<input> element focus is lost in either case.

I have tried to fix this issue
But I encountered some problem
have we got some way to access vnode from html element?
I want to get vnode path as https://github.com/google/incremental-dom/issues/237
But I did not find a good way to do that
have i missing something? or have other better solution?😅

@Kingwl you can use v-show. It works correctly.

Alternative way:
https://codepen.io/furrya_black/pen/ZVVLpQ

Just html-layout usually has a more complex structure. What do you thing?
As a proof you can see any css framework: material disign, bulma, bootstrap, etc (what else)

UPD: if your case require rerender-mehanic (with v-if)

It also seems to break (loose focus) if the spans are shown initially:

https://codepen.io/janschoenherr/pen/NoJpoY?editors=1111

I would think that it doesn't need to do insertBefore in this case.

F.Y.I. It seems that #9473, #9496, #9808 all share the same root cause.

I'm using Vue 2.6.7 and this issue is still relevant, i was able to work it out by using v-show instead of v-if on a sibling, but it's still confusing why it doesn't work when v-if is used

I found that @focus is not reliable when using DOM manipulation, such as v-if and therefore, directives would be the best approach:

<template>
    <form method="POST"
          autocomplete="off"
          @submit.prevent="onSubmit">

<!--     We set the directive here v-focus with the function prop handleFocus -->
        <div class="flex mt-1 rounded-md shadow-sm">
            <input id="title"
                   type="text"
                   v-focus="handleFocus"
                   v-model="form.title">
        </div>

    </form>
</template>


<script>
        export default {
            name: "FormComponent",
            data() {
                return {
                    // A form class example, but you could just have a var
                    form: new this.$form({
                        title: null,
                    })
                }
            },
            directives: {
                focus: {
                    inserted(el,binding) {
                        // We set focus on the element
                        el.focus();
                        // optional if you had other operations. Data and props, etc. are not available inside
                        // a direective uless yoru decorate the provider, which is a pain. So we call the binding
                        // which is the handleFocus set on input. We pass the el but we could inject nothing or a event, binding, etc
                        if (!_.isUndefined(binding.value)) binding.value(el)
                    }
                }
            },
            methods: {
                handleFocus: function() {
                    // For example reset a form v-model
                    this.form.title = null;
                },
            }
        }
    </script>

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lmnsg picture lmnsg  Â·  3Comments

julianxhokaxhiu picture julianxhokaxhiu  Â·  3Comments

wufeng87 picture wufeng87  Â·  3Comments

gkiely picture gkiely  Â·  3Comments

loki0609 picture loki0609  Â·  3Comments