web-dev-qa-db-fra.com

Pourquoi document.querySelectorAll renvoie-t-il un StaticNodeList plutôt qu'un vrai tableau?

Cela me dérange que je ne puisse pas simplement faire document.querySelectorAll(...).map(...) même dans Firefox 3.6, et je ne trouve toujours pas de réponse, donc j'ai pensé que je posterais sur SO la question de ce blog:

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

Est-ce que quelqu'un connaît une raison technique pour laquelle vous n'obtenez pas un tableau? Ou pourquoi un StaticNodeList n'hérite pas d'un tableau de telle manière que vous pouvez utiliser map, concat, etc.?

(BTW si ce n'est qu'une fonction que vous voulez, vous pouvez faire quelque chose comme NodeList.prototype.map = Array.prototype.map; ... mais encore une fois, pourquoi cette fonctionnalité (intentionnellement?) Est bloquée en premier lieu?)

87
Kev

Je pense que c'est une décision philosophique du W3C. La conception du DOM [spec] du W3C est assez orthogonale à la conception de JavaScript, car le DOM est destiné à être neutre en termes de plateforme et de langage.

Les décisions comme "getElementsByFoo() renvoie un NodeList" ordonné ou "querySelectorAll() renvoie un StaticNodeList" sont très intentionnelles, de sorte que les implémentations n'ont pas de s'inquiéter de l'alignement de leur structure de données retournée en fonction des implémentations dépendantes du langage (comme .map étant disponible sur les tableaux en JavaScript et Ruby, mais pas sur les listes en C #).

Le W3C vise bas: ils diront qu'un NodeList devrait contenir une en lecture seule .length Propriété de type unsigned long parce qu'ils croient que chaque implémentation peut au moins prendre en charge que , mais ils ne diront pas explicitement que l'opérateur d'index [] devrait être surchargé pour prendre en charge l'obtention d'éléments positionnels, car ils ne veulent pas contrecarrer un petit langage pauvre qui vient implémenter getElementsByFoo() mais ne supporte pas la surcharge d'opérateur. C'est une philosophie répandue présente dans la plupart des spécifications.

John Resig a exprimé une option similaire comme la vôtre, à laquelle il ajoute :

Mon argument n'est pas tant que NodeIterator n'est pas très semblable à DOM, c'est qu'il n'est pas très semblable à JavaScript. Il ne tire pas parti des fonctionnalités présentes dans le langage JavaScript et les utilise au mieux de ses capacités ...

Je sympathise quelque peu. Si le DOM était écrit spécifiquement avec des fonctionnalités JavaScript à l'esprit, il serait beaucoup moins gênant et plus intuitif à utiliser. En même temps, je comprends les décisions de conception du W3C.

73
Crescent Fresh

Vous pouvez utiliser ES2015 (ES6) opérateur d'étalement :

[...document.querySelectorAll('div')]

convertira StaticNodeList en tableau d'éléments.

Voici un exemple d'utilisation.

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>
153
Vlad Bezden

Je ne sais pas pourquoi il renvoie une liste de nœuds au lieu d'un tableau, peut-être parce que, comme getElementsByTagName, il mettra à jour le résultat lorsque vous mettrez à jour le DOM. Quoi qu'il en soit, une méthode très simple pour transformer ce résultat en un tableau simple est:

Array.prototype.slice.call(document.querySelectorAll(...));

et ensuite vous pouvez faire:

Array.prototype.slice.call(document.querySelectorAll(...)).map(...);
42
mck89

Pour ajouter à ce que Crescent a dit,

si c'est juste une fonction que vous voulez, vous pouvez faire quelque chose comme NodeList.prototype.map = Array.prototype.map

Ne faites pas ça! Ce n'est pas du tout garanti de fonctionner.

Aucun standard JavaScript ou DOM/BOM ne spécifie que la fonction constructeur NodeList existe même en tant que propriété globale/window, ou que le NodeList renvoyé par querySelectorAll en héritera, ou que son prototype est accessible en écriture, ou que la fonction Array.prototype.map fonctionnera réellement sur une NodeList.

Une NodeList peut être un "objet hôte" (et en est un, dans IE et certains navigateurs plus anciens). Les méthodes Array sont définies comme étant autorisées à fonctionner sur n'importe quel "Objet natif" JavaScript qui expose les propriétés numériques et length, mais ils ne sont pas tenus de travailler sur les objets Host (et dans IE, ils ne le font pas).

Il est ennuyeux que vous n'obteniez pas toutes les méthodes de tableau sur les listes DOM (toutes, pas seulement StaticNodeList), mais il n'y a pas de moyen fiable de le contourner. Vous devrez convertir manuellement chaque liste DOM que vous récupérez en tableau:

Array.fromList= function(list) {
    var array= new Array(list.length);
    for (var i= 0, n= list.length; i<n; i++)
        array[i]= list[i];
    return array;
};

Array.fromList(element.childNodes).forEach(function() {
    ...
});
12
bobince

Je pense que vous pouvez simplement faire

Array.prototype.map.call(document.querySelectorAll(...), function(...){...});

Cela fonctionne parfaitement pour moi

2
Max Leps

C'est une option que je voulais ajouter à la gamme d'autres possibilités suggérées par d'autres ici. Il est uniquement destiné au plaisir intellectuel et n'est pas déconseillé .


Juste pour le fun de celui-ci, voici un moyen de "forcer" querySelectorAll à s'agenouiller et à s'incliner devant vous:

Element.prototype.querySelectorAll = (function(QSA){
    return function(){
        return [...QSA.call(this, arguments[0])]
    }
})(Element.prototype.querySelectorAll);

Maintenant, cela fait du bien de passer à travers cette fonction, en lui montrant qui est le patron. Maintenant, je ne sais pas quoi de mieux, en créant un tout nouveau wrapper de fonction nommé et que tout votre code utilise ce nom étrange (à peu près de style jQuery ) ou remplacer une fois la fonction comme ci-dessus pour que le reste de votre code puisse toujours utiliser le nom de la méthode DOM d'origine querySelectorAll.

  • Une telle approche éliminerait l'utilisation possible de sous-méthodes

Je ne recommanderais cela en aucune façon, sauf si vous ne donnez honnêtement pas un [vous savez quoi].

0
vsync