Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript JavaScript and the DOM (Retiring) Traversing the DOM Getting All Children of a Node with children

Jonathon Irizarry
Jonathon Irizarry
9,154 Points

The children property vs the querySelectorAll property in this scenario.

Would it not be safer to use - const lis = listUl.querySelectorAll('li'); - over using - const lis = listUl.children - due to the fact if you add any children elements to <ul> in the future you could risk placing the 'down', 'up', and 'remove' buttons on them when you ran your code in the browser?

3 Answers

Steven Parker
Steven Parker
220,695 Points

Neither of these would cause the buttons to be affected by any elements added later.

But the big difference between them is that listUl.querySelectorAll('li') would collect all list elements, including ones that might be part of nested lists. But listUl.children would return only the list items that are direct children of the "listUI" element.

.children returns a live HTMLCollection so it would be affected by children elements added or removed later.

Steven Parker
Steven Parker
220,695 Points

Sure, a live vs. static NodeList is another difference between those functions, but it's not significant here as the code that used it to establish the buttons wouldn't run again when elements are added.

Jonathon Irizarry
Jonathon Irizarry
9,154 Points

Okay I see what you're saying with the querySelectorAll('li') it's almost like fishing for a huge fish with a decent sized net in the ocean but sometimes you will grab some other fish that are nested in the net as well. The listUl.children though seems like it would be troublesome and be affected if I wanted to add more direct children elements to the unordered list that were not <li> elements. For example:

<!DOCTYPE html>
<html>
  <head>
    <title>JavaScript and the DOM</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <h1 id="myHeading">JavaScript and the DOM</h1>
    <p>Making a web page interactive</p>
    <button id="toggleList">Hide list</button>
    <div class="list">
      <p class="description">Things that are purple:</p>
      <input type="text" class="description">
      <button class="description">Change list description</button>
      <ul>
        <button>hello</button>
        <li>grapes</li>
        <li>amethyst </li>
        <li>lavender</li>
        <li>plums</li>
      </ul>
        <input type="text" class="addItemInput">
        <button class="addItemButton">Add item</button>
    </div>
    <script src="app.js"></script>
  </body>
</html>
/*jshint esversion: 6 */

const toggleList = document.getElementById('toggleList');
const listDiv = document.querySelector('.list');
const descriptionInput = document.querySelector('input.description');
const descriptionP = document.querySelector('p.description ');
const descriptionButton = document.querySelector('button.description');
const listUl = listDiv.querySelector('ul');
const addItemInput = document.querySelector('input.addItemInput');
const addItemButton = document.querySelector('button.addItemButton');
const lis = listUl.children;

function attachListItemButtons(li) {
    let up = document.createElement('button');
    up.className = 'up';
    up.textContent = 'Up';
    li.appendChild(up);

    let down = document.createElement('button');
    down.className = 'down';
    down.textContent = 'Down';
    li.appendChild(down);

    let remove = document.createElement('button');
    remove.className = 'remove';
    remove.textContent = 'Remove';
    li.appendChild(remove);
}

for (let i = 0; i < lis.length; i += 1) {
    attachListItemButtons(lis[i]);
}

addItemButton.addEventListener('click', () => {
    let ul = document.getElementsByTagName('ul')[0];
    let li = document.createElement('li');
    li.textContent = addItemInput.value;
    attachListItemButtons(li); //spits out the li with the buttons attached and class names!
    ul.appendChild(li);
    addItemInput.value = '';
});

If you run that code in the browser you will see that the button <button>hello</button> gains the same class attributes and text content as the other <li> children. Is there a way to make the .children property more specific and only choose "li" children and not the "button" child element of the unordered list? I read the whole documentation on the children property, I see since it is an HTML collection you can index through them like you would an array, so the solution would be to start the for loop off at an index of 1. This would solve the problem here but if I moved the button element to an index of [3] and not the start of the list, how would I specify it to only apply className and textContent? (My best guess would be to modify the function and conditionals so that it would only run if the children element is a <li> tag.

Steven Parker
Steven Parker
220,695 Points

But that's not appropriate HTML code. A list element (either ul or ol) should have only list items (li) as it's children. Anything else should be inside one of the list items.

See this MDN page on ul for more information.

Jonathon Irizarry
Jonathon Irizarry
9,154 Points

Awww I see I should have looked more into HTML documentations. I just assumed it was an appropriate scenario because it was running fine but then ran into errors in the HTML validator. So in the case of unordered lists (ul) that can only contain one type of children element (li) it's best to use .children property, but in the case of an element like <div> that can contain more than one type of children element (i.e. 'p' or 'h1') it would be wiser to reference your element with one of the many other selectors like query or getElement. Sorry for asking so many questions. I'm just now getting serious when it comes to learning programming, it's just all these concepts we are learning with Javascript seem to hide some more of the complexity and origins of programming. Sometimes I feel as if I'm learning down a route that is towards becoming a programmer, yet the journey has been foggy due to these newer languages hiding some of the original mechanics of programming since they are already built into the language. Do you think learning c++ would be a more suitable language if I really wanted to know the beginning of programming, how it all came together, and gain another perspective towards programming?

Steven Parker
Steven Parker
220,695 Points

The limit of child types isn't the only reason to make use of the children property, but it's certainly a good one in this case.

You should decide what kind of programming you'd like to do, that might help in your choice of language(s) to learn. The applications for JavaScript and for C++ are very different. I've done both, but never for the same task. And JavaScript is far easier than C++. I would not recommend C++ unless you have a strong desire to work with the kind of applications it is generally used for.

Happy coding!

Aakash Srivastav
seal-mask
.a{fill-rule:evenodd;}techdegree
Aakash Srivastav
Full Stack JavaScript Techdegree Student 11,626 Points

Hey Steven , as you said JavaScript is far easier than C++ . Can you please elaborate?
I dont know much about the differences of both . But I want to know.
Thanks in advance.