Skip to content
Advertisement

Dynamic class binding doesn’t work in Vue

I’m using Vue2 and I face some problems with dynamic class binding (highlight in the navigation menubar the current section name in which the user is currently). I’m using the Intersection observerAPI which I initialize and let it loop over my components from the mounted() life hook. However, when I update the current section in my observer:

if (active.length) {
   this.activeEntry = active[0].target.childNodes[0].id
   console.log(this.activeEntry)
}

(For full code see below) it prints the changes of the current sections “targetEntry” to the console, but… my actual data “targetEntry” doesn’t actually get updated/affected (Am I not able to change data from inside the observer?). I guess I mixe here some things up. How do I need to rewrite my observer functionality (i.e. make use of computed) to actually change my “targetEntry” data so the dynamic classes binding will be executed? I’m still a beginner to Vue and Javascript…

Many thanks in advance!

<template>
  <div id="Navigation-c">
    <ul>
      <li :class="{ active: activeEntry == 'Entry-c' }" v-scroll-to="'#Entry-c'"><font-awesome-icon icon="fa-solid fa-chevron-up" /></li>
      <li :class="{ active: activeEntry == 'Motivation-c' }" v-scroll-to="'#Motivation-c'"><p>Intro</p></li>
      <li :class="{ active: activeEntry == 'NeuralNetworks-c' }" v-scroll-to="'#NeuralNetworks-c'"><p>AI</p></li>
      <li :class="{ active: activeEntry == 'About-c' }" v-scroll-to="'#About-c'"><p>About</p></li>
      <li :class="{ active: activeEntry == 'Contact-c' }" v-scroll-to="'#Contact-c'"><p>Contact</p></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "Navigation-c",
  data: () => ({
    activeEntry: '',
    observer: null
  }),
  methods: {

  },
  updated() {

  },
  mounted() {
    this.observer = new IntersectionObserver(
      function (entries) {
        const active = entries.filter((e) => e.isIntersecting);
        if (active.length) {
          this.activeEntry = active[0].target.childNodes[0].id
          console.log(this.activeEntry)
        }
      },
      { threshold: [0.5] }
    )
    // loop over components
    const matches = document.querySelectorAll("section.box");
    for (let i = 0; i < matches.length; i++) {
      this.observer.observe(matches[i]);
    }
  },
  computed: { 
    
  },
  created() {

  },
};
</script>

<style lang="scss" scoped>
.active {
  color: red !important;
}
</style>

Advertisement

Answer

The keyword this loses it’s context inside of new IntersectionObserver() without the use of an arrow function. Therefore attempting to assign this.activeEntry will not work unless you rewrite using the arrow function:

mounted() {
    this.observer = new IntersectionObserver(
      (entries) => { // arrow function!
        const active = entries.filter((e) => e.isIntersecting);
        if (active.length) {
          this.activeEntry = active[0].target.childNodes[0].id
          console.log(this.activeEntry)
        }
      },
      { threshold: [0.5] }
    )
    // loop over components
    const matches = document.querySelectorAll("section.box");
    for (let i = 0; i < matches.length; i++) {
      this.observer.observe(matches[i]);
    }
  },
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement