0

Scenario

I would like to get all child nodes of my div and change it color. Code:

function myFunction() {
  var divv = document.getElementById("divv");
  var myCollection = divv.childNodes;
  var len = myCollection.length;
  var i;
  for (i = 0; i < len; i++) {
    myCollection[i].style.color = "red";
  }
}
<div id="divv">

  <h2>JavaScript HTML DOM</h2>

  <p>Hello World!</p>
  <p>Hello Norway!</p>
  <p>Click the button to change the color of all p elements.</p>

  <button onclick="myFunction()">Try it</button>
</div>

Error: This is not working. It seems tha in my collection i have all nodes. h2 p text buton. I Expeceted just p h2 and buton.

EDIT Explanation Note: Whitespace inside elements is considered as text, and text is considered as nodes. Comments are also considered as nodes.

So we need to check if node is element node, or use querySelectorAll. Examples in answers below. Thanks for your help.

zolty13
  • 1,943
  • 3
  • 17
  • 34
  • 1
    Possible duplicate of [Using querySelectorAll to retrieve direct children](https://stackoverflow.com/questions/3680876/using-queryselectorall-to-retrieve-direct-children) – Mamun Jul 13 '18 at 08:06

2 Answers2

4

Text nodes do not have style attributes. If you want to use childNodes, check that the nodeType is 1 (an Element node) first:

function myFunction() {
  var divv = document.getElementById("divv");
  var myCollection = divv.childNodes;
  var len = myCollection.length;
  var i;
  for (i = 0; i < len; i++) {
    if (myCollection[i].nodeType === 1) myCollection[i].style.color = "red";
  }
}
<div id="divv">
  <h2>JavaScript HTML DOM</h2>
  <p>Hello World!</p>
  <p>Hello Norway!</p>
  <p>Click the button to change the color of all p elements.</p>
  <button onclick="myFunction()">Try it</button>
</div>

But I would prefer using querySelectorAll and forEach here:

function myFunction() {
  document.querySelectorAll('#divv > *')
    .forEach(child => child.style.color = "red");
}
<div id="divv">
  <h2>JavaScript HTML DOM</h2>
  <p>Hello World!</p>
  <p>Hello Norway!</p>
  <p>Click the button to change the color of all p elements.</p>
  <button onclick="myFunction()">Try it</button>
</div>

(or, you could simply set #divv's style.color to red)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • thank you. It is not about just color :). Itg is simple example of my problem. I do not understand why i get text nodes when i want only child node. Text node is grandchild. var myCollection = divv.childNodes; – zolty13 Jul 13 '18 at 08:11
  • 1
    Because `childNodes` will include text nodes. Newlines are text nodes too. If you want no text nodes, you could have HTML like `

    JavaScript HTML DOM

    Hello World!

    Hello Norway!

    ` with no newlines and no spaces between the element `<>`s.
    – CertainPerformance Jul 13 '18 at 08:23
3

You could use the children property to access the children of a given node:

The ParentNode property children is a read-only property that returns a live HTMLCollection which contains all of the child elements of the node upon which it was called.

- MDN web docs

function myFunction() {
  var divv = document.getElementById("divv");
  var myCollection = divv.children;
  var len = myCollection.length;
  var i;
  for (i = 0; i < len; i++) {
    myCollection[i].style.color = "red";
  }
}
<div id="divv">
  <h2>JavaScript HTML DOM</h2>
  <p>Hello World!</p>
  <p>Hello Norway!</p>
  <p>Click the button to change the color of all p elements.</p>
  <button onclick="myFunction()">Try it</button>
</div>

Another way to do with ES6 would be to spread the child nodes into an array and loop through them with a .forEach:

const myFunction = () => {
  
  [...document.querySelector('#divv').children].forEach(child => {
  
    child.style.color = 'red';
  
  });
  
}
<div id="divv">
  <div class="child">
    I am a child
  </div>
  <div>
    <div class="grandchild">
      I am a grand child
    </div>
  </div>
  
  <button onclick="myFunction()">Try it</button>
</div>

Alternatively, you could use the .forEach from the NodeList class directly but the previous method gives you more freedom to work with Array's method such as .reduce, .map, etc...

const myFunction = () => {
  
  document.querySelectorAll('#divv > *').forEach(child => {
  
    child.style.color = 'red';
  
  });
  
}
<div id="divv">
  <div class="child">
    I am a child
  </div>
  <div>
    <div class="grandchild">
      I am a grand child
    </div>
  </div>
  
  <button onclick="myFunction()">Try it</button>
</div>
Community
  • 1
  • 1
Ivan
  • 34,531
  • 8
  • 55
  • 100
  • ChildNode returns always also grandchildren? – zolty13 Jul 13 '18 at 08:13
  • @zolty13, No `The Node.childNodes read-only property returns a live NodeList of child nodes of the given element where the first child node is assigned index 0.`, so it only returns the children, not the grand-children. But remember that by adding style to the parent you are adding style to the children and grand-children too! – Ivan Jul 13 '18 at 08:18
  • So why I get 11 elements from childNode. @CertainPerfomance uses: if (myCollection[i].nodeType === 1) myCollection[i].style.color = "red"; – zolty13 Jul 13 '18 at 08:21
  • Yes you get 11, look at `myCollection` there are only child nodes not grandchild – Ivan Jul 13 '18 at 08:24
  • @zolty13, it is to distinguish `text` from `element`: read [this](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType) – Ivan Jul 13 '18 at 08:24
  • I expect h2 p p p and buton – zolty13 Jul 13 '18 at 08:28
  • I got explanation: Note: Whitespace inside elements is considered as text, and text is considered as nodes. Comments are also considered as nodes. Thank you for help – zolty13 Jul 13 '18 at 08:35