Jake
Jake

Reputation: 148

Select certain elements between two elements [jQuery]

I'd like to know if it's possible to select certain elements, crawling up the DOM, between two points.

Below is an example of the tree. My desire is to select all of the labels with a data-tagname attribute, starting from the input and ending at the .parent. The idea then is to set it as the input's name i.e name="one_two_three_four".

I can do this for a specific DOM tree by explicitly traversing upwards to the relevant elements. For example:

$('input').each(function(){
    let label = 
        $(this).parent().parent()
               .parent().parent()
               .parent().parent()
               .prev('[data-tagname]')
               .attr('data-tagname')
          + '_' 
          + $(this).parent().parent()
               .parent().parent()
               .prev('[data-tagname]')
               .attr('data-tagname')
          + '_' 
          + $(this).parent().parent()
               .prev('[data-tagname]')
               .attr('data-tagname')
          + '_' 
          + $(this).prev('[data-tagname]')
                   .attr('data-tagname');
    $(this).attr('name', label);
    // 
    $(this).attr('placeholder', label);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="stop-here">
    <label data-tagname="one">1</label>
    <ul><li>
      <label data-tagname="two">2</label>
      <ul><li>
        <label data-tagname="three">3</label>
        <ul><li>
          <label data-tagname="four">4</label>
          <input type="text" id="startHere"/>
        </li></ul>
      </li></ul>
    </li></ul>
  </li>
</ul>

This approach is verbose and inflexible. As the structure won't always be the same (some elements are nested 5 times, others 2), it doesn't solve the general case.

TL;DR

I'm looking for a solution along the lines of traversing up the DOM tree from point A, filtering the good stuff out, and stopping at point B. Maybe we go from A to B and push all of the elements we've selected to an empty array?

Upvotes: 0

Views: 483

Answers (1)

Jeff Standen
Jeff Standen

Reputation: 6876

You can use the .parentsUntil() function to traverse up the DOM from each INPUT to the first LI.parent:

$('input').each(function() {
    var $this = $(this);
    var name = $this
        .parentsUntil($this.closest('.stop-here').parent(), 'li')
        .map(
            function() { 
                return $(this).find('> label:first').attr('data-tagname');
            }
        )
        .toArray()
        .reverse()
        .join('_')
    ;
    
    $this.attr('name', name);
    $this.attr('placeholder', name);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="stop-here">
    <label data-tagname="one">1</label>
    <ul><li>
      <label data-tagname="two">2</label>
      <ul><li>
        <label data-tagname="three">3</label>
        <ul><li>
          <label data-tagname="four">4</label>
          <input type="text" id="startHere"/>
        </li></ul>
      </li></ul>
    </li></ul>
  </li>
</ul>

Since you want to include LI.stop-here, we use its parent as the first argument to .parentsUntil().

Then we use .map() to aggregate all the data-tagname attributes, typecast them to an array, reverse the order, and join them into a string using _ as a delimiter.

Finally, we set the name of each INPUT to that name string. In your example, that's "one_two_three_four".

Upvotes: 1

Related Questions