Question

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!

Was it helpful?

Solution

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);

OTHER TIPS

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')
document.querySelectorAll("input, select"); 

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.

function mergeNodeLists(a, b) {
  var slice = Array.prototype.slice;
  return slice.call(a).concat(slice.call(b));
}

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

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
}

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)... }

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'));
    }

})();

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:

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');

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]);
                }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top