Adding active class to anchor tags

Viewed 744

I've been trying to add a .active class to the current link and I know it's been asked here before, but, none of the answers seems to match my need exactly.

It's a Vue component. I don't want to add vue-router. I want to add the active class dynamically to whichever link is clicked.

<template>
  <nav>
    <ul>
      <li v-for="(item, $index) in items" :key="$index" >
        <a :href="item.link">{{ item.name }}</a>     //how do I dynamically add the active class here?
      </li>
    </ul>
  </nav>
</template>

<script>
export default {
  name: "Navbar",
  props: {
    items: {
      type: Array,
      default: null,
    },
  },
}
</script>

<style>
 ul li .active {
   background-color: red;
   cursor: pointer;
 }
</style>

The list of links:

const items = [
  {
    name: "Home",
    link: "#home",
  },
  { 
    name: "About", 
    link: "#about" 
  },
  {
    name: "Contacts",
    link: "#contact",
  },
];
2 Answers

You can make use of the hashchange event and Vue's class binding:

<template>
    <nav>
        <ul>
            <li v-for="(item, $index) in items" :key="$index">
                <a :href="item.link" :class="{ active: hash === item.link }">{{ item.name }}</a>
            </li>
        </ul>
    </nav>
</template>

<script>
export default {
    name: 'Navbar',

    props: {
        items: {
            type: Array,
            default: null,
        },
    },

    data() {
        return {
            hash: window.location.hash,
        };
    },

    created() {
        window.addEventListener('hashchange', () => {
            this.hash = window.location.hash;
        });
    },
};
</script>

<style>
ul li .active {
    background-color: red;
    cursor: pointer;
    font-weight: bold;
}
</style>

Another solution is to use a click event and setTimeout.

Using setTimeout without a delay will simply push the evaluation to the back of the queue, meaning it waits for the URL to update before reading it. Without this, the method will always be one step behind.

Here's an example:

<template>
    <nav>
        <ul>
            <li v-for="(item, $index) in items" :key="$index">
                <a
                    @click="setUrlSection(item.section)"
                    :href="item.link"
                    :class="{ active: urlSection === item.link }"
                    >{{ item.name }}</a
                >
            </li>
        </ul>
    </nav>
</template>

<script>
export default {
    name: 'Navbar',

    data() {
        return {
            items: [
                {
                    name: 'Home',
                    link: '#home',
                    section: 'hash',
                },
                {
                    name: 'About',
                    link: '/about',
                    section: 'pathname',
                },
                {
                    name: 'Contacts',
                    link: '#contact',
                    section: 'hash',
                },
            ],
            urlSection: window.location.hash || window.location.pathname,
        };
    },

    methods: {
        setUrlSection(section) {
            setTimeout(() => (this.urlSection = window.location[section]));
        },
    },
};
</script>

<style>
ul li .active {
    background-color: red;
    cursor: pointer;
    font-weight: bold;
}
</style>

Keep in mind, you have to add a section property to each item so it knows which window.location property to read.

You can use the VueJs :class

<template>
  <nav>
    <ul>
      <li v-for="(item, $index) in items" :key="$index" >
        <a :href="item.link" :class="{'active': current_page_name == item.name }">{{ item.name }}</a>
      </li>
    </ul>
  </nav>
</template>


<script>
export default {
  computed: {
    current_page_name() {
        return this.$route.name;
    }
  }
}
</script>

If you do not want to use vue-router you'll have to write more code. Here is example


<script>
export default {
  data(){
    return {
      url: '',
    }
  },
  computed: {
    current_page_name() {

      if(this.url){
        var URL = this.url;
        var arr=URL.split('/');
        var route_name = arr[1]; // i think zero is the domain, then 1 is the page name,
        return route_name;
      }

      return '';
    }
  },

  mounted(){
    this.url = window.location.href;
  }
}
</script>
Related