-4

I have the following markup:

<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span>Link 2</span>
    <button>
      chevron_down
    </button>
    <ul class="lvl--2" alt="NOT THIS UL">
      <li>Sub Link 1</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>
<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span alt="CLICK HERE">Link 2</span>
    <button>
      chevron_down
    </button>
    <ul class="lvl--2" alt="SELECT THIS">
      <li>Sub Link 1</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>

I click on "Link 2". How can I now select the next ul (.lvl - 2)? How can I find the next UL? Regardless of whether there is the button or not.

This doesn't work:

element.closest(ul); 

It selects "lvl - 1".

QJan84
  • 786
  • 3
  • 9
  • 22

2 Answers2

0

In order to get the element you want, you need a function (see nextSibling below), that can get you the next element matching a given selector, if present.

Code:

function nextSibling (element, selector) {
  /* Check whether a selector is given. */
  if (selector) {
    var
      /* Cache the children of the parent and the index of the element in them. */
      family = element.parentNode.children,
      index = [].indexOf.call(family, element),

      /* Find the elements with greater index that match the given selector. */
      siblings = [].filter.call(family, (e, i) => e.matches(selector) && i > index);

    return siblings[0];
  }
  else return element.nextElementSibling;
}

The above function can be used as follows inside a click event handler:

nextSibling(this, "ul.lvl--2");

Check out the following snippet for a working example.

Snippet:

/* ----- JavaScript ----- */

function nextSibling (element, selector) {
  /* Check whether a selector is given. */
  if (selector) {
    var
      /* Cache the children of the parent and the index of the element them. */
      family = element.parentNode.children,
      index = [].indexOf.call(family, element),

      /* Find the elements with greater index that match the given selector. */
      siblings = [].filter.call(family, (e, i) => e.matches(selector) && i > index);

    return siblings[0];
  }
  else return element.nextElementSibling;
}

document.getElementById("span").onclick = function() {
    console.log(nextSibling(this, "ul.lvl--2"));
}
<!----- HTML ----->
<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span>Link 2</span>
    <button>
      chevron_down
    </button>
    <ul class="lvl--2" alt="NOT THIS UL">
      <li>Sub Link 1</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>
<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span id = "span" alt="CLICK HERE">Link 2 (CLICK)</span>
    <button>
      chevron_down
    </button>
    <ul class="lvl--2" alt="SELECT THIS">
      <li>Sub Link 1</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>
Angel Politis
  • 10,955
  • 14
  • 48
  • 66
-1

Have a go with querySelector and closest (ancestor) or nextSibling

document.querySelector(".lvl--1 span").onclick=function() {
  // parent UL
  console.log(this.closest("ul").querySelector("ul").className)
  // OR parent LI:
  console.log(this.closest("li").querySelector("ul").className)
  // OR nextSibling:
  var el = this.nextSibling;
  while (el) {
    if (el && el.nodeName=="UL") {
      console.log(el.className);
      break;
    }
    el = el.nextSibling;
  }
}
<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span>Link 2 (CLICK)</span>
    <button>
      chevron_down
    </button>
    <ul class="lvl--2">
      <li>Sub Link 1</li>
      <li>Sub Link 2</li>
      <li>Sub Link 3</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>

If the UL is NOT on the same lever as the span you click you need this kind of code:

document.querySelector(".lvl--1 span").onclick=function() {
  var parent = this.closest("li"); // assuming LI is a parent that contains a UL further down
  var ULs = parent.querySelectorAll("ul");
  for (var i=0;i<ULs.length;i++) {
    for (var j=0;j<ULs[i].classList.length;j++) {
      if (ULs[i].classList[j].indexOf("lvl")==0) {
        console.log("found",ULs[i].classList[j]) 
      }
    }  
  }
}
<ul class="lvl--1">
  <li>Link 1</li>
  <li>
    <span>Link 2 (CLICK)</span>
    <button>
      chevron_down
    </button>
    <ul>
      <li>Sub Link 1</li>
      <li>Sub Link 2</li>
      <ul class="lvl--2">
        <li>...</li>
      </ul>
      <li>Sub Link 3</li>
    </ul>
  </li>
  <li>Link 3</li>
</ul>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • I can only write code based on what I see. I do not know what you mean by several levels. The first two versions will go up to the nearest container that contains both the span and the UL on the same level. The last code will start at the span and continue to the next sibling UL. If it needs to go deeper you need to iterate and with the HTML you gave it is not needed – mplungjan Feb 05 '18 at 14:02
  • I do not understand what changed. What is the expected output on click on what? My code will show the next UL from the span regardless of how many levels you have – mplungjan Feb 05 '18 at 14:30
  • A comment as to why this is voted down would be appreciated – mplungjan Feb 05 '18 at 14:52