The vue-loader doesn't support the styling of dynamic content when used with scoped css.
So I'm thinking that there should be another mode than scoped css to handle the css that can be applied to all the elements as long as the component is loaded.
if the styles are a part of the component then they will be applied only when the component is loaded.
This is extremely useful when using vue-router for changing views, so only the styles needed for the component are loaded and removed with the component.
example:
<template>
<root>
<content></content>
</root>
</template>
<style src="file.css" bound></style>
can be compiled to:
<template>
<root>
<content></content>
<link rel="stylesheet" type="text/css" href="link to scped css bundle">
</root>
</template>
This way it will be very useful to projects using jquery plugins or others where most of the content is generated dynamically.
And as it a build step I don't think it will effect the final build product.
From how I understand it, I don't think that this is a good solution.
If I understand the problem that you want to solve correctly, you are worried that styles that some 3rd party plugin, e.g. a jQuery Plugin, uses, would conflict with other CSS rules. Is that correct?
In that case, your solution makes that a bit more unlikely, but only insofar as the styles are gone when the component is not used.
there will inevitably be cases where this component (which uses your above feature), and some other component, which has CSS rules that conflict the the ones of the plugin, will both be in the document at the same time.
https://jsfiddle.net/Linusborg/2tvttm8a/
So this seems to add a false sense of security, and as soon as the above situation occurs, you will be wondering why sometimes the styles are ok, and sometimes they are not.
I think a far better approach would be to tackle this hands on and include the styles always - the sooner you find you that something is clashing, the better.
include the styles always
This is not an option for me because of the size of my project if the css of all plugins is included the page will become heavy.
@kazupon made it clear that it is not possible to add scopeId to dynamic content, so the solution is to daclare them globally,
another case is that I need to apply some styling to body like background-color e.t.c.. when a specific route/component is loaded.
eg- main layout--normal body
another layout like special pages like login section-- body has styles like bg-color, overflow e.t.c..
the way I see it there is no way to do this with scoped css.
and if the styles are given globally then they stay and apply to the body even after the route/component is changed.
that's why I think there should be a way to remove styles.
So this seems to add a false sense of security.
As this is going to a new option the consequences can be clearly documented and made clear to any one who decides to use this option
This is not an option for me because of the size of my project if the css of all plugins is included the page will become heavy.
In that case, just use webpack code splitting. Wrap the plugin's css and js in a module and lazy-load that in your component when you need it.
Then the css will be loaded only when needed, but stay in the page (not a problem in my book)
another case is that I need to apply some styling to body like background-color e.t.c.. when a specific route/component is loaded.
eg- main layout--normal body
another layout like special pages like login section-- body has styles like bg-color, overflow e.t.c..
That should be solved by adding/removing classes, not loading/ unloading css from a component. Css in vue files is meant for those components only.
In that case, just use webpack code splitting. Wrap the plugin's css and js in a module and lazy-load that in your component when you need it.
Currently doing that. that causes some problems.
if css in page a overrides css in page b:
if page b loads first everything is okay, But if page a loads first and then page b then page b is broken due to css override.
Then the css will be loaded only when needed, but stay in the page
that staying on the page is my problem.
Css in vue files is meant for those components only.
the way i want is the routing work like any normal html site,
the css in that page is only for that page and no other styles to worry about,
but with vue-loader when global styles remain they are bound to override some other styles in some other pages.
I think the original poster was not so much talking about styles from external libraries conflicting with other styles, but rather applying styles from within the .vue file to dynamically created DOM nodes within the template.
See the following as an example case:
<template>
<div id='parent'>
<h1>Scoped Style Test for Dyanmic Components</h1>
<p>This is a static paragraph</p>
</div>
</template>
<script>
export default {
name: "test",
mounted() {
let node = document.createElement('p');
let text = document.createTextNode('This is a dynamic paragraph');
node.appendChild(text);
document.getElementById('parent').appendChild(node);
}
}
</script>
<style scoped>
p {
font-size: 3em;
}
</style>
Please see the full gist.
The intention here is that all paragraphs within this component have a font-size of 3em. This is true for the static paragraph within the template. The second paragraph (created from the mounted() function) will not have this style applied.
The reason for this is as part of the css compilation the css selector p is converted to p[data-v-xxxxxxx] but the data-v-xxxxxx attribute is only added to the static elements. One way to fix it would be to change things so instead of p[data-v-xxxxxx] the selector is converted to [data-v-xxxxxx] p. This would have the same effect of limiting scope but would apply the formatting to all p elements added within the component in the future. I'd be happy to help create a patch for this but I can't quite work out exactly where the p -> p[data-v-xxxxxx] translation is going on.
There are two things that would complicate this solution. Firstly, the style for the root node still needs to be applied as div[data-v-xxxxxx] rather than [data-v-xxxxxx] div and so we'd need to test for the root node specifically.
Secondly, it's not clear how this would work with components composed in via slots. Do they inherit the styles (as they would if this was just straight html; this is what would happen with this implementation) or should they not be affected by styles in their parent component (might be harder to do with this approach).
The workaround is not to use the scoped attribute in which case everything will work fine in this component. The problem is however the styling will leak out into the rest of the system and all p elements will have a size of 3em. To avoid this, styles could be applied based on a class rather than the element and a class name chosen in such a way that it would be unlikely to conflict with any class names outside of the component (e.g. by using the BEM naming convention).
For elements that are created dynamically by the dev with them DOM API instead of letting Vue handle this, I think the solution is pretty straightforward: get the data-v-* attribute from the component's root element and apply it to all dynamically created elements.
Since these elements are handled outside of the dom that Vue controls, I don't think vue-loadet should have any responsibility in their styling.
Fair enough I guess. The main drawback having to manually add the data-v-* attribute is it makes it harder to use third party non-vue libraries that do DOM manipulation. I can understand the point that because these work behind Vue's back you may explicitly not want to support them natively but rather require some developer workaround.
As I mentioned above, simply leaving out the scoped attribute is probably the easiest way though it does potentially allow styles to leak out of your components.
The recommendation to manually add the data-v-* attribute is probably the best method to preserve component encapsulation but will require some extra coding, which for third party libraries would require some understanding of their implementation details potentially.
Just as an example of how the manual data-v-* workaround could be implemented, I've modified my component above such that it should now work with the scoped attribute. (I've used jQuery $.each() in my code but I'm sure it could be done easily enough in vanilla js).
<template>
<div id='parent'>
<h1>Scoped Style Test for Dyanmic Components</h1>
<p>This is a static paragraph</p>
</div>
</template>
<script>
export default {
name: "test",
mounted() {
let node = document.createElement('p');
let text = document.createTextNode('This is a dynamic paragraph');
node.appendChild(text);
// Support scoped style in vue loader
let re = /^data-v-\w{8}$/
$.each(this.$el.attributes, (k, v) => {
if (re.exec(v.name))
node.setAttribute(re.exec(v.name)[0], "");
}
document.getElementById('parent').appendChild(node);
}
}
</script>
<style scoped>
p {
font-size: 3em;
}
</style>
Closing outdated issues - this should be solvable with /deep/ or >>> selectors in the latest version.
Most helpful comment
This is not an option for me because of the size of my project if the css of all plugins is included the page will become heavy.
@kazupon made it clear that it is not possible to add scopeId to dynamic content, so the solution is to daclare them globally,
another case is that I need to apply some styling to body like background-color e.t.c.. when a specific route/component is loaded.
eg- main layout--normal body
another layout like special pages like login section-- body has styles like bg-color, overflow e.t.c..
the way I see it there is no way to do this with scoped css.
and if the styles are given globally then they stay and apply to the body even after the route/component is changed.
that's why I think there should be a way to remove styles.
As this is going to a new option the consequences can be clearly documented and made clear to any one who decides to use this option