Dasha Salo
Dasha Salo

Reputation: 5169

JavaScript NodeList

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!

Upvotes: 27

Views: 29416

Answers (11)

Zen
Zen

Reputation: 213

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

Upvotes: 0

yckart
yckart

Reputation: 33408

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

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

Upvotes: 2

Martin Zvar&#237;k
Martin Zvar&#237;k

Reputation: 2479

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

Upvotes: 0

Artur INTECH
Artur INTECH

Reputation: 7286

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:

Upvotes: 0

user1727984
user1727984

Reputation: 249

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

Upvotes: 1

Mike K.
Mike K.

Reputation: 11

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
}

Upvotes: 1

Yansky
Yansky

Reputation: 4760

document.querySelectorAll("input, select"); 

Upvotes: 13

Simon
Simon

Reputation: 37978

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

Upvotes: 45

Mario Menger
Mario Menger

Reputation: 5902

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

Upvotes: 20

Alexander Ulizko
Alexander Ulizko

Reputation: 231

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

})();

Upvotes: 0

Noldorin
Noldorin

Reputation: 147340

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.

Upvotes: 4

Related Questions