How to Loop Through All the Elements Returned from Getelementsbytagname

How to loop through all the elements returned from getElementsByTagName

You need to convert the nodelist to array with this:

<html>
<head>
</head>
<body>
<input type="text" value="" />
<input type="text" value="" />
<script>
function ShowResults(value, index, ar) {
alert(index);
}
var input = document.getElementsByTagName("input");
var inputList = Array.prototype.slice.call(input);
alert(inputList.length);
inputList.forEach(ShowResults);
</script>
</body>
</html>

or use for loop.

for(let i = 0;i < input.length; i++)
{
ShowResults(input[i].value);
}

and change ShowResults function to:

function ShowResults(value) {
alert(value);
}

Why do we need to do that?

Some objects in JavaScript look like an array, but they aren’t one. That usually means that they have indexed access and a length property, but none of the array methods. Examples include the special variable arguments, DOM node lists, and strings. Array-Like Objects and Generic Methods gives tips for working with array-like objects.
source

UPDATE for 07.10.2019

Nowdays with ES6 you can use [...inputList].forEach, or Array.from(inputList)

Looping through JavaScript getElementsByTagName() object

You have an error in the console: Uncaught TypeError: Cannot set property 'display' of undefined

Try:

var wrapper = document.querySelector(".wrapper");

var divs = wrapper.getElementsByTagName("div");

for (i = 0; i < divs.length; ++i) {
each = divs[i];
if (each.classList.contains("test2")) {
each.style.display = "none";
}
}

Demo

How to getElementsByTagName in a for loop

There are three problems with the code. First: myP.lenght should be myP.length. Second: You are incrementing myP, you should increment i and the third: style.color should be "red" and not red.

var myP = document.getElementsByTagName('p');for (var i = 0; i < myP.length; i++) { // should be i++  myP[i].style.color = "red"; // color = "red"}
<div id="wrap">  <p>first paragraph </p>  <p>2nd paragraph </p>  <p>3rd paragraph </p></div>

Why can't I iterate over all the elements returned by getElementsByTagName?

Change the first line to:

var tds = [].slice.call(document.getElementsByTagName('td'), 0);

The value returned from .getElementsByTagName() is a NodeList, not an array. NodeList objects are "live", which means that they change as you change the DOM. That is, tds.length is decrementing, but your i is incrementing as well - thus you're missing an element each iteration. If you turn it into an array first, as above, then your code should work.

Iterating over elements from DOMDocument::getElementsByTagName() doesn't work

The problem seems to be that you are changing the document structure while trying to iterate over it.

The alternative is to use XPath, which will take it's own copy of the nodes for you to loop over, the changes are fairly small, but will give the output your after...

public function replace() {
$xp = new DOMXPath($this->document);

foreach ($this->tags as $name => $callable) {
$elements = $xp->query("//".$name);
foreach ($elements as $element) {
$callable($element, $this->document);
}
}

return $this->document->saveHTML();
}

How can I loop through ALL DOM elements on a page?

You can pass a * to getElementsByTagName() so that it will return all elements in a page:

var all = document.getElementsByTagName("*");

for (var i=0, max=all.length; i < max; i++) {
// Do something with the element here
}

Note that you could use querySelectorAll(), if it's available (IE9+, CSS in IE8), to just find elements with a particular class.

if (document.querySelectorAll)
var clsElements = document.querySelectorAll(".mySpeshalClass");
else
// loop through all elements instead

This would certainly speed up matters for modern browsers.


Browsers now support foreach on NodeList. This means you can directly loop the elements instead of writing your own for loop.

document.querySelectorAll('*').forEach(function(node) {
// Do whatever you want with the node object.
});

Performance note - Do your best to scope what you're looking for by using a specific selector. A universal selector can return lots of nodes depending on the complexity of the page. Also, consider using document.body.querySelectorAll instead of document.querySelectorAll when you don’t care about <head> children.

How to fix 'for each' iteration with getELementsbyTagName?

There is in fact only one element with that class name, article-content, so you are doing an outer loop of one and thus get no further than i = 1. Additionally, during your first loop you are changing the variable you are looping over which will most likely lead to an error.

For Each para In paras
Set para = para.getElementsByTagName("p")(i)

In the above, para is your loop variable.

Also, the collection returned by para.getElementsByTagName("p") will start at 0.

How your code would work is if you indexed into initial collection returned by getElementsByClassName and then chain on getElementsByTagName, and use that as your collection to For Each over (leaving index starting at 1 as you can then use it to write out to the correct row; you can use your loop variable para to get the current node innerText):

Option Explicit
Public Sub TryKeywordSearch()
Dim http As Object, html As New HTMLDocument
Dim paras As Object, para As Object, i As Long

Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://www.fool.com/earnings/call-transcripts/2019/07/17/netflix-inc-nflx-q2-2019-earnings-call-transcript.aspx", False
http.send
html.body.innerHTML = http.responseText
Set paras = html.getElementsByClassName("article-content")(0).getElementsByTagName("p")
i = 1
For Each para In paras
ThisWorkbook.Worksheets("Sheet1").Cells(i, 1).Value = para.innerText
i = i + 1
Next
End Sub

Instead, you can use a faster, and more readable IMO, css selector combination to get all p tags within a parent with class article-content:

Option Explicit

Public Sub GetParagraphs()
Dim http As Object, html As HTMLDocument, paragraphs As Object, i As Long
Set html = New HTMLDocument

With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://www.fool.com/earnings/call-transcripts/2019/07/17/netflix-inc-nflx-q2-2019-earnings-call-transcript.aspx", False
.send
html.body.innerHTML = .responseText
End With
Set paragraphs = html.querySelectorAll(".article-content p")
For i = 0 To paragraphs.Length - 1
ThisWorkbook.Worksheets("Sheet1").Cells(i + 1, 1) = paragraphs.item(i).innerText
Next i
End Sub

For loop for HTMLCollection elements

In response to the original question, you are using for/in incorrectly. In your code, key is the index. So, to get the value from the pseudo-array, you'd have to do list[key] and to get the id, you'd do list[key].id. But, you should not be doing this with for/in in the first place.

Summary (added in Dec 2018)

Do not ever use for/in to iterate a nodeList or an HTMLCollection. The reasons to avoid it are described below.

All recent versions of modern browsers (Safari, Firefox, Chrome, Edge) all support for/of iteration on DOM lists such nodeList or HTMLCollection.

Here's an example:

var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}

