Vue-loader: Scoped styles don't apply to v-html injections

Created on 28 Mar 2017  ·  16Comments  ·  Source: vuejs/vue-loader

Version

11.0.0

Reproduction link

https://github.com/foundryspatial-duncan/vue-loader-issue-demo/blob/master/src/App.vue

Steps to reproduce

  1. In a single-file Vue component (.vue file), add some scoped styles (with the scoped property on the style tag).
  2. Inject some HTML that matches your scoped style's selector with v-html

What is expected?

The styles would affect the injected HTML

What is actually happening?

The styles do not affect the injected HTML


Looks like HTML injected with v-html aren't given the "data-v-[hash]" property (like data-v-08ce5946).
I think there's a proposal somewhere to only add the data-v-[hash] to the component's root element and then _prefix_ all the styles to scope them. That would solve this issue as well.

Most helpful comment

You can use the /deep/ selector to get your styles to be applied:

<div class="description" v-if="description" v-html="description"></div>

.description {
  /deep/ p {
    margin-bottom: 10px;
  }
}

All 16 comments

Prefixing is simply different from what scoped CSS does. The HTML injected is raw and we are likely not going to change this. You can just give the container element a class and use nested selectors to style the raw HTML.

There still has a problem.
some info:

"dependencies": {
"vue": "^2.2.4",
"vue-highlightjs": "^1.2.2",
"vue-markdown": "^2.1.3"
},
"devDependencies": {
"autoprefixer": "^6.7.7",
"babel-cli": "^6.24.0",
"babel-eslint": "^7.2.1",
"babel-loader": "^6.4.1",
"babel-plugin-component": "^0.9.1",
"babel-plugin-dynamic-import-node": "^1.0.1",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.24.0",
"babel-preset-stage-2": "^6.22.0",
"css-loader": "^0.28.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^10.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-html": "^2.0.1",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-node": "^4.2.2",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^2.3.1",
"eventsource-polyfill": "^0.9.6",
"express": "^4.15.2",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.28.0",
"ip": "^1.1.5",
"node-sass": "^4.5.2",
"ora": "^1.2.0",
"progress-bar-webpack-plugin": "^1.9.3",
"sass-loader": "^6.0.3",
"url-loader": "^0.5.8",
"vue-loader": "^11.3.4",
"vue-router": "^2.4.0",
"vue-style-loader": "^2.0.5",
"vue-template-compiler": "^2.2.6",
"webpack": "^2.3.1",
"webpack-dev-middleware": "^1.10.1",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
}

template:

<span class="focus-btn" @click="focus" :class="{'focued': isFocus}" v-html="isFocusText"></span>

script:

computed: { isFocusText () { return this.isFocus ? '已关注' : '<i class="plus">+</i>关注'; } }

scss:

<style scoped lang="scss"> .focus-btn i.plus{ color:red;} </style>

Omit some unrelated code.

it's style not work till remove 'scope' in style tag.

Should I remove 'scope' in style tag or remove '\' in isFocusText?

Whether I style element in raw html?

You could use something like v-if to show your focus text instead. @yyx990803 said above that they probably won't address this, but if you're using scss you can "scope" your styles by nesting everything in a selector for your component's root element. So, not the end of the world 👍

You can use the /deep/ selector to get your styles to be applied:

<div class="description" v-if="description" v-html="description"></div>

.description {
  /deep/ p {
    margin-bottom: 10px;
  }
}

/deep/ selection helped me a lot. Thanks :)

I stumbled upon this today and oddly, nothing was working (unscoped css, global css).

I noticed that while this solution worked, the /deep/ selector was deprecated by some time ago and throws errors in IE: https://stackoverflow.com/questions/25609678/what-do-deep-and-shadow-mean-in-a-css-selector

I then found this: https://medium.com/@brockreece/scoped-styles-with-v-html-c0f6d2dc5d8e

Which recommends something along the lines of:

.description >>> p {
  margin-bottom: 10px;
}

Which works as well, without a deprecated selector.

edit: Then again, the vue docs say >>> is just an alias for /deep/, so I'm not sure anymore https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors

Since the discussion is still going on, I just wanted to draw attention to my comment above about using the root element's selector to scope your styles. It can produce simpler, more predictable style behaviors and doesn't muddy up the DOM with the extra scope attributes. You just have to be aware of how one component's styles might affect its child components. I use the scoped attribute pretty sparingly these days, in favor of a slightly more thoughtful design. I think it's worth considering if it suits your project!

Yes I totally agree - something very weird was happening where even unscoped Styling in the component and even the global Stylesheet was not working - and only the deep selector worked - in the end it turned out webpack was hung up on something and restarting it fixed everything.

I agree I would aim for a root selector instead as well and switched things over when it worked again.

You can use the /deep/ selector to get your styles to be applied:

<div class="description" v-if="description" v-html="description"></div>

.description {
  /deep/ p {
    margin-bottom: 10px;
  }
}

wow! /deep/ works. learned something today :)

I am using <style lang="scss" module> and I am passing a raw html from my backend config. footer: 'some text \n'+ '<a href="#" :class="$style.tc">link</a>',
In the original component:
<div :class="$style.footer"> <p :class="$style['footer-text']" v-html="footer"></p> </div>


.footer-text{ /deep/ a{ color: #1665c0; text-decoration: none; } }
The above style is applied to the link element and it works fine. But when I replace 'a' with '.tc'(class name) it does not work.
.footer-text{ /deep/ .tc{ color: #1665c0; text-decoration: none; } }
Also the alias for '/deep/' ,i.e, '>>>' does not work

/deep/ p

not working with with scss

/deep/ p

not working with with scss

In my case, with scss, it is working pretty fine:

    /deep/ &-icon {
      // See: https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
      font-size: 30px;
    }

Not sure if I missed it, but would be great to put a mention in the Docs about this not working (might save some churn for other noobs like myself :-) )

You can use the /deep/ selector to get your styles to be applied:

<div class="description" v-if="description" v-html="description"></div>

.description {
  /deep/ p {
    margin-bottom: 10px;
  }
}

/deep/ works bro, tks so much

/deep/ is not working in [email protected] use ::v-deep instead.
.right { ::v-deep * { ... } }

Try this :)

<div class="content" v-html="content"></div>
.content ::v-deep {
  h2 {
    color: blue;
  }
  a {
    color: blue;
  }
}
Was this page helpful?
0 / 5 - 0 ratings