2

I would like to determine if a variable is a an array of DOM elements NodeList or a single DOM element. I tried Array.isArray(x) but it returns false when x = document.getElementsByClassName("test") because it is a NodeList not an array.

Is there a an alternative way to do this?

The solution I came up with was to test if typeof x.attributes == "undefined", presuming that all DOM elements will have an attributes property. Is there anything wrong with this?

sample:

var x = document.getElementsByClassName("test");
var y = x[0];

console.log(Array.isArray(x));                   // returns false
console.log(typeof x.attributes == "undefined"); // returns true

console.log(Array.isArray(y));                   // returns false
console.log(typeof y.attributes == "undefined"); // returns false

sample html:

<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
hal
  • 4,845
  • 7
  • 34
  • 57

3 Answers3

2

document.getElementsByClassName() turns back a NodeList not an Array, which is why you're getting false.

Try this in your browser console:

var x = document.getElementsByClassName("header")
x.isArray() // TypeError: Object #<NodeList> has no method 'isArray'
BWStearns
  • 2,567
  • 2
  • 19
  • 33
2

You already have two answers explaining, why it fails. To make it work, the way you might expect it, you could test for "array-like" objects:

Source: https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-7/array-like-objects

// Determine if o is an array-like object.
// Strings and functions have numeric length properties, but are 
// excluded by the typeof test. In client-side JavaScript, DOM text
// nodes have a numeric length property, and may need to be excluded 
// with an additional o.nodeType != 3 test.
function isArrayLike(o) {
    if (o &&                                // o is not null, undefined, etc.
        typeof o === "object" &&            // o is an object
        isFinite(o.length) &&               // o.length is a finite number
        o.length >= 0 &&                    // o.length is non-negative
        o.length===Math.floor(o.length) &&  // o.length is an integer
        o.length < 4294967296)              // o.length < 2^32
        return true;                        // Then o is array-like
    else
        return false;                       // Otherwise it is not
}

This basically just tests if the object has a length property that is a positive integer.

Here you can see it in action: fiddle

Another solution, that is more specific to your problem, would be to just test, if the object is an instance of NodeList:

console.log(x instanceof NodeList); // returns true
console.log(y instanceof NodeList); // returns false
basilikum
  • 10,378
  • 5
  • 45
  • 58
1

document.getElementsByClassName() returns an HTMLCollection, not an array:

elements = document.getElementsByClassName(names); // or:
elements = rootElement.getElementsByClassName(names);

elements is a HTMLCollection of found elements. names is a string representing the list of class names to match; class names are separated by whitespace getElementsByClassName can be called on any element, not only on the document. The element on which it is called will be used as the root of the search.

You'd need to convert that HTMLCollection into an array first.

The W3C spec also details this:

The getElementsByClassName(classNames) method must return the list of elements with class names classNames for the context object.

Given the following XHTML fragment:

<div id="example">
    <p id="p1" class="aaa bbb"/>
    <p id="p2" class="aaa ccc"/>
    <p id="p3" class="bbb ccc"/>
</div>

A call to document.getElementById('example').getElementsByClassName('aaa') would return a HTMLCollection with the two paragraphs p1 and p2 in it.

A call to getElementsByClassName('ccc bbb') would only return one node, however, namely p3. A call to document.getElementById('example').getElementsByClassName('bbb ccc ') would return the same thing.

A call to getElementsByClassName('aaa,bbb') would return no nodes; none of the elements above are in the aaa,bbb class.

The getElementById(elementId) method must return the first element, in tree order, within the context object's tree, whose ID is elementId, or null if there is none.

Codeman
  • 12,157
  • 10
  • 53
  • 91