To include older browsers (including things like IE), this will work everywhere:

var list = document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
console.log(list[i].id); //second console output
}

Explanation For Why You Should Not Use for/in

for/in is meant for iterating the properties of an object. That means it will return all iterable properties of an object. While it may appear to work for an array (returning array elements or pseudo-array elements), it can also return other properties of the object that are not what you are expecting from the array-like elements. And, guess what, an HTMLCollection or nodeList object can both have other properties that will be returned with a for/in iteration. I just tried this in Chrome and iterating it the way you were iterating it will retrieve the items in the list (indexes 0, 1, 2, etc...), but also will retrieve the length and item properties. The for/in iteration simply won't work for an HTMLCollection.


See http://jsfiddle.net/jfriend00/FzZ2H/ for why you can't iterate an HTMLCollection with for/in.

In Firefox, your for/in iteration would return these items (all the iterable properties of the object):

0
1
2
item
namedItem
@@iterator
length

Hopefully, now you can see why you want to use for (var i = 0; i < list.length; i++) instead so you just get 0, 1 and 2 in your iteration.


Evolution of Browser Support for NodeList and HTMLCollection iteration

Following below is an evolution of how browsers have evolved through the time period 2015-2018 giving you additional ways to iterate. None of these are now needed in modern browsers since you can use the options described above.

Update for ES6 in 2015

Added to ES6 is Array.from() that will convert an array-like structure to an actual array. That allows one to enumerate a list directly like this:

"use strict";

Array.from(document.getElementsByClassName("events")).forEach(function(item) {
console.log(item.id);
});

Working demo (in Firefox, Chrome, and Edge as of April 2016): https://jsfiddle.net/jfriend00/8ar4xn2s/


Update for ES6 in 2016

You can now use the ES6 for/of construct with a NodeList and an HTMLCollection by just adding this to your code:

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

Then, you can do:

var list = document.getElementsByClassName("events");
for (var item of list) {
console.log(item.id);
}

This works in the current version of Chrome, Firefox, and Edge. This works because it attaches the Array iterator to both the NodeList and HTMLCollection prototypes so that when for/of iterates them, it uses the Array iterator to iterate them.

Working demo: http://jsfiddle.net/jfriend00/joy06u4e/.


Second Update for ES6 in Dec 2016

As of Dec 2016, Symbol.iterator support has been built-in to Chrome v54 and Firefox v50 so the code below works by itself. It is not yet built-in for Edge.

var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}

Working demo (in Chrome and Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/

Third Update for ES6 in Dec 2017

As of Dec. 2017, this capability works in Edge 41.16299.15.0 for a nodeList as in document.querySelectorAll(), but not an HTMLCollection as in document.getElementsByClassName() so you have to manually assign the iterator to use it in Edge for an HTMLCollection. It is a total mystery why they'd fix one collection type, but not the other. But, you can at least use the result of document.querySelectorAll() with ES6 for/of syntax in current versions of Edge now.

I've also updated the above jsFiddle so it tests both HTMLCollection and nodeList separately and captures the output in the jsFiddle itself.

Fourth Update for ES6 in Mar 2018

Per mesqueeeb, Symbol.iterator support has been built-in to Safari too, so you can use for (let item of list) for either document.getElementsByClassName() or document.querySelectorAll().

Fifth Update for ES6 in Apr 2018

Apparently, support for iterating an HTMLCollection with for/of will be coming to Edge 18 in Fall 2018.

Sixth Update for ES6 in Nov 2018

I can confirm that with Microsoft Edge v18 (that is included in the Fall 2018 Windows Update), you can now iterate both an HTMLCollection and a NodeList with for/of in Edge.

So, now all modern browsers contain native support for for/of iteration of both the HTMLCollection and NodeList objects.



Related Topics



Leave a reply



Submit