Search code examples
javascriptclassname

Adding active class based on current URL


I am trying to add 'active' class on my link. It is saying

Uncaught TypeError: document.querySelectorAll(...).each is not a function

Here is my code:

const current = window.location.href;
document.querySelectorAll("#nav-tab a").each(function(){
    const $this = this;
    
    if($this.attr('href').indexOf(current) !== -1){
        $this.classList.add("active");
    }
});

Can you help me? Or is there a better way to add a class name based on the current URL? Thank you so much!


Solution

  • You have a few issues with your code. Your mainly confusing jQuery methods with regular native browser methods/conventions:

    1. You need to use .forEach() and not .each(). The .forEach() method is a method on the NodeList that querySelectorAll() returns.

    2. .attr() is not a valid method. To get an element's attribute you can use .getAttribute(). We can use .href here instead to get the href. Note that getAttribute("href") will retrieve the URL as it is in your mark-up, whereas .href will retrieve the full, eg, if you had href="/foo/bar", .href will give https://example.com/foo/bar, whereas .getAttribute() will return just /foo/bar.

    3. Use the element parameter of the function instead of this. When you use .forEach() you're iterating over the elements in your NodeList (ie: the elements you selected), so you can access each using the first parameter of the forEach callback. The this value in the browser (if not in strict mode) will default to window, so it won't be the element like you're expecting it to be:

    const current = window.location.href;
    document.querySelectorAll("#nav-tab a").forEach(function(elem){ 
        if(elem.href.includes(current)){
          elem.classList.add("active");
        }
    });
    

    I've also changed .indexOf(...) !== -1 to .includes(), which is a more modern way to check if a string contains another value.


    I will point out that you can make your query selector more advanced, which will limit the number of elements you iterate:

    const current = window.location.href;
    document.querySelectorAll(`#nav-tab a[href*="${current}"]`).forEach(elem => { 
      elem.classList.add("active");
    });
    

    This uses the attribute selector a[href*=...] to select the a elements that have a href that contains the text in stored in current.