215

I'm trying to loop over ALL elements on a page, so I want to check every element that exists on this page for a special class.

So, how do I say that I want to check EVERY element?

isherwood
  • 58,414
  • 16
  • 114
  • 157
Florian Müller
  • 7,448
  • 25
  • 78
  • 120

12 Answers12

323

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.

lxg
  • 12,375
  • 12
  • 51
  • 73
Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 3
    This method seems very nice, but how can I select an element in the upper method? I only got the index 'i'? – Florian Müller Nov 23 '10 at 13:35
  • 3
    @Florian: just like you would access an array element -- `all[i]` would give you the current element. – Andy E Nov 23 '10 at 13:36
  • 3
    How to select the element in side the loop? – Debiprasad Apr 19 '13 at 13:39
  • 1
    Just out of curiosity, why did you create that max variable inside the for loop instead of just doing `...; i < all.length; i++`? – Jesse Aldridge Mar 22 '15 at 22:54
  • 2
    @JesseAldridge: just a force of habit/good practice. Avoiding the property lookup on every iteration is usually a micro-optimisation, but it's not particularly more difficult to write and so I just do it naturally. – Andy E Mar 23 '15 at 09:04
  • WOW! Even though it's a cool party trick to select `*`, OP clearly indicates he has an X/Y problem. Please emphasize that using a for loop to iterate over the entire dom to find a class is **not** a good idea! Also there is no need to check for querySelectorAll, just needs [getElementsByClassName](https://developer.mozilla.org/en/docs/Web/API/Document/getElementsByClassName) – Jonathan Oct 06 '15 at 15:23
  • 2
    @Jonathan `getElementsByClassName()` has worse support than `querySelectorAll()` (the former is not supported in IE 8). The OP clearly stated that he wants to loop over __all__ elements on a page, for which I gave him the solution and offered an alternative. I'm not sure what the problem is with that ;-). – Andy E Oct 06 '15 at 15:55
  • 1
    Wouldn't have guessed `querySelectorAll` has better support but you are correct. OP should definitely use that. – Jonathan Oct 06 '15 at 16:17
  • How do you tell if one element is above another (like after all the z indexes are calculated) with the results from querySelectorAll? – SuperUberDuper Aug 23 '16 at 19:56
  • @SuperUberDuper that's a bit trickier. For that, you'd need to check where the elements intersect and then use something like [document.elementsFromPoint](https://developer.mozilla.org/en-US/docs/Web/API/Document/elementsFromPoint) do determine the order. – Andy E Aug 24 '16 at 08:19
  • thanks, does elementsFromPoint follow the z - index stacking order the user sees? – SuperUberDuper Aug 25 '16 at 12:44
  • 1
    @SuperUberDuper According to [this document](https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint) - yes (key words: _in paint order_). Judging from the note on `elementFromPoint()`, elements excluded from being a target for hit testing (eg. `pointer-events: none` elements) will probably be omitted from the result. – ksadowski Aug 11 '17 at 16:54
  • document.querySelectorAll('*') does not match elements - at least in Chrome! – Rob Gravelle Dec 17 '18 at 14:23
  • @RobGravelle check your code, it works fine in Chrome :-) http://jsfiddle.net/AndyE/rb1L30ht/1/ – Andy E Dec 19 '18 at 09:42
  • var all = document.getElementsByTagName("*"); .. this works great if you just drop it into the main [script] tag. Then you can reuse the all var in all the called functions without ever reloading it. – Joseph Poirier Apr 28 '19 at 14:06
  • OMG you just completely changed my life. I just used this to store all elements if they have an id in an object called "$" with the id being the key. This is awesome. Now I can select them via $.id, an even shorter syntax than JQuery AND have the benefit of having them cached for faster access? Just wow! – Foxcode Apr 26 '20 at 18:57
  • @AndyE Getting all elements using ```getElementsByTagName('*')``` might result in repeating text elements. Recursion could be a way as well – Yi Xiang Chong Jul 09 '20 at 07:10
  • @AndyE will `getElementsByTagName()` traverse all child nodes too? – Volatil3 Jun 25 '22 at 09:36
