简体   繁体   中英

How to prevent Vue.js from automatically merging CSS classes?

I recently started working with Vue.js and I really enjoy it but I have come across something that I just can't figure out how to do. I have searched and read the documentation but found no solution.

When you add a CSS class to any of your components Vue.js will automatically add or merge the CSS classes with the root element in your child component.

This is a nice feature but I need to deactivate this feature for a specific component since I want the classes to be added to a child element of the root element.

I made this fiddle to illustrate http://jsfiddle.net/rasmuswoelk/9m2j0a9s/3

<div id="demo">
  <child-component class="some-class"></child-component>
</div>

(The "some-class" is being added automatically and adds a green background color)

How can I prevent Vue.js from automatically merging CSS classes?

I think that you will want to wrap the whole thing in another <div></div> as Vue will merge the classes on the top element. If you just want the style for <h1> then try doing this:

template: `<div><h1 class="child">Hello</h1></div>`

This should give you the expected behavior, let me know if it doesn't work.

Updated I disagree that it is intuitive to have a class that is applied to a component be moved by that component to an inner element, so my recommendation is still: Pass it as a prop.

However, it is possible to do what you want to do, with some caveats. The component will have a list of classes that belong on the outer element. In mounted , it looks at $el.classList and finds the classes that are not among the known outer element classes. Those get applied to the inner element and removed from the outer element. Caveat: if one of the outer element classes is applied to the component, it will not be moved to the inner element. And any updates to the applied class will not be caught.

 Vue.component('child-component', { template: `<div :class="outerClasses"><h1 :class="childClasses">Hello</h1></div>`, data() { return { outerClasses: ['child'], childClasses: [] }; }, mounted() { this.childClasses = Array.from(this.$el.classList).filter((c) => !this.outerClasses.includes(c)); for (const c of this.childClasses) this.$el.classList.remove(c); } }); var demo = new Vue({ el: '#demo' }); 
 .child { border: 1px solid red; padding: 20px; text-align: center; } .some-class { background-color: green; } 
 <script src="//vuejs.org/js/vue.min.js"></script> <div id="demo"> <child-component class="some-class abc"></child-component> </div> 

You can't abstract away the wrapper div that you have in your template, and trying to make the component work in such as way as though that div doesn't exist will only lead to confusion and incorrect styling.

Let's say that you managed to get your component to apply the outer class to a nested element in its template (which is what you want). Now when people use that component and apply a class to it, the style is applied to the input element and it is as though the whole component consists solely of the input element and there is no such wrapper div (this is the behavior you want).

The problem with this is that some styles will work (like background-color , or any appearance style), but other styles that affect the layout won't work (like position: absolute , because it will cause the input element to be positioned relative to the wrapper div which is not what was expected).

People who use that component should know about how the template is structured so they can style it correctly, because in reality there is a wrapper div there which they need to take into account.

I should mention you might be able to abstract away the wrapper div by using web components or the shadow DOM, but that's out of context of Vue.

A more simpler way (imo) is to bind the incoming class attribute and dynamic class prop to the data and remove them from the vnode (virtual node) during the created() hook.

 Vue.component('child-component', { template: ` <div class="child"> <h1 :class="staticClass">Hello</h1> </div> `, data() { return { staticClass: [], } }, created() { // dynamic :class prop this.staticClass.push(this.$vnode.data.class) // delete if not applying on this.$el delete this.$vnode.data.class // static class attr this.staticClass.push(this.$vnode.data.staticClass) // delete if not applying on this.$el delete this.$vnode.data.staticClass }, }); var demo = new Vue({ el: '#demo', data: { dynamicClass: 'dynamic-class' }, }); 
 .child { border: 1px solid red; padding: 20px; text-align: center; } .some-class { background-color: green; } .dynamic-class { border: 1px solid yellow; } 
 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script> <div id="demo"> <child-component :class="dynamicClass" class="some-class"></child-component> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM