Solution 1 :

I consider that you have some nested ul tags, so in your example, the ul.root is level 1 and the inner ul is level 2 and if instead of ‘Eats1’ you have a ul tag, it will be level 3 and …
Use this function:

function changeElementOnLevel(level){
    let query = 'ul.root>li';
    for (let i = 1; i < level; i++) {
        query += '>ul>li'
    }
    query += ':last-child'
    var lastLi = document.querySelectorAll(query);
    for(i = 0; i < lastLi.length; i++){
        // your code 
    }
 }

Solution 2 :

It’s depends what exactly you want to do. Are you going to change inner HTML on click? What do you mean by saying third level? You have 3 levels, and 3 lists. More information needed in order to help you.

                

  var myElement = document.getElementsByClassName('root');
            var arrayOfPets = myElement[0].children; // we are getting first level, all animals

            var secondLevelArr = [];

            for(i = 0; i < arrayOfPets.length; i++){ 
                arrayOfPets[i].classList.add('last'); 
                arrayOfPets[i].style.background = 'green'; 

                var secondLevel = arrayOfPets[i].children[0].children;
                // Push results to array
                secondLevelArr.push(secondLevel);
               } // will add class and background to all of them
          // To add styles only to last element, you do not need to loop through them

          arrayOfPets[arrayOfPets.length - 1].style.background = 'red'; 

          for(i = 0; i < secondLevelArr.length; i++){

            secondLevelArr[i][0].style.color = "white";

            for(j = 0; j < secondLevelArr[i].length; j++){
                secondLevelArr[i][j].style.textDecoration = 'line-through';
            }

            secondLevelArr[i][secondLevelArr[i].length - 1].style.textDecoration = 'none';
          }
              
 <ul class='root'>
                <li>Cat
                  <ul>
                    <li>Eats1</li>
                    <li>Sleeps</li>
                    <li>Snuggles</li>
                    <li>Plays</li>
                    <li>Meows</li>
                  </ul>
                </li>
                <li>Dog
                  <ul>
                    <li>Eats2</li>
                    <li>Sleeps</li>
                    <li>Snuggles</li>
                    <li>Plays</li>
                    <li>Barks</li>
                  </ul>
                </li>
                <li>Fish
                  <ul>
                    <li>Eats3</li>
                    <li>Sleeps</li>
                    <li>Swims</li>
                    <li>Plays</li>
                    <li>Swims</li>
                  </ul>
                </li>
            </ul>

Solution 3 :

Here’s what I came up with on the fly:

First, I’d make a small tweak to your HTML (I’m not sure what you have is actually valid…it might display how you like, but structurally it’s going to cause problems). It is going to be difficult to set the value of an “li”, if it has the value AND a nested list. If you reset the innerText or the innerHtml, you’re going to completely overwrite the rest of the HTML in that tag, meaning you’ll lose the nested list. You could work around this, but why bother, just close those tags predictably.

(Note I don’t think any of the other answers address this issue).

So I’d first do this, notice how I close the “li” for Cat, Dog, and Fish:

          <ul class='root'>
            <li>Cat</li>
              <ul>
                <li>Eats1</li>
                <li>Sleeps</li>
                <li>Snuggles</li>
                <li>Plays</li>
                <li>Meows</li>
              </ul>
            <li>Dog</li>
              <ul>
                <li>Eats2</li>
                <li>Sleeps</li>
                <li>Snuggles</li>
                <li>Plays</li>
                <li>Barks</li>
              </ul>
              <li>Fish</li>
                <ul>
                  <li>Eats3</li>
                  <li>Sleeps</li>
                  <li>Swims</li>
                  <li>Plays</li>
                  <li>Swims</li>
                </ul>
            </ul>

Now you can select elements and set values very straightforwardly (and the HTML is sound); the selectors here basically say “give me the li and ul children that are ONLY direct descendants of whatever element I’m currently working with” (otherwise you will get all of them no matter how deeply nested, which you don’t want).

This code gets you the desired result by working recursively on nested “li” and “ul” collections, note that it also works on the top level “li” collection:

    const top_layer = document.querySelectorAll ( '.root' );
    const the_new_val = 'THE NEW VAL'; 
    function setProps ( elems, level ) {
      Array.from ( elems ).forEach ( x => { 
        const li = x.querySelectorAll ( ':scope > li' );
        const ul = x.querySelectorAll ( ':scope > ul' );
        if ( Array.from ( li ).length >= level ) {
          li [ level ].innerText = the_new_val;
            setProps ( li [ level ].children, level );
        } 
        if ( Array.from ( ul ).length ) {
            setProps ( ul, level );
        }
      });
    }

setProps ( top_layer, 2 );

Yes, you could work with “children”, but since we are directly interested in setting “li” values, which always appear in “ul” tags, the explicit selectors make it more obvious what’s going on and would ignore any other children that may be around, feel free to make that change if you like.

The displayed result:

Displayed Result

Solution 4 :

It is not very clear what you are trying to achieve 🙂 But you can try :nth-child() – CSS pseudo-class selector that allows you to select elements based on their index (source order) inside their container.

This is just an example:

function find(n) {
    // returns NodeList
    var liNodeList = document.querySelectorAll('li:nth-child(' + n + ')');
    console.log(li);

    // if you need to do something with those elements, you can iterate
    for (var i = 0; i < liNodeList.length; ++i) {
        var item = liNodeList[i];
        // do what you need with particular item
    }
}

Also, the right method is querySelectorAll(...). What you are using querySelectAll does not exist.

Solution 5 :

Try like below,

I had used mix of query selectors and traversal to achieve this,

function changeElementOnLevel(level) {
        var rootElement = document.querySelector(".root");

        let targetLi;
        if (level === 1) {
          targetLi = rootElement.children[rootElement.children.length - 1];
          let ul = targetLi.querySelector("ul"); // Since a ul is also there in level 1, I am expecting we need to change only li value not the ul
          targetLi.textContent = "changed Value";
          targetLi.append(ul);
        } else if (level === 3) {
          targetLi = rootElement.querySelector(
            "li:last-child ul li:last-child"
          );
          targetLi.textContent = "changed Value";
        }
      }

      changeElementOnLevel(3);
<ul class="root">
      <li>
        Cat
        <ul>
          <li>Eats1</li>
          <li>Sleeps</li>
          <li>Snuggles</li>
          <li>Plays</li>
          <li>Meows</li>
        </ul>
      </li>
      <li>
        Dog
        <ul>
          <li>Eats2</li>
          <li>Sleeps</li>
          <li>Snuggles</li>
          <li>Plays</li>
          <li>Barks</li>
        </ul>
      </li>
      <li>
        Fish
        <ul>
          <li>Eats3</li>
          <li>Sleeps</li>
          <li>Swims</li>
          <li>Plays</li>
          <li>Swims</li>
        </ul>
      </li>
    </ul>

Solution 6 :

This code resolved my issue:

var parent = document.querySelector('.root');

function setFirstItemClass(element, level){
    level = +level;
    if(level == 1){
        console.dir(element);
        if(element.children.length > 0){
            element.children[0].style.backgroundColor = 'red';
        }
    } else {
        if(element.children.length > 0){
            level--;
            for(child of element.children){
                setFirstItemClass(child, level);
            }
        }

    }
}

setFirstItemClass(parent, 3);

Problem :

I ran into a problem of targeting different nested levels.

I read that it is possible to use .children feature + for loop, but I failed to do that.

Let’s say I want to have a function where you pass the nesting level and it will change some property of <li> element on that level only.

I wrote this function to add classes to all last <li> 

  function changeElement(){ 
  var lastLi = document.querySelectorAll('li:last-child'); 
    for(i = 0; i < lastLi.length; i++){ 
     lastLi[i].classList.add('last'); 
     lastLi[i].style.background = 'green'; 
    } 
  } 

But now I need to target <li> elements on specific nested level

  function changeElementOnLevel(level) {
  var lastLi = document.querySelectorAll('li:last-child');

  for (i = 0; i < lastLi.length; i++) {
    //if level is 1 than we should change ul.root > li:last-child
    //if level is 3 than we should change ALL <li:last-child> inside ul.root 
> li > ul > 
  }

}
changeElementOnLevel(3)
<ul class='root'>
  <li>Cat
    <ul>
      <li>Eats1</li>
      <li>Sleeps</li>
      <li>Snuggles</li>
      <li>Plays</li>
      <li>Meows</li>
    </ul>
  </li>
  <li>Dog
    <ul>
      <li>Eats2</li>
      <li>Sleeps</li>
      <li>Snuggles</li>
      <li>Plays</li>
      <li>Barks</li>
    </ul>
  </li>
  <li>Fish
    <ul>
      <li>Eats3</li>
      <li>Sleeps</li>
      <li>Swims</li>
      <li>Plays</li>
      <li>Swims</li>
    </ul>
  </li>

Comments

Comment posted by Tim Consolazio

What you want is a little unclear. You say you want to pass a level, and that a property on that “li” will change. But you have a “ul”, with “li” children, each which has a “ul”, each which was several “li” children. It looks like you actually need to think recursively.

Comment posted by Tim Consolazio

I posted an answer that works. Think recursion!!!

Comment posted by Карина Баринова

Hello, by changing the elements I mean adding new classes to them so I need to define which element code should work with

Comment posted by Tet Nuc

I’ve edited my code, please see if it is something you are looking for.

Comment posted by Карина Баринова

Hello, John! it is my first task from the DOM classes and Ii believe it should be done using the loop. So I code accepts a specific level from the user > goes to that level and add something(e.x add a new class to all last

  • on that level

    Comment posted by Карина Баринова

    I wrote this function to add classes to all last

  • function addClass(){ var lastLi = document.querySelectorAll(‘li:last-child’); for(i = 0; i < lastLi.length; i++){ lastLi[i].classList.add('last'); lastLi[i].style.background = 'green'; } } But now I need to target
  • elements on specific nested level

  • By