Subscribe on changes!

:class on <input> element breaks Bootstrap 5 dropdown behaviour

avatar
Dec 22nd 2022

Vue version

3.2.45

Link to minimal reproduction

https://sfc.vuejs.org/#__DEV__eNrtVdtu20YQ/ZUpX2QDJtk4jpsSVhE7TePWCdymbg2k7MOKHFJrL2eZvUhVBP17Z3mR5cQu0L70JQREcudydObCmXV02rbJwmOURSe2MLJ1YNH5FpSgeppHzubRdznJptXGwRoMVrCByugGJuw2ySkngEE9mWntrDOi7eQKHSyEgmnw2suj6x/Pzt68yqN9gJwKTdZBY+tBPTlHpTRca6PKryb7O/6WLf7Io/dodB4d5NElYfe8Wvbnq7lBlvyZU+WpcFIT+LYUDn/V3hS4ZzOmJKlmTIB1uEGATfjncWqZC8Am3HYAGnGLZ1qVe/uQcVAKRRfn4G04Q4buQGA6hZHggHaS9tnk3PHBYdMqZsSn4A8nStItMO+KUzx3rrVZmhYlJTe2RCUXJiF0KbVNus3oi2fJYfI0LaV1aWHtnSJpJCUsySPmpRjPupVCO0d0LJLksDbSrYJiLp4+P4rNzJ4e/n528cu8rpcf/zr/pv35pTj9cHl0/Lamy7fPv/54/eTdtT8/fvL96zfL9z+9Ki8OL0T54fCHb1/+9vr4GaMWRlurjawlMa4gTatG+75VhhDhao6gsHIwF1RCaXRb6iWBbtFwJiwU2hgsnFodwMw7cGz+7hw0IVRCctGdhkJpiyCrTlnM+UDcnYq9Qo0KBq4ZKCitY0zQvWUejeVjqmNJk77CnPtSLhhZWMvUuQudkISGE9V6F9dG+7YLI4Qw3seQxusexDayJj7aem4tO1gujCg1qdXWCWaOtjmJna5rhUEWa++4NzC2yNRKYVZsm/Wdmkf87Bvs/sW9LuKZHWB2KH1Ox6vPeMcNkodKKocmftRz6FpYxJU2oZc4Yd3H+aDlo0mKJX8KHNKLQsnilhX3v9R9RluvwcJmc8KtvniIRKrkp1GlXu2Idj1H6cMV+/9Klo2oa5hUy3jGvTrJdqfO5kuh/2Whw8ztz+FtO3Gjg6jfTnHDs/LGauJV183xfFBwZNk42blgnndJxi/jWLZVERbkjU20qVN+S4wnJxtM0DbxzOilRcPAYRfdYaQsXHCeDVKJBs0/YX5i+jjuduaHDvsvi+Nmd2/MPJUKu/UR/mVYXJto8zcj95PT

Steps to reproduce

Create a simple bootstrap dropdown, but add a :class property to the input element which changes a class (e.g. fw-bold) depending on the selected element.

Click to open the dropdown, click on an item which would prompt the above class to change.

See SFC playground example, which has two dropdowns - one with the :class, and one without.

Relevant template snippet

   <div class="dropdown  m-4">
            <input readonly class=" btn dropdown-toggle btn-outline-secondary" :value="val"
                :class="{ 'fw-bold': makeBold() }"
                data-bs-toggle="dropdown">
            <ul class="dropdown-menu filter-dropdown">
                <li v-for="s in vals">
                    <div class="dropdown-item" @click="updateSource(s)">{{ s }}</div>
                </li>
            </ul>
        </div>

What is expected?

The input element re-renders and the dropdown is closed.

What is actually happening?

The input element displays correctly, but the dropdown does not close.

System Info

SFC Playground with Bootstrap 5.2.3

Any additional comments?

This may be in the no-mans-land between Bootstrap and Vue :)

avatar
Dec 22nd 2022

Bootstrap adds a class called show that vue is unaware of, and it seems to make the close behavior depend on that.

When you select "zero", which makes Vue add another class to the element, Vue overrides the classes with the ones it knows - and show is not one of them, so it's effectively being removed.

And bootstrap then doesn't close the dropdown, because apparently, that missing class on the input means it's not in need of closing, or something.

You can verify this by: 1) opening the dropdown 2) removing the show class on the input 3) selecting any entry. It will also not close.

This is a wontfix. Two libraries changing the same element is generally a very bad idea. Vue can't reliably to its job (and neither can bootstrap as we see) when other code is messing in its turf.

avatar
Dec 22nd 2022

...Vue overrides the classes with the ones it knows - and show is not one of them, so it's effectively being removed.

That's more than a little unexpected. Is there a reason that Vue can't leave those classes it doesn't know about untouched? Would having and additional (but less flexible) variant of the :class attribute help?

I think I can solve my particular problem by using a computed CSS style to 'bold' the text rather than the bootstrap utility fw-bold class.

Edit: Yes, that works.

avatar
Dec 22nd 2022

We would need to diff the classList of every element on every component update, which would add non-trivial overhead in the perf-sensitive update path.