/    Sign up×
Community /Pin to ProfileBookmark

Javascript Loop

I want to select a menu button depending on current page.

The traditional way is to use i++ to iterate the “list”. I want to avoid this, because it is almost unreadable to a newbie:

“`
(function() {
var anchors = document.querySelectorAll(‘.nav a’),
current = window.location.href;

for (var i = 0; i < anchors.length; i++) {
if (current.indexOf(anchors[i].href) != -1) {
anchors[i].classList.add(“active”);
}
}
})();
“`

Less traditional way. More readable:

“`
const navbtn = document.querySelectorAll(‘.navbtn’);
var current = window.location.href;

(function() {
navbtn.forEach(myFunction);

function myFunction(el) {
if (current.indexOf(el.href) != -1) {
el.classList.add(“active”);
}
}
})();
“`

But I am searching for ways to do it even more readable and using less rows. I have search but found no simpler ways to do this.

Is there simpler or more readable ways?

to post a comment
JavaScript

18 Comments(s)

Copy linkTweet thisAlerts:
@SempervivumDec 31.2020 — 
  • 1. You can use an anonymous function

  • 2. Your can use `includes` instead of indexOf
    <i>
    </i>(function() {
    const navbtn = document.querySelectorAll('.navbtn');
    const current = window.location.href;
    navbtn.forEach(el =&gt; {
    if (current.includes(el.href)) {
    el.classList.add("active");
    }
    });
    })();
  • Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626338

    > You can use an anonymous function

    > Your can use includes instead of indexOf


    Thank You! I am still struggling to understand Javascript and readability is one way.

    A different, but related question. How do I do if two different pages should select the same button?

    I have searched and found something like this that not works:

    ``<i>
    </i>const homebtn = document.querySelector(".navbtn").href = "/home";

    if (any of this two pages is active) {
    homebtn.classList.add("active");
    }<i>
    </i>
    ``
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — Unfortunately I do not understand your question. AFAIK the purpose of this script is to highlight an item in the navigation based on the page that is loaded currently. So far it is self adjusting, i. e. when the menu is the same it will work without adjustments.
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626343 Unfortunately I do not understand your question.

    It is about main menu and submenus.

    Your solution works perfect with "sub menus". As there is almost always a correspondent sub menu.

    But the "main menu button" may have an associated submenu with say 10 links. But ALL these ten links should also select the correspondent main menu button.

    For an example: The About menu should be selected whether you select in the submenu "Our staff", "Contact us" or "Our history". You cannot have a complete dynamic selection of the About main menu button AFAIK.

    There may be many solution how to do this. But it should work regardless of button click or url link. So the link "https://example.com/ourstaff" should select both the main menu button AND the correspondent sub menu button. So it is a two dimensional selection.

    Make sense?
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — Now I understand, yes, this make sense.

    Take a look at this thread:

    https://www.webdeveloper.com/d/392258-drop-down-menu-problem

    This code:
    // make all parent li's visible:
    // start with current li
    let prnt = li[i];
    // while parent li is available
    while (prnt = prnt.closest('ul').closest('li')) {
    // make it visible
    prnt.style.display = "";
    }
    finds all parent elements. Try to adjust it to your needs. Instead of making the element visible you will have to add the class "active".
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626349 Take a look at this thread:

    As I interpret this example is the main menu and submenu in the same list (drop down 2 level). In my case there is 2 separate menu lists without any connection. http://94.237.92.101:2020

    Do I interpret this example correct?
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — Yes, this code will work only if there is only one menu and one root item. However I've never encountered anything different before. Please explain your situation more detailed and provide some sample code or the URL of your page.

    Or is the menu duplicated in order to provide one for desktop and another one for mobile?
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626351 Please explain your situation more detailed and provide some sample code or the URL of your page.

    In this example ( http://94.237.92.101:2020 ) I have a clumsy solution that I want to rewrite by using semi dynamic Javascript. This site are using Javascript for the submenu and Golang for the main menu. Or the main menu can be more traditional like https://gowebstatic.tk where I did not manage to select the main menu buttons.

    I do want 2 separate menus because this works better with small screens and phones. Dropdown in 2 levels tends to become hard to navigate when there is lots of links.
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — I took a look at gowebstatic.tk first and now I understand things better. The problem is that the URL visible in the address bar of the browser is not reflecting the hierarchical structure of the site. One can only derive the item in the submenu on the left based on the URL.

    Possible solution: Add a data attribute to the items in the submenu containing the ID or class of the corresponding item in the main menu.
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626353 Possible solution: Add a data attribute to the items in the submenu containing the ID or class of the corresponding item in the main menu.

    Yes, I thought of this. This involves a lot of typing (50 extra main menu stuff) that I want to avoid, so I am looking for a more dynamic solution. Like this pseudo code:

    ``<i>
    </i>switch (window.location.href) {
    case "subitem1","subitem2","subitem3":
    select main button 1
    break
    case "subitem4","subitem5","subitem6":
    select main button 2 etc
    break
    default:
    log any missing links
    }<i>

    </i>
    ``

    Is there a solution to select a button by its "href" without looping?
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — I do not recommend a switch but an array instead like this:
    // Key: href in sub menu
    // value: href in main menu
    const assignMainMenu = {
    '/prepare': 'prepare',
    '/vps': 'prepare'
    // and so on
    };
    Then you can easily get the href in the main menu based on the href in the sub menu.

    >Is there a solution to select a button by its "href" without looping?

    Yes, there is: You can use ` document.querySelector` like this:
    // href of sub menu in variable hrefSubmenu
    itemMainMenu = document.querySelector('.btnnav[href="' + hrefSubMenu+ '"]);
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — > @Sempervivum#1626355 Yes, there is: You can use document.querySelector like this:

    What am I doing wrong here? https://jsfiddle.net/p8dztLmu/1/
    Copy linkTweet thisAlerts:
    @SempervivumDec 31.2020 — The classes do not match:

    HTML: `class="navbtn"</C><br/>
    JS: <C>
    .btnnav`
    Copy linkTweet thisAlerts:
    @sibertauthorDec 31.2020 — Thank you hawk eye!

    https://jsfiddle.net/behzam2s/3/
    Copy linkTweet thisAlerts:
    @sibertauthorJan 01.2021 — Here is my final result. Could be nicer, but it works as expected:

    http://94.237.92.101:6060/

    The code behind selecting menus in two dimension:

    ``<i>
    </i>
    const nav = document.querySelector('nav');
    const navbtn = document.querySelectorAll('.navbtn');

    const subnav = document.querySelector('.subnav');
    const subnavbtn = document.querySelectorAll('.subnav a')

    const current = window.location.href;
    const hrefcurr = window.location.pathname;

    btnhome = document.querySelector('.navbtn[href="/home"]');
    btntopic = document.querySelector('.navbtn[href="/topics"]');
    btnuser = document.querySelector('.navbtn[href="/users"]');

    /* select correnspondent sub menu btn */
    (function() {
    subnavbtn.forEach(el =&gt; {
    if (current.includes(el.href)) {
    el.classList.add("selected");
    }
    });
    })();

    /* select correnspondent main menu btn */
    switch (hrefcurr) {
    case "/":
    case "/home":
    case "/api":
    case "/client":
    btnhome.classList.add("selected")
    break
    case "/topics":
    case "/newtopics":
    btntopic.classList.add("selected")
    break
    case "/users":
    case "/mod":
    btnuser.classList.add("selected")
    break
    default:
    alert("missing "+hrefcurr)
    }<i>
    </i>
    ``


    Should I add "window.onload = select" or something?
    Copy linkTweet thisAlerts:
    @SempervivumJan 03.2021 — My solution based on an array that assigns the path in the main menu to that in the sub menu:
    // key: path in sub menu
    // value: path in main menu
    const menuAssign = {
    '/home': '/home',
    '/client': '/home',
    '/api': '/home',
    '/topics': '/topics',
    '/alltopics': '/topics',
    '/users': '/users',
    '/mod': '/users'
    }
    const url = location.href;
    // get path, e. g. "/topics", from url
    const result = url.match(//[a-z]+$/);
    // is there a path?
    if (result) {
    // get path and path in main menu
    const path = result[0],
    mainPath = menuAssign[path];
    console.log(path, mainPath);
    // add class "active" in main menu according to path
    document.querySelector('nav a[href="' + mainPath + '"]').classList.add('active');
    // add class "active" in sub menu according to path
    document.querySelector('div.subnav a[href="' + path + '"]').classList.add('active');
    } else {
    document.querySelector('nav a[href="/home"]').classList.add('active');
    }
    Copy linkTweet thisAlerts:
    @SempervivumJan 03.2021 — ... or without an array but a data attribute in the ul of the sub menu:
    &lt;nav class="main-nav"&gt;
    &lt;a href="/home" class="navbtn"&gt;&lt;img src="icn/home.svg" alt="Home"&gt;Home&lt;/a&gt;
    &lt;a href="/topics" class="navbtn"&gt;&lt;img src="icn/topics.svg" alt="Messages"&gt;Topics&lt;/a&gt;
    &lt;a href="/users" class="navbtn"&gt;&lt;img src="icn/users.svg" alt="Users"&gt;Users&lt;/a&gt;
    &lt;/nav&gt;

    <i> </i>&lt;nav class="subnav"&gt;
    <i> </i> &lt;ul data-main-path="/topics"&gt;
    <i> </i> &lt;li&gt;&lt;a href="/topics"&gt;Recent Topics&lt;/a&gt;&lt;/li&gt;
    <i> </i> &lt;li&gt;&lt;a href="/alltopics"&gt;All Topics&lt;/a&gt;&lt;/li&gt;
    <i> </i> &lt;/ul&gt;
    <i> </i>&lt;/nav&gt;

    <i> </i>&lt;script&gt;
    <i> </i> const url = location.href;
    <i> </i> const result = url.match(//[a-z]+$/);
    <i> </i> if (result) {
    <i> </i> const path = result[0];
    <i> </i> const subItem = document.querySelector('nav.subnav a[href="' + path + '"]');
    <i> </i> const mainPath = subItem.closest('ul').dataset.mainPath;
    <i> </i> const mainItem = document.querySelector('nav.main-nav a[href="' + mainPath + '"]')
    <i> </i> console.log(path, mainPath);
    <i> </i> mainItem.classList.add('active');
    <i> </i> subItem.classList.add('active');
    <i> </i> } else {
    <i> </i> document.querySelector('nav a[href="/home"]').classList.add('active');
    <i> </i> }
    Copy linkTweet thisAlerts:
    @sibertauthorJan 17.2021 — > @Sempervivum#1626394 ... or without an array but a data attribute in the ul of the sub menu:

    This was the easiest way when using Golang. http://94.237.92.101:6060/home

    Based on your code:

    ``<i>
    </i>const navbtn = document.querySelectorAll('.navbtn');
    const subnavbtn = document.querySelectorAll('.subnav a')
    const current = window.location.href;
    const data = document.querySelector(".subnav ul").getAttribute("data-mainnav");

    /* selecting mainbtn and subnavbtn based on current url */
    window.addEventListener('load', (event) =&gt; {
    navbtn.forEach(el =&gt; {
    if (data.includes(el.pathname)) {
    el.classList.add("selected");
    } else {
    el.classList.remove("selected")
    }
    });

    subnavbtn.forEach(function(btn) {
    if (current.includes(btn.href)){
    btn.classList.add("selected");
    } else {
    btn.classList.remove("selected");
    }
    });
    });<i>
    </i>
    ``


    Thank you!
    ×

    Success!

    Help @sibert spread the word by sharing this article on Twitter...

    Tweet This
    Sign in
    Forgot password?
    Sign in with TwitchSign in with GithubCreate Account
    about: ({
    version: 0.1.9 BETA 3.29,
    whats_new: community page,
    up_next: more Davinci•003 tasks,
    coming_soon: events calendar,
    social: @webDeveloperHQ
    });

    legal: ({
    terms: of use,
    privacy: policy
    });
    changelog: (
    version: 0.1.9,
    notes: added community page

    version: 0.1.8,
    notes: added Davinci•003

    version: 0.1.7,
    notes: upvote answers to bounties

    version: 0.1.6,
    notes: article editor refresh
    )...
    recent_tips: (
    tipper: @darkwebsites540,
    tipped: article
    amount: 10 SATS,

    tipper: @Samric24,
    tipped: article
    amount: 1000 SATS,

    tipper: Anonymous,
    tipped: article
    amount: 10 SATS,
    )...