50

Was looking for same. Well, not exactly. I only wanted to list all DOM Nodes.

var currentNode,
    ni = document.createNodeIterator(document.documentElement, NodeFilter.SHOW_ELEMENT);

while(currentNode = ni.nextNode()) {
    console.log(currentNode.nodeName);
}

To get elements with a specific class, we can use filter function.

var currentNode,
    ni = document.createNodeIterator(
                     document.documentElement, 
                     NodeFilter.SHOW_ELEMENT,
                     function(node){
                         return node.classList.contains('toggleable') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
                     }
         );

while(currentNode = ni.nextNode()) {
    console.log(currentNode.nodeName);
}

Found solution on MDN

traditional
  • 942
  • 10
  • 19
  • never saw document.ceeateNodeIterator. Seems interesting what new features JS brings ;) – Florian Müller Oct 03 '14 at 10:03
  • 3
    A cool feature of this is that the nodeiterator also walks the nodes in the order they appear in the html. I wonder if some of the `document.body.getElementsByTagName('*')` could return the nodes in scrambled order. – Civilian Aug 09 '16 at 21:55
  • Wow it's actually well supported! – rogerdpack May 01 '20 at 05:40
19

As always the best solution is to use recursion:

loop(document);
function loop(node){
    // do some thing with the node here
    var nodes = node.childNodes;
    for (var i = 0; i <nodes.length; i++){
        if(!nodes[i]){
            continue;
        }

        if(nodes[i].childNodes.length > 0){
            loop(nodes[i]);
        }
    }
}

Unlike other suggestions, this solution does not require you to create an array for all the nodes, so its more light on the memory. More importantly, it finds more results. I am not sure what those results are, but when testing on chrome it finds about 50% more nodes compared to document.getElementsByTagName("*");

Simson
  • 3,373
  • 2
  • 24
  • 38
Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
  • 26
    The best time to use recursion is the best time to use recursion. – Adamlive Oct 05 '17 at 14:05
  • 11
    “it founds about 50% more nodes in compare to `document.getElementsByTagName("*");`” — yup, it’ll find [text nodes and comment nodes as well as element nodes](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType). As the OP was just asking about elements, that’s unnecessary. – Paul D. Waite Mar 02 '18 at 16:03
  • 4
    It _might_ be lighter on memory. Depending on how much you do in each level of recursion, you can build a mightily big call stack by the time you get to the bottom. A `NodeList` is simply referencing the `Node`s that are already built in your DOM, so it's not as heavy as you might imagine. Someone who knows more can weigh in, but I think it's just a memory reference size, so 8 bytes per node. – Josh from Qaribou Jun 28 '18 at 21:23
11

Here is another example on how you can loop through a document or an element:

function getNodeList(elem){
var l=new Array(elem),c=1,ret=new Array();
//This first loop will loop until the count var is stable//
for(var r=0;r<c;r++){
    //This loop will loop thru the child element list//
    for(var z=0;z<l[r].childNodes.length;z++){

         //Push the element to the return array.
        ret.push(l[r].childNodes[z]);

        if(l[r].childNodes[z].childNodes[0]){
            l.push(l[r].childNodes[z]);c++;
        }//IF           
    }//FOR
}//FOR
return ret;
}
Community
  • 1
  • 1
Juggernogger93
  • 119
  • 1
  • 2
5

Andy E. gave a good answer.

I would add, if you feel to select all the childs in some special selector (this need happened to me recently), you can apply the method "getElementsByTagName()" on any DOM object you want.

For an example, I needed to just parse "visual" part of the web page, so I just made this

var visualDomElts = document.body.getElementsByTagName('*');

This will never take in consideration the head part.

synaptikon
  • 699
  • 1
  • 8
  • 16
korvus
  • 334
  • 4
  • 5
5

For those who are using Jquery

$("*").each(function(i,e){console.log(i+' '+e)});
Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
3

from this link
javascript reference

