16

I am trying to create a rotating text animation using Vue.js and I used this CodePen as inspiration.

  • I got all the HMTL elements properly in place (i.e., as in the CodePen mentioned). In short:

    • each word is formed of several <span> elements, each containing one letter.
    • following a specific time interval, each <span> that holds a letter gets applied an .in and .out CSS class. This goes on indefinitely.
    • here is what it looks like in the DOM:

      enter image description here

  • the problem is that no matter what CSS selectors I use, I can't target the .in and .out classes, unless I do it via Developer Tools in Chrome:

    • original output:

      enter image description here

    • output after I added the classes in Developer Tools:

      enter image description here

Here is the bare minimum code of my Vue Component:

<template>
    <div id="app-loading">  
        <div class="words">
            <span v-for="setting in settings" v-html="setting.lettersHTML" :id="setting.id" class="word"></span>    
        </div>
    </div>
</template>


<script>
    export default {
        data() {
            return {
                settings: [
                    { word: 'WordOne', id: 1, lettersArray: null, lettersHTML: null },
                    { word: 'WordTwo', id: 2, lettersArray: null, lettersHTML: null }
                ],
                currentWord: 1
            }
        },

        created() {
            this.splitLetters();
        },

        mounted() {
            setInterval(this.changeWord, 1500);
        },

        methods: {
            splitLetters() {
                this.settings.forEach((setting) => {
                    let letters = [];
                    for (let i = 0; i < setting.word.length; i++) {
                        let letter = `<span class="letter">${ setting.word.charAt(i) }</span>`;
                        letters.push(letter);
                    }
                    setting.lettersArray = letters;
                    setting.lettersHTML = letters.join('');
                });
            },

            changeWord() {
                let current = document.getElementById(this.currentWord).getElementsByTagName('span');
                let next = (this.currentWord == this.settings.length) ? document.getElementById(1).getElementsByTagName('span') : document.getElementById(this.currentWord + 1).getElementsByTagName('span');
                // Animate the letters in the current word.
                for (let i = 0; i < current.length; i++) {
                    this.animateLetterOut(current, i);
                }
                // Animate the letters in the next word.
                for (let i = 0; i < next.length; i++) {
                    this.animateLetterIn(next, i);
                }
                this.currentWord = (this.currentWord == this.settings.length) ? 1 : this.currentWord + 1;
            },

            animateLetterOut(current, index) {
                setTimeout(() => {
                    current[index].className = 'letter out';
                }, index * 300);
            },

            animateLetterIn(next, index) {
                setTimeout(() => {
                    next[index].className = 'letter in';
                }, 340 + (index * 300));
            }
        }
    }
</script>


<style lang="scss" scoped>
    #app-loading {
        font-size: 4rem;
    }

    .words, .word {
        border: 1px solid rosybrown;
    }

    .letter {
        text-decoration: underline; // Not working.
    }

    .letter.in {
        color: red; // Not working.
    }

    .letter.out {
        color: blue; // Not working.
    }
</style>

What goes wrong that prevents these classes from being applied?

5 Answers 5

26

You're using v-html, but that doesn't work with scoped styles.

DOM content created with v-html are not affected by scoped styles, but you can still style them using deep selectors.

Sign up to request clarification or add additional context in comments.

2 Comments

You made my day! Thanks for the link.
As @mitchell-seedorf and @q10viking describe below: ::v-deep is deprecated. Use :deep(...) instead. See vuejs.org/api/sfc-css-features.html#deep-selectors
13

This worked for me:

<template>   
    <div class="a" v-html="content"></div> 
</template>

<script>   
    export default { 
        data() {
           return {
              content: 'this is a <a class="b">Test</a>',
           }
        },
    } 
</script>

<style scoped>   
.a ::v-deep .b { 
     color: red;
 }
</style>

1 Comment

As @mitchell-seedorf and @q10viking describe below: ::v-deep is deprecated. Use :deep(...) instead. See vuejs.org/api/sfc-css-features.html#deep-selectors
5

Yes,

v-html

doesn't work with scoped styles.

As Brock Reece explained in his article Scoped Styles with v-html, it should be solved like this:

<template>   
    <div class="a" v-html="content"></div> 
</template>

<script>   
    export default { 
        data() {
           return {
              content: 'this is a <a class="b">Test</a>',
           }
        },
    } 
</script>

<style scoped>   
.a >>> .b {
color: red;   
}
</style>

2 Comments

Strange enough, to me .a >>> .b does not work. Instead, .a ::v-deep .b works every time.
As @mitchell-seedorf and @q10viking describe below: ::v-deep is deprecated. Use :deep(...) instead. See vuejs.org/api/sfc-css-features.html#deep-selectors
2

Most answers are deprecated now that Vue3 is out. Up-to-date usage of deep selector:

.letter{
  &:deep(.in) {
    color:blue;
  }
  &:deep(.out) {
    color:red;
  }
}

Comments

2

Vue3: In Single-File Components, scoped styles will not apply to content inside v-html, because that HTML is not processed by Vue's template compiler.

You can use :deep() inner-selector in Vue3 project.

Here is a example:

<script setup lang="ts">

import {onMounted,ref } from 'vue'
const content = ref("")

onMounted(()=>{
    window.addEventListener('keydown',event =>{
        content.value = `
            <div class="key">
                <span class="content">${event.keyCode}</span>
                <small>event.keyCode</small>
            </div>
        `
    })
})

</script>

<template>
    <div class="container" v-html="content">
    </div>
</template>

<style lang="scss" scoped>  

.container{
    display: flex;
    
    :deep(.key){
        font-weight: bold;
        
        .content{
            font-size: 1.5rem;
        }
        
        small{
            font-size: 14px;
        }
    }
}
</style>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.