Search code examples
javascriptjqueryhtmlnested-listsparents

How to Get and filter Data attribute of all parents in a nested list, till a target (div) of html element?


I've a nested (UL->LI->UL-LI..) list. On any clicked node, I'm using ParentsUntil() to find all the parents till a certain ancestor element.

In each nested element's data attribute (data-weight:), there is number that represent weight.

I want to sum/aggregate total weight till the parent. These numbers (Areas) are in the data-area field of each item.

<ul class="level-1 yes"  data-weight="12" data-weight-total="0">
  <li class="item-i" data-weight="22" >I</li>
  <li class="item-ii" data-weight="4"  data-weight-total="0">II
    <ul class="level-2 yes" data-weight="12">
      <li class="item-a" data-weight="1.4">A</li>
      <li class="item-b" data-weight="128" data-weight-total="0">B
        <ul class="level-3" data-weight="63" data-weight-total="0">
          <li class="item-1" data-weight="54">1</li>
          <li class="item-2" data-weight="23">2</li>
          <li class="item-3" data-weight="107">3</li>
        </ul>
      </li>
      <li class="item-c" data-weight="231">C</li>
    </ul>
  </li>
  <li class="item-iii">III</li>
</ul>



    $( "li.item-2" )
       .parentsUntil( $( "ul.level-1" ), ".yes" );

In the list above,

  1. How can I get the an array/list of them items from the clicked item to the parent item with their data-weight [key,value]? for e.g. $VarArrayCargoVessel
  2. And as I traverse up, How can I sum/total weights (data-weight-total) of each/nested list and populate\fill-in the data-weight-total? I have zero's right now, because I dont know how to insert/write into a array value

Solution

  • $("li").click(function(event){
      event.stopPropagation();
      var list = $(this).closest("ul");
      var children = list.children();
      var values = [];
      for(var i = 0; i < children.length; i++){
        values.push(parseInt((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
      }
      var sum = 0;
      for(var j = 0; j < values.length; j++){
        sum += values[j];
      }
      $("#summed").html(sum);
    });
    li{
      cursor: pointer;
      }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <ul class="level-1 yes"  data-weight="12" data-weight-total="0">
      <li class="item-i" data-weight="22" >I</li>
      <li class="item-ii" data-weight="4"  data-weight-total="0">II
        <ul class="level-2 yes" data-weight="12">
          <li class="item-a" data-weight="1.4">A</li>
          <li class="item-b" data-weight="128" data-weight-total="0">B
            <ul class="level-3" data-weight="63" data-weight-total="0">
              <li class="item-1" data-weight="54">1</li>
              <li class="item-2" data-weight="23">2</li>
              <li class="item-3" data-weight="107">3</li>
            </ul>
          </li>
          <li class="item-c" data-weight="231">C</li>
        </ul>
      </li>
      <li class="item-iii">III</li>
    </ul>
    
    <div id="summed"></div>

    Okay, let's get through this step by step. The first thing we do is creating a click event for each "li" item. If a li item is clicked, we execute our function ("function(event)").

    The first line stops the event from propagating up. jQuery would go up to the very first "ul" item otherwise and execute the code for each "ul" element. We would receive the first list all the time.

    http://api.jquery.com/event.stopPropagation/

    We then get the closest "ul" element and store this in the variable "list". We don't want the list itself, that's why we store its children ("li" elements) in the variable "children".

    We loop then through each children and store the "data-weight" attribute (method "getAttribute") in the array "values". Note that we only store the attribute if it's actually exists. If not, we just store "0".

    https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute

    We then create a variable "sum" which stores our summed up attributes. To do so we loop through the array and add the value (+= values[j]) to the sum variable.

    The last step writes the sum variable in the html of the div with the id #summed (check the html section).

    Further reading:

    https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute
    http://api.jquery.com/closest/
    http://api.jquery.com/click/
    http://api.jquery.com/event.stopPropagation/
    http://api.jquery.com/children/
    http://www.sitepoint.com/shorthand-javascript-techniques/
    http://api.jquery.com/html/
    

    Version 2:

    $("li").click(function(event){
      event.stopPropagation();
      var list = $(this).find("ul").first();
      console.log(list);
      var children = list.children();
      var values = [];
      for(var i = 0; i < children.length; i++){
        values.push(parseInt((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
      }
      var sum = 0;
      for(var j = 0; j < values.length; j++){
        sum += values[j];
      }
      $("#summed").html(sum);
    });
    li{
      cursor: pointer;
      }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <ul class="level-1 yes"  data-weight="12" data-weight-total="0">
      <li class="item-i" data-weight="22" >I</li>
      <li class="item-ii" data-weight="4"  data-weight-total="0">II
        <ul class="level-2 yes" data-weight="12">
          <li class="item-a" data-weight="1.4">A</li>
          <li class="item-b" data-weight="128" data-weight-total="0">B
            <ul class="level-3" data-weight="63" data-weight-total="0">
              <li class="item-1" data-weight="54">1</li>
              <li class="item-2" data-weight="23">2</li>
              <li class="item-3" data-weight="107">3</li>
            </ul>
          </li>
          <li class="item-c" data-weight="231">C</li>
        </ul>
      </li>
      <li class="item-iii">III</li>
    </ul>
    
    <div id="summed"></div>

    Note the third line in the answer. I've changed it to:

    var list = $(this).find("ul").first();
    

    Version 3:

    $("li").click(function(event){
      event.stopPropagation();
      var children = $(this).find("ul").first().children();
      if(children.length === 0){
        children = $(this);
      }
      console.log(children);
      var values = [];
      for(var i = 0; i < children.length; i++){
        values.push(parseFloat((children[i].getAttribute("data-weight") !== null) ? children[i].getAttribute("data-weight") : "0"));
      }
      var sum = 0;
      for(var j = 0; j < values.length; j++){
        sum += values[j];
      }
      $("#summed").html(sum);
    });
    li{
      cursor: pointer;
      }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <ul class="level-1 yes"  data-weight="12" data-weight-total="0">
      <li class="item-i" data-weight="22" >I</li>
      <li class="item-ii" data-weight="4"  data-weight-total="0">II
        <ul class="level-2 yes" data-weight="12">
          <li class="item-a" data-weight="1.4">A</li>
          <li class="item-b" data-weight="128" data-weight-total="0">B
            <ul class="level-3" data-weight="63" data-weight-total="0">
              <li class="item-1" data-weight="54">1</li>
              <li class="item-2" data-weight="23">2</li>
              <li class="item-3" data-weight="107">3</li>
            </ul>
          </li>
          <li class="item-c" data-weight="231">C</li>
        </ul>
      </li>
      <li class="item-iii">III</li>
    </ul>
    
    <div id="summed"></div>