JavaScript NodeList
Asked Answered
C

11

27

is there a way to join 2 NodeLists returned by 2 calls of document.getElementsByTagName?

Say, I have the following code

var inputs = documentElement.getElementsByTagName('input');
var selects = document.getElementsByTagName('select');

I want to loop through the results. Is it possible in one loop?

Thank you in advance!

Costa answered 27/5, 2009 at 9:17 Comment(1)
Looping them both at the same time or sequentially?Those
M
45

Seems like you can use the same Array.prototype.slice.call that makes the args array-like object become an array. (See here)

var inputs = document.getElementsByTagName('input');
var selects = document.getElementsByTagName('select');

inputs = Array.prototype.slice.call(inputs);
selects = Array.prototype.slice.call(selects);

var res = inputs.concat(selects);

alert(res.length);
Mcinerney answered 27/5, 2009 at 10:22 Comment(2)
I was just about to say that :) +1 Prototype and other libraries use slice to convert the node lists to arrays.Immaterialize
A minor thing to keep in mind is that this won't work in Internet Explorer or the BlackBerry browser. jQuery 1.4 has an interesting test that is used to fall back to a loop to combine Node Lists. I've excerpted it here: pastebin.com/TkTwMG17Jehias
G
20

You can't join them, but you can still loop through them sequentially in one loop like this:

for ( var i = 0; i < inputs.length + selects.length; i++ ) {
    var element = ( i < inputs.length ) ? inputs[i] : selects[i-inputs.length];
}

Alternatively, using jQuery, you could select them all in one go:

$('input, select')
Gopherwood answered 27/5, 2009 at 9:54 Comment(0)
P
13
document.querySelectorAll("input, select"); 
Phospholipide answered 23/2, 2010 at 11:36 Comment(2)
requires FF 3.1+, Safari 3.1+, or IE8+Artois
Very helpful. A simple alternative approach to the to main goal in question.Widow
C
4

As far as I know, the NodeList type is immutable (see this article for example), which means you'll have to generate your own object.

A simple method would just be to create an array and copy all the elements into that array.

var inputs = documentElement.getElementsByTagName('input');
var selects = document.getElementsByTagName('select');
var all = new Array(inputs.length + selects.length);

var index = 0;
for (i = 0; i < inputs.length; i++)
    all[index++] = inputs[i];
for (i = 0; i < selects.length; i++)
    all[index++] = selects[i];

The all variable then contains the union of the two sets of nodes.

Caravan answered 27/5, 2009 at 9:34 Comment(3)
It will need 2 loops anyway. For my particular task I need to loop through the results only once so creating an object is not worth it. I can't join the results and that's what I wanted to know, thank you!Costa
Yeah, using two loops is the clearest solution in my opinion, anyway. (I considered the one loop option, but it doesn't really help things.) Glad you have a solution.Caravan
Well. And what about Array.prototype.slice.call(thatNodeListOfYours, 0); ?Tubulure
M
2
function mergeNodeLists(a, b) {
  var slice = Array.prototype.slice;
  return slice.call(a).concat(slice.call(b));
}

console.log( mergeNodeLists( inputs, selects ) ); // => [input, select]

Maddi answered 23/6, 2013 at 15:58 Comment(0)
G
1

I threw this together. There might be a bit of overhead from doing an if and .length for every loop, but I think its minor unless the number of elements get extreme.

inputs = div.getElementsByTagName('input');
selects = div.getElementsByTagName('select');
for (i=0; i<inputs.length+selects.length; i++) {
    element = (i<inputs.length ? inputs[i] : selects[i-inputs.length]);

    // do whatever with element
}
Gnomic answered 4/4, 2011 at 22:45 Comment(0)
B
1

My short code for bookmarklets:

var e, t = d.getElementsByTagName('textarea'), u = d.getElementsByTagName('input'), i = t.length;
    while(e = (i > 0) ? t[--i] : u[-i--]){ if(e.offsetHeight > 0)... }
Barbaresi answered 8/10, 2012 at 7:18 Comment(0)
G
0

First, I thought that this is possible to concat arrays using Array.prototype, like this:

Array.prototype.concat.call(selects, inputs);

But it doesn't work, so that I've made an arrays from node collections and concat it. Looks like that:

(function () {

    var inputs = document.getElementsByTagName('input'),
        selects = document.getElementsByTagName('select'),
        result,
        i,
        node;

    function convert (collection) {
        var a = [];
        for (var i = 0, length = collection.length; i < length; i++) {
            a.push(collection[i]);
        }
        return a;
    }

    // concatenation && convertation
    result = Array.prototype.concat(convert(inputs), convert(selects));
    // traversing
    i = result.length;
    while(node = result[--i]) {
        alert(node.getAttribute('name'));
    }

})();
Gelding answered 27/5, 2009 at 9:53 Comment(2)
I believe the first trick works in any (major) browser except for IE.Those
And first trick won't work at WebKit-based browsers (safari, chrome)Gelding
M
0

Nowadays I would definitely use the following:

Chrome, Firefox 3.5+, IE8+

var elements = document.querySelectorAll('a');

for (var i = 0, element; (element = elements[i]); i++) {
    console.log(element);
}

IE11+, Firefox 24+, Chrome 30+ (with experiments enabled)

let elements = document.querySelectorAll('a');

for (let i = 0, element; (element = elements[i]); i++) {
    console.log(element);
}

"element = elements[i]" is preferred over "elements.length" since:

"Node lists are often implemented as node iterators with a filter. This means that getting a property like length is O(n), and iterating over the list by re-checking the length will be O(n^2)."

Unlike array access, which is as far as I remember O(1).

More details:

Marchak answered 15/2, 2014 at 9:37 Comment(0)
B
0

Array.prototype.slice.call() fails in IE 7, use this:

Object.prototype.getMyElements = function(tags){
    tags = tags.split(',');
    var i, j, col=[], ci=0;
    for(i=0; i<tags.length; i++) {
        var objs = this.getElementsByTagName(tags[i]);
        for(j=0; j<objs.length; j++) col[ci++] = objs[j];
    }
    return col;
}
var objs = document.getMyElements('INPUT,TEXTAREA');
var objs = document.getElementById('myform').getMyElements('INPUT,TEXTAREA');
Burg answered 1/4, 2014 at 15:47 Comment(0)
R
0

try my way:

 var allES = [];
 var inputs = document.getElementsByTagName("input");
        for (i = 0; i < inputs.length; i++) {
              allES.push(inputs[i]);
            }
    // gather SELECT elements
         var selects = document.getElementsByTagName("select");
            for ( i=0; i < selects.length; i++){
                allES.push(selects[i]);
                }
Rovelli answered 24/2, 2015 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.