skip to Main Content

I have a function that is supposed to change words every few seconds. Everything works as expected, but I get following error every few seconds in the VSC console:

TypeError: Cannot set properties of null (setting ‘textContent’) at
Timeout._onTimeout (…/components/home/Hero.vue:36:28) at
listOnTimeout (node:internal/timers:564:17)

I don’t understand why I get this error, because when I console.log word.value, I get the element. So I have access to it and the function is working, the words change every few seconds, as expected. I’m a bit confused and don’t know how to get the problem solved. I don’t get the error in the Google Chrome console, only in VSC.

<span ref="word"></span>
onMounted(() => {
  randomizeText()
  wordChanger
})

onBeforeRouteLeave(() => {
  clearTimeout(wordChanger)
})

const wordChanger = setInterval(randomizeText, 4000)

const word = ref(null)
const words = reactive(['Word-1', 'Word-2'])
let i = 0

function randomizeText() {
  i = randomNum(i, words.length)
  const newWord = words[i]

  setTimeout(() => {
    word.value.textContent = newWord
  }, 200) // time to allow opacity to hit 0 before changing word
}

function randomNum(num, max) {
  let j = Math.floor(Math.random() * max)

  // ensure diff num every time
  if (num === j) {
    return randomNum(i, max)
  } else {
    return j
  }
}

3

Answers


  1. Instead of using the const word = ref(null) as refs. You can directly call it the constant word in your template. It should still work the same.

    <template>
      <div>
        <span>{{ word }}</span>
      </div>
    </template>

    Inside the setTimeout function, change this word.value.textContent = newWord to this word.value = newWord.

    Here is the updated codes. Tested and it works.

    <script setup lang="ts">
    onMounted(() => {
      randomizeText()
      wordChanger
    })
    
    onBeforeRouteLeave(() => {
      clearTimeout(wordChanger)
    })
    
    const wordChanger = setInterval(randomizeText, 4000)
    
    const word = ref('') // UPDATED
    const words = reactive(['Word-1', 'Word-2'])
    let i = 0
    
    function randomizeText() {
      i = randomNum(i, words.length)
      const newWord = words[i]
    
      setTimeout(() => {
        word.value = newWord // UPDATED
      }, 200) // time to allow opacity to hit 0 before changing word
    }
    
    function randomNum(num, max) {
      let j = Math.floor(Math.random() * max)
    
      // ensure diff num every time
      if (num === j) {
        return randomNum(i, max)
      } else {
        return j
      }
    }
    </script>
    <template>
      <div>
        <span>{{ word }}</span>
      </div>
    </template>

    Hope that helps you.

    Login or Signup to reply.
  2. This is probably a timing issue (element not mounted while it is being changed by the code)

    You can do this without setting the textContent property dirctly, by using the v-html directive and just changing the referenced variable (ref)
    Also, you can just start the interval in the onMounted() hook.

    import {onMounted, reactive, ref} from "vue";
    import {onBeforeRouteLeave} from "vue-router";
    
    let wordChanger;
    
    onMounted(() => {
        randomizeText()
        wordChanger = setInterval(randomizeText, 4000);
    })
    
    onBeforeRouteLeave(() => {
        clearTimeout(wordChanger)
    })
    
    let word = ref('Hoi')
    const words = reactive(['Word-1', 'Word-2'])
    let i = 0
    
    function randomizeText() {
        i = randomNum(i, words.length)
        const newWord = words[i]
    
        setTimeout(() => {
            word.value = newWord
        }, 200) // time to allow opacity to hit 0 before changing word
    }
    
    function randomNum(num, max) {
        let j = Math.floor(Math.random() * max)
    
        // ensure diff num every time
        if (num === j) {
            return randomNum(i, max)
        } else {
            return j
        }
    }
    <span v-html="word"></span>
    Login or Signup to reply.
  3. You probably are using the ref ‘word’ wrong and some other details I share below:-
    note: I add comments to the lines I changed to help you understand better

    setup() {
        
    
        const word = ref(null);
        const words = reactive(['Word-1', 'Word-2']);
    
        function randomizeText(i) {
          const p = randomNum(i, words.length);
          const newWord = words[p];
    
          setTimeout(() => {
            word.value = newWord;
          }, 200); // time to allow opacity to hit 0 before changing word
        }
    
        function randomNum(num, max) {
          let j = Math.floor(Math.random() * max);
    
          // ensure diff num every time
          if (num === j) {
            // notice here as well I use num instead of i
            return randomNum(num, max);
          } else {
            return j;
          }
        }
        
        const wordChanger = setInterval(randomizeText, 4000);
        
        onMounted(() => {
          randomizeText(0); // start with 0
          // wordChanger; // no need using this here
        });
    
        onBeforeRouteLeave(() => {
          clearTimeout(wordChanger);
        });
    
        return {
          word,
        };
      },
    
    <span>{{ word }}</span>
    

    here’s a demo:- https://stackblitz.com/edit/vue-rap7bb?file=src/components/HelloWorld.vue

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search