<html>
<head>
<title>A Simple Page</title>
<script language="JavaScript">
<!--
function findhead1()
{
    var tag, tags;
    // or you can use var allElem=document.all; and loop on it
    tags = "The tags in the page are:"
    for(i = 0; i < document.all.length; i++)
    {
        tag = document.all(i).tagName;
        tags = tags + "\r" + tag;
    }
    document.write(tags);
}

//  -->
</script>
</head>
<body onload="findhead1()">
<h1>Heading One</h1>
</body>
</html>

UPDATE:EDIT

since my last answer i found better simpler solution

function search(tableEvent)
    {
        clearResults()

        document.getElementById('loading').style.display = 'block';

        var params = 'formAction=SearchStocks';

        var elemArray = document.mainForm.elements;
        for (var i = 0; i < elemArray.length;i++)
        {
            var element = elemArray[i];

            var elementName= element.name;
            if(elementName=='formAction')
                continue;
            params += '&' + elementName+'='+ encodeURIComponent(element.value);

        }

        params += '&tableEvent=' + tableEvent;


        createXmlHttpObject();

        sendRequestPost(http_request,'Controller',false,params);

        prepareUpdateTableContents();//function js to handle the response out of scope for this question

    }
shareef
  • 9,255
  • 13
  • 58
  • 89
  • according to [this SO discussion](http://stackoverflow.com/questions/2408424/document-all-vs-document-getelementbyid), `document.all` is discouraged in favor of `document.getElementBy*`. – thejoshwolfe Apr 06 '13 at 18:29
  • @thejoshwolfe thanks what do you think of my socond solution i updated – shareef Apr 07 '13 at 06:04
2

Getting all elements using var all = document.getElementsByTagName("*"); for (var i=0, max=all.length; i < max; i++); is ok if you need to check every element but will result in checking or looping repeating elements or text.

Below is a recursion implementation that checks or loop each element of all DOM elements only once and append:

(Credits to @George Reith for his recursion answer here: Map HTML to JSON)

function mapDOMCheck(html_string, json) {
  treeObject = {}

  dom = new jsdom.JSDOM(html_string) // use jsdom because DOMParser does not provide client-side Window for element access
  document = dom.window.document
  element = document.querySelector('html')

  // Recurse and loop through DOM elements only once
  function treeHTML(element, object) {
    var nodeList = element.childNodes;

    if (nodeList != null) {
      if (nodeList.length) {
        object[element.nodeName] = []; // IMPT: empty [] array for parent node to push non-text recursivable elements (see below)

        for (var i = 0; i < nodeList.length; i++) {
          console.log("nodeName", nodeList[i].nodeName);

          if (nodeList[i].nodeType == 3) { // if child node is **final base-case** text node
            console.log("nodeValue", nodeList[i].nodeValue);
          } else { // else
            object[element.nodeName].push({}); // push {} into empty [] array where {} for recursivable elements
            treeHTML(nodeList[i], object[element.nodeName][object[element.nodeName].length - 1]);
          }
        }
      }
    }
  }

  treeHTML(element, treeObject);

}
Yi Xiang Chong
  • 744
  • 11
  • 9
0

Use *

var allElem = document.getElementsByTagName("*");
for (var i = 0; i < allElem.length; i++) {
    // Do something with all element here
}
ashleedawg
  • 20,365
  • 9
  • 72
  • 105
jacky wong
  • 11
  • 1
  • 2
0

i think this is really quick

document.querySelectorAll('body,body *').forEach(function(e) {
defend orca
  • 617
  • 7
  • 17
0

this is how one can make black and white mode looping through all elements

var all = document.getElementsByTagName("*");
for (var i=0, max=all.length; i < max; i++) {
     all[i].style.backgroundColor  = 'black';
     all[i].style.color  = 'white';
}
Hebe
  • 661
  • 1
  • 7
  • 13
-2

You can try with document.getElementsByClassName('special_class');

Jimish Gamit
  • 1,014
  • 5
  • 15
  • 4
    The correct method is `getElementsByClassName()` and it's not supported by Internet Explorer up to version 9. – Andy E Nov 23 '10 at 13:31