0

Within the following code, I am trying to get all .node elements that are direct child elements of .row elements. Currently my code will look for any given element that matches the .node class within .row element.

How could I get the direct node (e.g. in CSS this would look something like this: .row > .node) in plain JavaScript?

let rows = document.getElementsByClassName("row");
var nodes = [];

for (let i = 0; i < rows.length; i++) {
  console.log(rows[i].getElementsByClassName("node"));
}
.node {
  border-radius: 50%;
  border: 1px solid #000;
}

.node,
.empty {
  display: inline-block;
  height: 50px;
  width: 50px;
  line-height: 50px;
  text-align: center;
}

.d-none {
  display: none;
}
<div class="row">
  <div class="node">
    Foo
  </div>
  <div class="node d-none">
    Foo
  </div>
  <div class="row">
    <div class="empty">
      &nbsp;
    </div>
    <div class="node d-none">
      Foo
    </div>
  </div>
</div>

The problem is that the final .node element (which is nested in like so: .row > .row > .node) will be called for multiple times because I am not addressing it as direct child.

I saw this answer which explains the use of document.querySelector. Should I be using that for the purpose I am trying to reach here? Or Could I achieve the same by just using document.getElementsByClassName?

Barrosy
  • 1,407
  • 2
  • 25
  • 56
  • This repo shows some examples of js code without jquery. There's a section for querySelector https://github.com/nefe/You-Dont-Need-jQuery#query-selector – NDUF May 09 '19 at 08:27

2 Answers2

5

Just use querySelectorAll and use the query string .row > .node:

const nodes = document.querySelectorAll(".row > .node");
nodes.forEach(node => console.log(node.textContent));
.node {
  border-radius: 50%;
  border: 1px solid #000;
}

.node,
.empty {
  display: inline-block;
  height: 50px;
  width: 50px;
  line-height: 50px;
  text-align: center;
}

.d-none {
  display: none;
}
<div class="row">
  <div class="node">
    Foo
  </div>
  <div class="node d-none">
    Foo
  </div>
  <div class="row">
    <div class="empty">
      &nbsp;
    </div>
    <div class="node d-none">
      Foo
    </div>
  </div>
</div>

In almost all cases, a selector string that works to select elements in jQuery will work to select elements with querySelectorAll as well (and to select elements with CSS). (There are a few rare exceptions like :contains, which only have meaning in jQuery.)

Note that you need to use

.row > .node

not

.row < .node

to select .nodes which are children of a .row.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Does `document.querySelectorAll` return the same as what I described above? And a bit off-topic, but what benefit other than readability does the `.forEach` has? Is it working the same as my loop, or even better? – Barrosy May 09 '19 at 08:31
  • 1
    As one paste puts it: Loops fail to clearly signal the intention of your code, and as such should be avoided whenever possible (prefer higher-order functions, such as .map, .reduce, .filter, ...). See also: http://qr.ae/RNMEGA and https://gist.github.com/robotlolita/7643014 Code readability is possibly the *most important thing* to optimize for when writing code, in most situations (IMO), so I always avoid `for` loops when possible, they're verbose, require manual iteration, have no built-in abstraction, and aren't composable. – CertainPerformance May 09 '19 at 08:35
  • 1
    `querySelectorAll` returns a NodeList, which is an array-like object (and has the `forEach` method), but it's not exactly the same thing as an array. If you want to transform it into an array, you can spread it: `[...document.querySelectorAll(..)]` – CertainPerformance May 09 '19 at 08:36
  • So if I understand it correctly: if I would like to get a single element within a HTML collection (e.g. by using `document.getElementsByClassName`), it would not be a correct way to use a loop to get this job done? Would you address that situation with above given example? Or would that be a different matter? (Am just asking this out of curiosity) – Barrosy May 09 '19 at 08:45
  • 1
    Problems with the `getElementsBy` methods are that (1) they're inflexible (in comparison to the ability to use a query string, as you can see with `querySelectorAll`) and (2) they're *live* collections - they may mutate *while you're iterating over them*! This is very unintuitive and is a frequent source of bugs. – CertainPerformance May 09 '19 at 08:51
  • 1
    If you used `getElementsByClassName` to achieve your goal here, you would have to specify that the `.node` you want is a direct child of the current `.row` being iterated over - perhaps call `querySelector` on the `.row`, or (worse idea) turn the `.children` of the `.row` into an array and then use `.find` to find a child which is a `.node`. – CertainPerformance May 09 '19 at 08:51
0

document.querySelector('.row > .node') The selector parameter in querySelector function is the same with CSS/JQuery

Neil Ning
  • 11
  • 3