skip to Main Content

I have a nested unordered list with an arbitrary number of levels. I want to be able to access the top-level list item element from any level list item element.

Markup

<ul class="list">
  <li class="list-item">Level 1
    <ul class="list">
      <li class="list-item">Level 2
        <ul class="list">
          <li class="list-item">Level 3
            <ul class="list">
              <li class="list-item is-selected">Level 4</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

For example, if I am on the following list item element level:
<li class="list-item">Level 4</li>
I want to be able to access the following list item element using JavaScript:

<li class="list-item">Level 1</li>

I have tried the following code, but it doesn’t work:

const currentListItem = document.querySelector('.list-item.is-selected');

const parentUlElement = currentListItem.parentNode;


while (parentUlElement && !parentUlElement.classList.contains('list')) {
  parentUlElement = parentUlElement.parentNode;
}

// This will select the `<li class="list-item">Level 1` element.
const levelOneListItem = parentUlElement;
const currentListItem = document.querySelector('.list-item.is-selected');

const parentUlElement = currentListItem.parentNode;

while (parentUlElement && !parentUlElement.classList.contains('list')) {
  parentUlElement = parentUlElement.parentNode;
}

const levelOneListItem = parentUlElement;
  currentListItem.addEventListener("click", () => {
   console.log("levelOneListItem", levelOneListItem);
});
<ul class="list">
  <li class="list-item">Level 1
    <ul class="list">
      <li class="list-item">Level 2
        <ul class="list">
          <li class="list-item">Level 3
            <ul class="list">
              <li class="list-item is-selected">Level 4</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

How can I access the top-level list item element from any level list item
Note: Te level of the elements is not fixed

3

Answers


  1. You could use closest() to find it. Use as selector:

     *:not(.list-item) > .list > .list-item
    

    This means: find in the path to the current node, the list-item whose grandparent is not a list-item.

    const currentListItem = document.querySelector('.list-item.is-selected');
    
    const levelOneListItem = currentListItem.closest("*:not(.list-item) > .list > .list-item");
    
    console.log("levelOneListItem", levelOneListItem);
    <ul class="list">
      <li class="list-item">Level 1
        <ul class="list">
          <li class="list-item">Level 2
            <ul class="list">
              <li class="list-item">Level 3
                <ul class="list">
                  <li class="list-item is-selected">Level 4</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
    Login or Signup to reply.
  2. You can use the has pseudo-class to find the first list-item that has a descendant list-item that is-selected:

    const currentListItem = document.querySelector('.list-item.is-selected');
    const levelOneListItem= document.querySelector('.list-item:has(.list-item.is-selected)');
    
      currentListItem.addEventListener("click", () => {
       console.log("levelOneListItem", levelOneListItem);
    });
    <div class="div">
    <ul class="list">
      <li class="list-item">Level 1
        <ul class="list">
          <li class="list-item">Level 2
            <ul class="list">
              <li class="list-item">Level 3
                <ul class="list">
                  <li class="list-item is-selected">Level 4</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
    </div>
    Login or Signup to reply.
  3. It seems to me that your requirement to access the first li in the ul from any level within the ul is not the correct way to think about this.

    Why can’t you just select the first li within the ul from the beginning of the document?

    // Get the first <li> with a class of "list-item" within the first <ul> with a class of "list".
    const firstLI = document.querySelector("ul.list > li.list-item");
    
    // Verify results:
    console.log(firstLI.outerHTML);
    <ul class="list">
      <li class="list-item">Level 1
        <ul class="list">
          <li class="list-item">Level 2
            <ul class="list">
              <li class="list-item">Level 3
                <ul class="list">
                  <li class="list-item is-selected">Level 4</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search