Search code examples
jquerycssjquery-selectors

jQuery selector containing attribute and sibling selectors not working


I have a mobile menu which opens when an icon is clicked. Most of the menu links lead to other pages, but there are also local links with href='#xxxx' attributes, with same-page-IDs as their link target.

The opening and closing of the menu is triggered by a jQuery script which works when the icon is clicked (in the snippet below: the red square).

When clicking a local link leading to an ID further down on the page, the menu remains open, which it shouldn't. I tried to add a script to close the menu when any menu item is clicked, but the selector in that script doesn't seem to work.

Here's the non-working version - note the jQuery selector combining an attribute selector and a sibling selector with others. This is the mobile version only, I left out all the media queries.

Note: The very same selector works in CSS (last rule):

(BTW: The HTML code for the menu is generated by Wordpress)

//Toggle mobile menu when clicking on the nav icon
jQuery('#navicon_1').on('click', function() {
  jQuery('#main_nav').slideToggle(300);
  jQuery(this).attr("aria-expanded", function(i, attr) { //change expanded-status 
    return attr === 'true' ? 'false' : 'true';
  });
});

//close mobile menu when clicking any menu icon
jQuery("#navicon_1[aria-expanded='true'] + #main_nav ul li.menu-item").on('click', function() {
  //alert("SHOULD WORK NOW");
  jQuery('#main_nav').hide(); //close menu
  jQuery('#navicon_1').attr("aria-expanded", function(i, attr) { //change expanded-status 
    return attr === 'true' ? 'false' : 'true';
  });
});
* {
  box-sizing: border-box;
}

#navicon_1 {
  position: absolute;
  right: 50px;
  width: 32px;
  height: 32px;
  cursor: pointer;
  display: block;
  border: none;
  background: red;
  text-indent: -5000px;
}

#main_nav {
  display: none;
  position: absolute;
  margin-right: -30px;
  top: 40px;
  overflow-y: auto;
  right: 40px;
  padding: 0 0 0 0;
  background: #fff;
  -webkit-box-shadow: -2px 10px 10px #aeaeae;
  box-shadow: -2px 10px 10px #aeaeae;
}

#main_nav>ul {
  width: 230px;
  bottom: 0;
  text-align: left;
  padding: 8px 0 8px 10px;
}

#main_nav>ul li {
  font-size: 17px;
  display: block;
  width: 100%;
  line-height: 1.6;
  padding: 4px 6px 4px 8px;
  background: #eee;
}

#navicon_1[aria-expanded='true']+#main_nav ul li.menu-item {
  background: #fb6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="navbar_top">
  <div id="navicon_1" role="button" aria-expanded="false" aria-label="click to open and close the menu" tabindex="0">
  </div>
  <nav role="navigation" id="main_nav" tabindex="0">
    <ul id="menu-hauptmenue" class="menu">
      <li id="menu-item-157" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-157"><a href="#">Menu 1</a></li>
      <li id="menu-item-168" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-168"><a href="#">Menu 2</a></li>
      <li id="menu-item-159" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-159"><a href="#">Menu 3</a></li>
      <li id="menu-item-324" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-324"><a href="#">Menu 4</a></li>
      <li id="menu-item-176" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-176"><a href="#">Menu 5</a></li>
    </ul>
  </nav>
</div>

And here is a version where the selector is simplified, leaving out the attribute and sibling selectors. That one works: When you click on any of the menu entries, the menu closes.

But I need to limit the function to the situation where #navicon_1 has the "aria-expanded=true" status - otherwise the menu disappears on desktop when a local link is clicked:

//Toggle mobile menu when clicking on the nav icon
jQuery('#navicon_1').on('click', function() {
  jQuery('#main_nav').slideToggle(300);
  jQuery(this).attr("aria-expanded", function(i, attr) { //change expanded-status 
    return attr === 'true' ? 'false' : 'true';
  });
});

jQuery("#main_nav ul li.menu-item").on('click', function () {
	//alert("SHOULD WORK NOW");
  jQuery('#main_nav').hide();//close menu
		jQuery('#navicon_1').attr("aria-expanded", function(i, attr) {//change expanded-status 
        return attr === 'true' ? 'false' : 'true';
    });
});
* {
  box-sizing: border-box;
}

#navicon_1 {
  position: absolute;
  right: 50px;
  width: 32px;
  height: 32px;
  cursor: pointer;
  display: block;
  border: none;
  background: red;
  text-indent: -5000px;
}

#main_nav {
  display: none;
  position: absolute;
  margin-right: -30px;
  top: 40px;
  overflow-y: auto;
  right: 40px;
  padding: 0 0 0 0;
  background: #fff;
  -webkit-box-shadow: -2px 10px 10px #aeaeae;
  box-shadow: -2px 10px 10px #aeaeae;
}

#main_nav>ul {
  width: 230px;
  bottom: 0;
  text-align: left;
  padding: 8px 0 8px 10px;
}

#main_nav>ul li {
  font-size: 17px;
  display: block;
  width: 100%;
  line-height: 1.6;
  padding: 4px 6px 4px 8px;
  background: #eee;
}

#navicon_1[aria-expanded='true']+#main_nav ul li.menu-item {
  background: #fb6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="navbar_top">
  <div id="navicon_1" role="button" aria-expanded="false" aria-label="click to open and close the menu" tabindex="0">
  </div>
  <nav role="navigation" id="main_nav" tabindex="0">
    <ul id="menu-hauptmenue" class="menu">
      <li id="menu-item-157" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-157"><a href="#">Menu 1</a></li>
      <li id="menu-item-168" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-168"><a href="#">Menu 2</a></li>
      <li id="menu-item-159" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-159"><a href="#">Menu 3</a></li>
      <li id="menu-item-324" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-324"><a href="#">Menu 4</a></li>
      <li id="menu-item-176" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-176"><a href="#">Menu 5</a></li>
    </ul>
  </nav>
</div>


Solution

  • When that new event handler is assigned, that element does not have that attribute set to that value (so actually it fails silently, because the selector fails to match any element). The solution is to either attach it and check the attribute's value within the handler or use event delegation.

    Here's how to check the value:

    jQuery("#navicon_1 + #main_nav ul li.menu-item").on('click', function() {
      if(!jQuery("#navicon_1").is("[aria-expanded='true']")) return;
      jQuery('#main_nav').hide(); //close menu
      jQuery('#navicon_1').attr("aria-expanded", function(i, attr) { //change expanded-status 
        return attr === 'true' ? 'false' : 'true';
      });
    });
    

    Here's how to use event delegation:

    jQuery("#navicon_1").parent().on('click', "#navicon_1[aria-expanded='true'] + #main_nav ul li.menu-item", function() {
      jQuery('#main_nav').hide(); //close menu
      jQuery('#navicon_1').attr("aria-expanded", function(i, attr) { //change expanded-status 
        return attr === 'true' ? 'false' : 'true';
      });
    });
    

    Here's the documentation for jQuery's "on" method.