Mihai
Mihai

Reputation: 2927

CSS styles not being applied to HTML within a Vue Component

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

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?

Upvotes: 15

Views: 16411

Answers (5)

Q10Viking
Q10Viking

Reputation: 1111

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>

Upvotes: 2

Mitchell Seedorf
Mitchell Seedorf

Reputation: 21

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;
  }
}

Upvotes: 2

crispengari
crispengari

Reputation: 9321

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>

Upvotes: 13

MegaMilivoje
MegaMilivoje

Reputation: 1801

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>

Upvotes: 5

ceejayoz
ceejayoz

Reputation: 179994

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.

Upvotes: 25

Related Questions