Search code examples
javascriptdomtree-traversaldom-traversal

How to get parent multiple levels up the DOM tree without jQuery or modifying Element.prototype?


I am wondering if there is a way to select a specific element way up the DOM only using vanilla JS while not having to use parentNode multiple times. I understand you can do this with jQuery and modifying Element.prototype, but are there any other pretty ways to write this.

  const deleteButtons = document.querySelectorAll('.delete-button');

  for (var i = 0; i < deleteButtons.length; i++) {
    deleteButtons[i].addEventListener('click', (e) => {
      e.preventDefault();

      //This is the crazy amount of parentNode usage
      bookDatabase.child(e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.getAttribute("id")).remove();
      });
    }

Solution

  • There is really only one other way, which is to use the DOM API element.closest(), but it does require that you provide a selector that can uniquely identify the particular element you need (or the first element that matches that selector from the descendant's point of view). Without that, multiple uses of the .parent() method would be required and you'd need to know how many levels up you want to go.

    // Starting from the inner most level
    var start = document.getElementById("source");
    
    // Let's say you wanted to reference the first ancestor
    // that has the "something3" class
    start.closest(".something3").classList.add("red");
    
    // Or, the second closest
    var firstMatch = start.closest(".something2");
    firstMatch.classList.add("yellow");
    
    // Or, even higher
    firstMatch.closest(".something1").classList.add("aqua");
    
    // And, of course, you can skip levels
    start.closest(".something1").classList.add("dropCap");
    #source {background-color:orange; }
    .red { background-color:red; }
    .yellow { background-color:yellow; font-size:1rem; }
    .aqua { background-color:aqua; }
    .dropCap { font-size:3em; }
    <div class="something1">Level 1
      <div class="something2">Level 2
        <div class="something3">Level 3
          <div id="source">Level 4</div>
        </div>  
      </div>
    </div>