NodeList.js
rend l'utilisation des API DOM natives sur un Array
de Nodes
aussi simple que jQuery
avec les avantages d'être extrêmement petit, à environ 4k minifiés , et le navigateur en tant que dépendance (c'est la partie la plus intéressante) .
La première chose que vous remarquerez est que j'utilise $$
, la raison pour laquelle j'ai choisi cela pour sélectionner DOM Nodes
est que si vous ouvrez vos outils de développement et tapez ce qui suit :
$$ ( 'div' ) ; // Will return a NodeList
NodeList.js
: HTML
que nous allons manipuler comme suit : < 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 >
#container
: Chacun des éléments suivants renvoie un Array of Nodes
(AKA my NodeList
, pas la NodeList
native du navigateur)
// Method 1
$$ ( '#container div' ) ;
// Method 2
$$ ( '#container' ) . children ;
// Method 3
$$ ( 'div div' ) ;
Si vous transmettez une chaîne de requête, vous pouvez transmettre un deuxième argument comme portée :
let container = document . getElementById ( 'container' ) ;
$$ ( 'div' , container ) ;
Ce qui équivaudrait à :
// Just this doesn't return my NodeList, but the browser's NodeList
container . querySelectorAll ( 'div' ) ;
Vous pouvez passer des nœuds comme arguments :
$$(document, document.body); // returns NodeList
Vous pouvez également passer 1 Array
de nœuds ou une NodeList
ou une HTMLCollection
qui ne sera pas aplatie, pour aplatir utilisez concat()
:
$$([document, document.body]); // returns NodeList
Node
:Comment procéderiez-vous normalement :
let children = document . getElementsByClassName ( 'child' ) ;
Vous obtiendrez maintenant les propriétés sur les enfants du #container
:
for ( let i = 0 , l = children . length ; i < l ; i ++ ) {
children [ i ] . id ; // ''
children [ i ] . nodeName ; // 'DIV'
children [ i ] . className ; // 'child'
}
Voici comment procéder avec nodeList.js
:
$$ ( '.child' ) . id ; // ['', '' ... x10]
$$ ( '.child' ) . nodeName ; // ['DIV', 'DIV' ... x10]
$$ ( '.child' ) . className ; // ['child', 'child' ... x10]
Par conséquent, vous liriez chaque propriété comme vous le feriez avec un seul Node
:)
Remarquez comment il renvoie un Array
de la valeur de la propriété, ce qui signifie que vous pouvez les sélectionner par index
et utiliser n'importe quelle Array Methods
dessus, vous verrez lorsque vous arriverez à la partie en boucle.
node
: Continuons à utiliser la variable children
, voici donc comment définir les propriétés des children
:
for ( let i = 0 , l = children . length ; i < l ; i ++ ) {
children [ i ] . className = 'containerChild' ;
children [ i ] . textContent = 'This is some text' ;
}
Voici comment procéder avec NodeList.js
:
$$ ( '.child' ) . className = 'containerChild' ;
$$ ( '.child' ) . textContent = 'This is some text' ;
node
: Toujours en utilisant la variable children
:
Ajoutons un écouteur d'événement à chaque nœud, même si event delegation
serait la meilleure solution, mais pour le bien de cet exemple :
for ( let i = 0 , l = children . length ; i < l ; i ++ ) {
children [ i ] . addEventListener ( 'click' , function ( ) {
console . log ( this , 'was clicked' ) ;
} ) ;
}
Voici comment procéder avec NodeList.js
:
$$ ( '.child' ) . addEventListener ( 'click' , function ( ) {
console . log ( this , 'was clicked' ) ;
} ) ;
Tellement cool, non ? Vous pouvez utiliser n'importe quelle Native DOM method
:
Définissons quelques attributs :
$$ ( '.child' ) . setAttribute ( 'class' , 'child div' ) ;
// For setting the class you could just do:
$$ ( '.child' ) . className = 'child div' ;
En cliquant sur les éléments :
$$ ( '.child' ) . click ( ) ;
Suppression des éléments :
$$ ( '.child' ) . remove ( ) ;
Je pense que vous comprenez le point : n'importe quelle Native DOM Method
dont chaque Node/Element
hérite, vous pouvez simplement appeler la NodeList
et elle sera appelée sur chaque élément.
BTW : toutes les méthodes DOM
qui renverraient normalement undefined
lorsqu'elles sont appelées sur un seul Node
renverront la même NodeList
pour permettre le chaînage de méthodes. Comme setAttribute()
.
Utiliser une boucle for et ES6
for-of
:
Nous allons simplement supprimer les nœuds du DOM
à titre d'exemple :
let nodes = $$ ( '.child' ) ;
for ( let i = 0 , l = nodes . length ; i < l ; i ++ ) {
nodes [ i ] . remove ( ) ;
}
for ( let node of nodes ) {
node . remove ( ) ;
}
Utilisation de forEach
:
// Removes all Nodes and returns same the NodeList to allow method chaining
$$ ( '.child' ) . forEach ( function ( node ) {
node . remove ( ) ;
} ) ;
// But Just do:
$$ ( '.child' ) . remove ( ) ;
Parcourir les propriétés :
// 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' ;
} ) ;
// Returns NodeList containing first Node
$$ ( '.child' ) . slice ( 0 , 1 ) ;
Le mappage est facile, il suffit d'obtenir la propriété comme vous le feriez sur un seul nœud
// 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 out the #container div
$$ ( 'div' ) . filter ( function ( div ) {
return ! div . matches ( '#container' ) ;
} ) ;
Je ne pouvais pas penser à un meilleur exemple pour utiliser Réduire sur une NodeList (mais c'est possible)
let unique = $$ ( 'div' ) . reduce ( function ( set , div ) {
set . add ( div . parentElement ) ;
return set ;
} , new Set ( ) ) ;
Il y a aussi reduceRight()
Les méthodes concat()
suivantes renvoient toutes une nouvelle NodeList
concaténée (n'affectant pas la NodeList
sur laquelle concat()
est appelée)
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()
est récursif, vous pouvez donc transmettre un Array
aussi profond que vous le souhaitez.
Maintenant, si vous transmettez quelque chose qui n'est pas un Node
, NodeList
, HTMLCollections
, Array
ou Array of Arrays
contenant autre chose qu'un Node
, NodeList
, HTMLCollections
, Array
générera une Error
.
let divs = $$ ( 'div' ) ;
// Pushes the document.body element, and returns the same NodeList to allow method chaining.
divs . push ( document . body ) ;
let divs = $$ ( 'div' ) ;
// Removes last Node in the NodeList and returns a NodeList of the removed Nodes
divs . pop ( ) ;
pop()
prend un argument facultatif indiquant le nombre Nodes
à POP
// Removes last 2 Nodes in the NodeList and returns a NodeList of the removed Nodes
divs . pop ( 2 ) ;
let divs = $$ ( 'div' ) ;
// Removes first Node in the NodeList and returns a NodeList of the removed Nodes
divs . shift ( ) ;
shift()
prend également un argument facultatif indiquant le nombre Nodes
à SHIFT.
// Removes first 2 Nodes in the NodeList and returns a NodeList of the removed Nodes
divs . shift ( 2 ) ;
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 ) ;
Remplaçons le premier élément qui serait #container par 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 ) ;
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 ;
} ) ;
// Returns the same NodeList, but reversed
$$ ( 'div' ) . reverse ( ) ;
Je n'ai pas mis de méthode join
pour NodeLists
car cela serait inutile sur les nœuds réels :
// Returns "[object HTMLDivElement], [object HTMLDivElement] ..."
$$ ( '.child' ) . join ( ) ;
Par conséquent, vous pouvez toujours l'utiliser lors du mappage des propriétés :
// Returns "child,child,child,child,child,child,child,child,child,child"
$$ ( '.child' ) . className . join ( ) ;
// Returns true if passed Node is included in the NodeList
$$ ( 'body' ) . includes ( document . body ) ;
// Returns body element: <body>
$$ ( 'body' ) . find ( function ( el ) {
return el === el ;
} ) ;
// Returns 0
$$ ( 'body' ) . findIndex ( function ( el ) {
return el === el ;
} ) ;
Il se peut qu'il y ait des méthodes DOM
qui porteront le même nom que celles de Array.prototype
à l'avenir, ou vous souhaiterez peut-être simplement convertir la NodeList
en un Array
que vous pourrez donc utiliser comme Array
natif :
asArray
$$ ( 'body' ) . asArray ; // returns Array
$$ ( 'body' ) . asArray . forEach ( function ( ) { ... } ) ; // uses native Array method therefore you cannot chain
Ok, que diriez-vous maintenant de traiter des éléments qui ont des propriétés uniques. Comme HTMLAnchorElement(s)
ils ont la propriété href
qui n'est pas héritée de HTMLElement
. Il n'y a pas HTMLAnchorElements
dans cet exemple, mais voici comment vous allez le gérer.
// 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()
peut également être utilisé sur un Array
de propriétés :
// Returns an Array of the value of each node.style.color
$$ ( '.child' ) . style . get ( 'color' ) ;
// Sets the href property of each Node in NodeList
$$ ( 'a' ) . set ( 'href' , 'https://www.example.com/' ) ;
set()
définira uniquement les propriétés sur les Nodes
dont les propriétés ne sont pas indéfinies :
$$ ( 'div, a' ) . set ( 'href' , 'https://www.example.com/' ) ;
href
ne sera défini que sur les éléments <a>
et non sur les <div>
s
set()
peut également être utilisé sur un Array
de propriétés :
// Sets each element's color to red and returns the Array of styles back
$$ ( '.child' ) . style . set ( 'color' , 'red' ) ;
Vous pouvez également définir plusieurs propriétés :
$$ ( '.child' ) . set ( {
textContent : 'Hello World' ,
className : 'class1 class2'
} ) ;
Idem avec les propriétés mappées :
$$ ( '.child' ) . style . set ( {
color : 'red' ,
background : 'black'
} ) ;
N'oubliez pas que vous pouvez enchaîner :
$$ ( '.child' ) . set ( {
textContent : 'Hello World' ,
className : 'class1 class2'
} ) . style . set ( {
color : 'red' ,
background : 'black'
} ) ;
Il existe des méthodes propres à certains éléments. Voici comment appeler ces méthodes :
$$ ( 'video' ) . call ( 'pause' ) ;
Ou vous pouvez simplement parcourir les éléments et appeler les méthodes
Qu'en est-il du passage d'arguments :
// Returns Array of `CanvasRenderingContext2D`
$$ ( 'canvas' ) . call ( 'getContext' , '2d' ) ;
Si la méthode appelée sur l'un des éléments renvoie quelque chose, un Array
de ces éléments renvoyés sera renvoyé par call()
sinon la NodeList
sera renvoyée pour permettre le chaînage des méthodes.
La méthode native item(index)
du navigateur fait la même chose que NodeList[index]
mais dans la mienne, elle renvoie ce Node
en tant que ma NodeList
(si vous connaissez jQuery
c'est la même chose que la méthode eq()
de jQuery)
// returns the <html> element
$$ ( 'html, body' ) [ 0 ] ;
// returns my NodeList [<html>]
$$ ( 'html, body' ) . item ( 0 ) ;
Ceci afin que vous puissiez continuer à utiliser les mêmes propriétés/méthodes de ma NodeList, au lieu d'avoir à slice
le seul Node
owner
: Tout ce que fait la propriété propriétaire, c'est vous renvoyer la NodeList
à partir de laquelle la propriété a été mappée :
var elms = $$ ( '.child' ) ;
elms . style . owner === elms ; // true
Je peux donc faire toutes sortes de choses :
N'oubliez pas que style
de mappage renvoie un Array
de CSSStyleDeclarations
$$ ( '.child' ) . style ;
Cela vous rendra la NodeList
à partir de laquelle style
a été mappé :
var childs = $$ ( '.child' ) ;
childs . style . owner === childs ; // true
Si vous connaissez jQuery
c'est la même chose que sa propriété prevObj
$$ . 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
} ) ;
}
Navigateur | Version |
---|---|
FireFox | 6+ |
Safari | 5.0.5+ |
Chrome | 6+ |
IE | 9+ |
Opéra | 11+ |
Attention : vous devez réaliser que ma bibliothèque dépend du navigateur qu'elle exécute (ce qui est génial, donc elle se met automatiquement à jour lorsque le navigateur met à jour le DOM
avec de nouvelles propriétés/méthodes) ce qui signifie : disons que la propriété hidden
n'existe pas dans le navigateur. API DOM
que vous ne pouvez pas faire : $$('.child').hidden = true;