1

As the title of this thread suggests, I'm trying to add classes to divs using data attributes in vanilla javascript. However, I'm not sure I'm going about it the most efficient way.

Below I've added some basic HTML and javascript. If anyone can help me fill in the blanks, I would appreciate it greatly.

var selector_mastmenu = document.getElementById("menu");

selector_mastmenu.addEventListener('click', function(event) {
    if(event.target.classList.contains("has-child")) {
        // Remove class `show` from all `menu__panel` divs.
        // Find `menu__panel` with matching `data-menu-id` and add class `show`.
    }
    if(event.target.classList.contains("panel__back")) {
        // Remove class `show` from all `menu__panel` divs.
        // Find `parent-menu-id` of `menu__panel` add class `show` to `menu-id`.
    }
});
#menu .menu__panel {
  display: none;
}
#menu .menu__panel.show {
  display: block;
}
<div id="menu">
    <div class="menu__panel" data-menu-id="2_1" data-parent-menu-id="1">
        <button class="panel__back" type="button">Return to Main Menu</button>

        <ul class="panel__menu">
            <li><a href="#">Second Level Menu Item</a></li>
            <li><a href="#">Second Level Menu Item</a></li>
            <li><a href="#">Second Level Menu Item</a></li>
        </ul>
    </div>

    <div class="menu__panel show" data-menu-id="1">
        <ul class="panel__menu">
            <li><a href="#" class="has-child" data-menu-id="2_1">Menu Item</a></li>
        </ul>
    </div>
</div>

1
  • 3
    You can use document.querySelectorAll(selector) to target all elements matching a given selector. Commented Nov 24, 2020 at 23:37

2 Answers 2

2

The current way you are using your event handler function is going to be problematic. How things are styled with css and where the use clicks could be inconsistent. The target element could be the li tag or the a tag.

Here is a working example without changing your html. However, if I was going to do this myslef, I would be more explicit with your click handlers so I would not need to traverse the dom with things like parentNode in my example. Why traverse the dom if you could just put the data attribute on the button itself?

var hasChildElements = document.querySelectorAll('.has-child');
var menuPanelElements = document.querySelectorAll('.menu__panel');
var panelBackElements = document.querySelectorAll('.panel__back');

hasChildElements.forEach(el => {
    el.addEventListener('click', showMenu)
})

panelBackElements.forEach(el => {
    el.addEventListener('click', hideMenu)
})

function showMenu(event) {
    var menuId = event.currentTarget.getAttribute('data-menu-id')
    var menuPanelEl = document.querySelector(`div[data-menu-id="${menuId}"]`)

    // Remove class `show` from all `menu__panel` divs.
    menuPanelElements.forEach(el => {
        el.classList.remove('show')
    })

    // Find `menu__panel` with matching `data-menu-id` and add class `show`.
    menuPanelEl.classList.add('show')
}

function hideMenu(event) {
    // Remove class `show` from all `menu__panel` divs.
    menuPanelElements.forEach(el => {
        el.classList.remove('show')
    })

    // Find `parent-menu-id` of `menu__panel` add class `show` to `menu-id`.
    var menuId = document.querySelector('.panel__back').parentNode.getAttribute('data-parent-menu-id');
    var parentMenu = document.querySelector(`div[data-menu-id="${menuId}"]`)
    parentMenu.classList.add('show')
}
#menu .menu__panel {
  display: none;
}
#menu .menu__panel.show {
  display: block;
}
<div id="menu">
    <div class="menu__panel" data-menu-id="2_1" data-parent-menu-id="1">
        <button class="panel__back" type="button">Return to Main Menu</button>

        <ul class="panel__menu">
            <li><a href="#">Second Level Menu Item</a></li>
            <li><a href="#">Second Level Menu Item</a></li>
            <li><a href="#">Second Level Menu Item</a></li>
        </ul>
    </div>

    <div class="menu__panel show" data-menu-id="1">
        <ul class="panel__menu">
            <li><a href="#" class="has-child" data-menu-id="2_1">Menu Item</a></li>
        </ul>
    </div>
</div>

Sign up to request clarification or add additional context in comments.

Comments

1

This is precisely the kind of thing document.querySelectorAll was built for. It takes in CSS-type selectors and returns every matching element in a node list, which is an array-like object that you can iterate over. For example, if you were looking for every object with the class menu__panel, you would want

document.querySelectorAll('.menu__panel') // note the . before the class, just like CSS
   .forEach(panel => {
      panel.classList.add('plain-string-class-name'); // Adds plain-string-class-name to your class='' attribute in your HTML element
      panel.classList.remove('plain-string-class-name'); // Removes plain-string-class-name from your class='' attribute in your HTML element. Does not error if plain-string-class-name does not exist.
});

1 Comment

Thank you very much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.