- ×Show All
Overview
What this is:
NodeList.js
makes using the Native DOM APIs on anArray
ofNodes
as easy asjQuery
with the benefits of it being extremely small at around 4k minified, and the browser as a dependency (That's the most interesting part).The first thing you'll notice is I'm using
$$
, the reason I chose this for selectingDOM Nodes
is because if you open up your devtools and type in the following:$$('div'); // Will return a NodeList
NodeList.js
Usage:The
HTML
we'll manipulate in the following:<body> <div id="container" class="cont"> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> <div class="child"></div> </div> </body>
Let's start of by querying
#container
's children:Each of the following returns an
Array of Nodes
(AKA myNodeList
, not the browser's nativeNodeList
)// Method 1 $$('#container div'); // Method 2 $$('#container').children; // Method 3 $$('div div');
If you pass a query string there's a second argument you can pass as the scope:
let container = document.getElementById('container'); $$('div', container);
Which would be equivalent to:
// Just this doesn't return my NodeList, but the browser's NodeList container.querySelectorAll('div');
You can pass nodes as arguments:
$$(document, document.body); // returns NodeList
You can also pass 1
Array
of Nodes or aNodeList
or anHTMLCollection
Will not be flattened, to flatten useconcat()
:$$([document, document.body]); // returns NodeList
Getting properties of each
Node
:How you would normally do it:
let children = document.getElementsByClassName('child');
Now you would get properties on the
#container
's children:for(let i = 0, l = children.length; i < l; i++) { children[i].id; // '' children[i].nodeName; // 'DIV' children[i].className; // 'child' }
Here's how you would do it with
nodeList.js
:$$('.child').id; // ['', '' ... x10] $$('.child').nodeName; // ['DIV', 'DIV' ... x10] $$('.child').className; // ['child', 'child' ... x10]
Therefore you would read each property just like you would with a single
Node
:)Notice how it returns an
Array
of the property's value, meaning you can select them byindex
and use anyArray Methods
on them, you'll see when you get to the looping part.Setting properties on each
node
:Let's continue using the
children
variable, so this is how you would set properties on thechildren
:for(let i = 0, l = children.length; i < l; i++) { children[i].className = 'containerChild'; children[i].textContent = 'This is some text'; }
Here's how you'd do it with
NodeList.js
:$$('.child').className = 'containerChild'; $$('.child').textContent = 'This is some text';
Calling methods on each
node
:Still using the
children
variable:Let's add an event listener to each node, even though
event delegation
would be best, but for the sake of this example:for(let i = 0, l = children.length; i < l; i++) { children[i].addEventListener('click', function() { console.log(this, 'was clicked'); }); }
Here's how you'd do it with
NodeList.js
:$$('.child').addEventListener('click', function() { console.log(this, 'was clicked'); });
So cool right? You can use any
Native DOM method
:Let's set some attributes:
$$('.child').setAttribute('class', 'child div'); // For setting the class you could just do: $$('.child').className = 'child div';
Clicking the elements:
$$('.child').click();
Removing the elements:
$$('.child').remove();
I think you're getting the point: any
Native DOM Method
that everyNode/Element
inherits you could just call on theNodeList
and it'll be called on each element.BTW: All
DOM
Methods that would normally returnundefined
when called on a singleNode
will return the sameNodeList
back to allow Method Chaining. LikesetAttribute()
.Looping
Using a for loop and
ES6
for-of
:We'll just remove the nodes from the
DOM
as examples:let nodes = $$('.child'); for(let i = 0, l = nodes.length; i < l; i++) { nodes[i].remove(); } for(let node of nodes) { node.remove(); }
Using
forEach
:// Removes all Nodes and returns same the NodeList to allow method chaining $$('.child').forEach(function(node) { node.remove(); }); // But Just do: $$('.child').remove();
Looping through the properties:
// Returns Array of style objects (CSSStyleDeclaration) let styles = $$('.child').style; for(let i = 0, l = styles.length; i < l; i++) { styles[i].color = 'red'; } for(let style of styles) { style.color = 'red'; } styles.forEach(function(style) { style.color = 'red'; }); // OR loop through the nodes themselves let nodes = $$('.child'); for(let i = 0, l = nodes.length; i < l; i++) { nodes[i].style.color = 'red'; } for(let node of nodes) { node.style.color = 'red'; } nodes.forEach(function(node) { node.style.color = 'red'; });
Array Methods
Slice
// Returns NodeList containing first Node $$('.child').slice(0, 1);
Map
Mapping is easy just get the property just like you would on a single Node
// Returns an Array of the id of each Node in the NodeList $$('#container').id; // No need for $$('#container').map(function(element) { return element.id; }); // Map() Checks if Array is fully populated with nodes so returns a NodeList populated with firstChld nodes $$('#container div').map(function(div) { return div.firstChild; }); // Maps the firstChild node and removes it, and returns the NodeList of firstChild Nodes $$('#container').map(function(div) { return div.firstChild; }).remove(); // Or: $$('#container').firstChild.remove();
Filter
// Filter out the #container div $$('div').filter(function(div) { return !div.matches('#container'); });
Reduce
I couldn't think of a better example for using Reduce on a NodeList (but it's possible)
let unique = $$('div').reduce(function(set, div) { set.add(div.parentElement); return set; }, new Set());
There's also
reduceRight()
Concat
The following
concat()
methods all return a new concatenatedNodeList
(Not affecting theNodeList
thatconcat()
is being called on)let divs = $$('div'); // Method 1 passing a Node let divsAndBody = divs.concat(document.body); // Method 2 passing an Array of Nodes let divsAndBody = divs.concat([document.body]); // Method 3 passing a NodeList let divsAndBody = divs.concat($$('body')); // Method 4 passing an Array of NodeList let divsAndBody = divs.concat([$$('body')]); // Method 5 passing multiple Nodes as arguments let divsAndBodyAndHTML = divs.concat(document.body, document.documentHTML); // Method 6 passing multiple Arrays of Nodes as arguments let divsAndBodyAndHTML = divs.concat([document.body], [document.documentHTML]); // Method 7 passing multiple Arrays of NodeList as are arguments let divsAndBodyAndHTML = divs.concat([$$('body')], [$$('html')]);
Concat()
is recursive so you can pass anArray
that is as deep as you'd like.Now if you pass anythinng that's not a
Node
,NodeList
,HTMLCollections
,Array
or deepArray of Arrays
that contain something other than aNode
,NodeList
,HTMLCollections
,Array
will Throw anError
.Push
let divs = $$('div'); // Pushes the document.body element, and returns the same NodeList to allow method chaining. divs.push(document.body);
Pop
let divs = $$('div'); // Removes last Node in the NodeList and returns a NodeList of the removed Nodes divs.pop();
pop()
takes an optional argument of how manyNodes
to POP// Removes last 2 Nodes in the NodeList and returns a NodeList of the removed Nodes divs.pop(2);
Shift
let divs = $$('div'); // Removes first Node in the NodeList and returns a NodeList of the removed Nodes divs.shift();
shift()
also takes an optional argument of how manyNodes
to SHIFT// Removes first 2 Nodes in the NodeList and returns a NodeList of the removed Nodes divs.shift(2);
Unshift
let divs = $$('div'); // Inserts/unshifts the document.body into the beginning of the NodeList and returns the same NodeList to allow method chaining. divs.unshift(document.body);
Splice
Let's replace the first element which would be #container with document.body
let divs = $$('div'); // Removes the first Element, inserts document.body in its place and returns a NodeList of the spliced Nodes divs.splice(0, 1, document.body);
Sort
let divs = $$('.child'); // Gives each div a data-index attribute divs.forEach(function(div, index) { div.dataset.index = index; }); // Reverse the NodeList and returns the same NodeList divs.sort(function(div1, div2) { return div2.dataset.index - div1.dataset.index; });
Reverse
// Returns the same NodeList, but reversed $$('div').reverse();
Join
I didn't put a
join
method forNodeLists
because it'd be useless on the actual Nodes:// Returns "[object HTMLDivElement], [object HTMLDivElement] ..." $$('.child').join();
Therefore you can still use it when mapping out properties:
// Returns "child,child,child,child,child,child,child,child,child,child" $$('.child').className.join();
Includes
// Returns true if passed Node is included in the NodeList $$('body').includes(document.body);
Find
// Returns body element: <body> $$('body').find(function(el) { return el === el; });
FindIndex
// Returns 0 $$('body').findIndex(function(el) { return el === el; });
There may be
DOM
methods that are the same name as the ones ofArray.prototype
in the future, or you may just want to convert theNodeList
to anArray
therefore you can use as a nativeArray
:The
asArray
property$$('body').asArray; // returns Array $$('body').asArray.forEach(function() {...}); // uses native Array method therefore you cannot chain
Ok now how about dealing with elements that have unique properties. Like
HTMLAnchorElement(s)
they have thehref
property which is not inherited fromHTMLElement
. There are noHTMLAnchorElements
in this example but here's how you'll deal with it.Special Methods
Get
// Returns undefined because it's a unique property that every element does not inherit $$('a').href // Returns an Array of href values $$('a').get('href');
Get()
can also be used on anArray
of properties:// Returns an Array of the value of each node.style.color $$('.child').style.get('color');
Set
// Sets the href property of each Node in NodeList $$('a').set('href', 'https://www.example.com/');
set()
will only set the properties on theNodes
who's properties are not undefined:$$('div, a').set('href', 'https://www.example.com/');
href
will only be set on the<a>
elements and not the<div>
sset()
can also be used on anArray
of properties:// Sets each element's color to red and returns the Array of styles back $$('.child').style.set('color', 'red');
You can also set multiple properties:
$$('.child').set({ textContent: 'Hello World', className: 'class1 class2' });
Same with mapped properties:
$$('.child').style.set({ color: 'red', background: 'black' });
Remember you can chain:
$$('.child').set({ textContent: 'Hello World', className: 'class1 class2' }).style.set({ color: 'red', background: 'black' });
Call
There are methods which are unique to certain elements. This is how you would call those methods:
$$('video').call('pause');
Or you could just loop through the elements and call the methods
What about passing arguments:
// Returns Array of `CanvasRenderingContext2D` $$('canvas').call('getContext', '2d');
If the method called on any of the elements returns something, an
Array
of those returned items would be returned fromcall()
otherwise theNodeList
will be returned to allow method chaining.The Item Method
The browser's native
item(index)
method does the same asNodeList[index]
but in mine it returns thatNode
as a myNodeList
(If you knowjQuery
it's the same as jQuery'seq()
method)// returns the <html> element $$('html, body')[0]; // returns my NodeList [<html>] $$('html, body').item(0);
This is so that you can keep using the same properties/methods of my NodeList, instead of having to
slice
out the oneNode
The
owner
property:All the owner propety does is give you back the
NodeList
that the property was mapped from:var elms = $$('.child'); elms.style.owner === elms; // true
So I can do all kinds of stuff:
Remember mapping
style
returns anArray
ofCSSStyleDeclarations
$$('.child').style;
This will give you back the
NodeList
whichstyle
was mapped from:var childs = $$('.child'); childs.style.owner === childs; // true
If you know
jQuery
its the same as itsprevObj
propertyAdding Your Own Methods:
$$.NL.myMethod = function() { // You'll have to write your own loop here if you want to call this on each Node or use: this.forEach(function(node) { // do something with each node }); }
NodeListJS Compatability
Browser Version FireFox 6+ Safari 5.0.5+ Chrome 6+ IE 9+ Opera 11+ Attention: You have to realize that my library's dependent on the browser it's running (which is awesome, so it automatically updates when the browser updates the
DOM
with new properties/methods) meaning: let's say the propertyhidden
doesn't exist in the browser'sDOM
API you can't do:$$('.child').hidden = true;