Search code examples
jqueryhtmlcssdrop-down-menutarget

jQuery choose children of e.target


I have jQuery code that toggle class while I clicking on list element. It's okay if you click on list area... But when you click on span element, which has text "Element1", it is not working.

  1. I want use this jQuery function to only elements that have another ul as a child (Element1).
  2. I don't want to close dropdown menu by clicking any child ul elements.

$(".sidebar>ul>li").has("ul").click(
  function(e) {
    if ((e.target == this)) {
      $(this).children("ul").toggleClass("toggle");
    }
  }
);
  
.sidebar ul li ul {
  display: none;
}
.sidebar ul li ul.toggle {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="sidebar">
 <ul>
  <li>
    <span>Element1</span>
    <ul>
      <li>
        <a>Subelement1</a>
      </li>
    </ul>
  </li>
  <li>
    <span>Element2</span>
  </li>
 </ul>
</div>


Solution

  • Here's a way you can achieve this:

    Please note that, as @ste2425 stated in his comment, "according to the html5 spec li elements are only valid as a list item inside a ul or ol element." so you should consider wrapping the outer li elements in an ul, or ol, tag.

    jsFiddle 1

    var sidebarItems = $(".sidebar>li");
    
    sidebarItems.on('click', function(e) {
      var $th = $(this),
        subList = $(this).children("ul");
    
      // collapse other sub-ul by removing the class "open"
      sidebarItems.not($(this)).children('ul').removeClass('open');
      
      // check if the clicked element is a child of the a sub-ul which
      // has class open, if NOT we toggle the "open" class, note that
      // we use $(e.target) to convert the target into a jQuery object
      if (!$(e.target).parents('ul').hasClass('open')) {
        subList.toggleClass('open');
      } else {
        // just for demonstration purposes
        console.log('you clicked ' + $(e.target).text());
      }
    });
    li { cursor: pointer; font-weight:bold; }
    li ul { display: none; }
    li ul.open { display: block; }
    li ul.open li{ font-weight:normal; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <div class="sidebar">
      <li>
        <span>Element 1</span>
        <ul>
          <li><a>Sub element 1.1</a></li>
          <li><a>Sub element 1.2</a></li>
        </ul>
      </li>
      <li>
        <span>Element 2</span>
        <ul>
          <li><a>Sub element 2.1</a></li>
          <li><a>Sub element 2.2</a></li>
          <li><a>Sub element 2.3</a></li>
        </ul>
      </li>
      <li>
        <span>Element 3</span>
        <ul>
          <li><a>Sub element 3.1</a></li>
          <li><a>Sub element 3.2</a></li>
        </ul>
      </li>
    </div>


    Another way to do this is using CSS, instead of the span elements we use "invisible" checkboxes with its label elements. and we use this CSS to open the sub ul which is a sibling of the relative checkbox:

    li input[type="checkbox"]:checked ~ ul{ display: block; }
    

    We still need JavaScript just in order to "uncheck" other checkboxes

    jsFiddle 2

    var items = $('.sidebar input[type="checkbox"]');
    items.on('click', function(){
      items.not($(this)).prop('checked', false);
    });
    .sidebar li, li>label{ cursor: pointer; }
    li>label{ font-weight:bold; }
    .sidebar li ul{ display: none; }
    .sidebar li input[type="checkbox"]{ display:none; }
    .sidebar li input[type="checkbox"]:checked ~ ul{ display: block; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <div class="sidebar">
      <li>
        <input id="item1" type="checkbox">
        <label for="item1">Element 1</label>
        <ul>
          <li><a>Sub element 1.1</a></li>
          <li><a>Sub element 1.2</a></li>
        </ul>
      </li>
      <li>
        <input id="item2" type="checkbox">
        <label for="item2">Element 2</label>
        <ul>
          <li><a>Sub element 2.1</a></li>
          <li><a>Sub element 2.2</a></li>
          <li><a>Sub element 2.3</a></li>
        </ul>
      </li>
      <li>
        <input id="item3" type="checkbox">
        <label for="item3">Element 3</label>
        <ul>
          <li><a>Sub element 3.1</a></li>
          <li><a>Sub element 3.2</a></li>
        </ul>
      </li>
      <li>
        <input id="item4" type="checkbox">
        <label for="item4">Element 4</label>
      </li>
    </div>