jqueryjquery-selectorsnested-listsjquery-eventsdom-node

jQuery targeting nested list


What I'm trying to achieve at first is to test if an "li" has an "ul" nested within, and if so, to show the nested "ul" on click. this is working well; HOWEVER, the if statement is returning true for every "li" not just the ones that have a child "ul". this means the links without submenus don't work, since e.preventDefault() gets invoked.

Thanks for help!

$('.main_nav').on('click', 'a', function(e) {
		if( $(this).parent().has('ul') ) {
			e.preventDefault();
			$(this).parent().children('ul').toggle( 800, 'easeOutQuint', function() {
				 $(this).closest('li').toggleClass('open dropdown');
    		}).closest('li').siblings('.open').toggleClass('open dropdown').children('ul').hide('fast');
		}
	});
 <nav class="main_nav" role="navigation" aria-label="Primary Menu">
        <ul id="menu-main" class="menu"><li id="menu-item-7" class="menu-item menu-item-type-post_type menu-item-object-page current-menu-item page_item page-item-4 current_page_item menu-item-7"><a href="http://projectbiketech.dev/">Home</a></li>
<li id="menu-item-22" class="dropdown menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-22"><a href="http://projectbiketech.dev/about/">About</a>
<ul class="sub-menu">
	<li id="menu-item-61" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-61"><a href="http://projectbiketech.dev/about/team/">Team</a></li>
	<li id="menu-item-60" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-60"><a href="http://projectbiketech.dev/about/board-of-directors/">Board of Directors</a></li>
	<li id="menu-item-59" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-59"><a href="http://projectbiketech.dev/about/advisory-board/">Advisory Board</a></li>
	<li id="menu-item-58" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-58"><a href="http://projectbiketech.dev/about/friends-of-pbt/">Friends of PBT</a></li>
</ul>
</li>
<li id="menu-item-25" class="dropdown menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-25"><a href="http://projectbiketech.dev/programs/">Programs</a>
<ul class="sub-menu">
	<li id="menu-item-65" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-65"><a href="http://projectbiketech.dev/programs/bike-tech-in-school/">Bike Tech In School</a></li>
</ul>
</li>
<li id="menu-item-28" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-28"><a href="http://projectbiketech.dev/chapters/">Chapters</a></li>
<li id="menu-item-31" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-31"><a href="http://projectbiketech.dev/media/">Media</a></li>
<li id="menu-item-34" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-34"><a href="http://projectbiketech.dev/support/">Support</a></li>
<li id="menu-item-37" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-37"><a href="http://projectbiketech.dev/contact/">Contact</a></li>
</ul>    </nav><!-- .main_navigation -->


Solution

  • You have the problem of a truthy value being populated into your conditional. has() does not return a boolean value, it returns a filtered jQuery collection based on the selector specified. You can use length to investigate the size of collection (which could be 0).

    Example:

    if( $(this).parent().has('ul').length > 0 ) {
    

    You might, as an alternative, consider specifying a different class to use to enforce this behavior as well. That would allow you to only attach this on click "override" behavior on those particular a tags of the given class. This would perform better than repeated DOM queries and traversal.