Ein häufiges Bedürfnis beim Schreiben von Vanilla JavaScript ist das Finden einer Auswahl von Elementen im DOM und das Durchlaufen dieser Elemente. Zum Beispiel das Finden von Instanzen eines Buttons und das Anhängen eines Klick-Handlers.
const buttons = document.querySelectorAll(".js-do-thing");
// There could be any number of these!
// I need to loop over them and attach a click handler.
Es gibt SO VIELE Möglichkeiten, dies zu tun. Lassen Sie uns sie durchgehen.
forEach
forEach ist normalerweise für Arrays, und interessanterweise ist das, was von querySelectorAll zurückkommt, kein Array, sondern eine NodeList. Glücklicherweise unterstützen die meisten modernen Browser die Verwendung von forEach auf NodeLists ohnehin.
buttons.forEach((button) => {
button.addEventListener('click', () => {
console.log("forEach worked");
});
});
Wenn Sie sich Sorgen machen, dass forEach möglicherweise nicht auf Ihrer NodeList funktioniert, könnten Sie sie zuerst in ein Array "spreaden"
[...buttons].forEach((button) => {
button.addEventListener('click', () => {
console.log("spread forEach worked");
});
});
Aber ich bin mir nicht sicher, ob das etwas nützt, da es unwahrscheinlich ist, dass es Browser gibt, die Spreads unterstützen, aber nicht forEach auf NodeLists. Vielleicht wird es seltsam, wenn Transpilation ins Spiel kommt, aber ich weiß es nicht. So oder so, Spreading ist nett, falls Sie etwas anderes Array-spezifisches verwenden möchten, wie .map(), .filter() oder .reduce().
Eine etwas ältere Methode ist, sich mit diesem kleinen Hack in das natürliche forEach des Arrays einzuklinken
[].forEach.call(buttons, (button) => {
button.addEventListener('click', () => {
console.log("array forEach worked");
});
});
Todd Motto hat diese Methode allerdings einmal stark kritisiert, also seien Sie gewarnt. Er empfahl, Ihre eigene Methode zu entwickeln (aktualisiert für ES6)
const forEach = (array, callback, scope) => {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]);
}
};
...die wir so verwenden würden
forEach(buttons, (index, button) => {
console.log("our own function worked");
});
for .. of
Die Browserunterstützung für for .. of Schleifen sieht ziemlich gut aus und das scheint mir eine super saubere Syntax zu sein
for (const button of buttons) {
button.addEventListener('click', () => {
console.log("for .. of worked");
});
}
Sofort ein Array erstellen
const buttons = Array.prototype.slice.apply(
document.querySelectorAll(".js-do-thing")
);
Jetzt können Sie alle normalen Array-Funktionen verwenden.
buttons.forEach((button) => {
console.log("apply worked");
});
Alte for-Schleife
Wenn Sie maximale Browserkompatibilität benötigen, ist es keine Schande, eine uralte klassische for-Schleife zu verwenden
for (let i = 0; i < buttons.length; ++i) {
buttons[i].addEventListener('click', () => {
console.log("for loop worked");
});
}
Moment! Das obige Beispiel enthält Pfeilfunktionen und ES6 let. Wenn Sie ältere Versionen unterstützen möchten und altes IE und ähnliches, müssen Sie...
for (var i = 0; i < buttons.length; ++i) {
buttons[i].addEventListener('click', function() {
console.log("for loop worked");
});
}
Bibliotheken
Wenn Sie jQuery verwenden, müssen Sie sich nicht einmal darum kümmern...
$(".buttons").on("click", () => {
console.log("jQuery works");
});
Wenn Sie ein React/JSX-Setup verwenden, müssen Sie sich überhaupt keine Gedanken über diese Art von Binding machen.
Lodash hat auch ein _.forEach, das vermutlich bei älteren Browsern hilft.
_.forEach(buttons, (button, key) => {
console.log("lodash worked");
});
Umfrage
Twitter-Leute
const els = document.querySelectorAll(".foo");
// welche Schleife verwendest du? eine davon? eine andere?
— Chris Coyier (@chriscoyier) 7. November 2018
Außerdem gibt es hier ein Pen mit all diesen Optionen.
Etwas bessere Unterstützung als NodeList.forEach (obwohl immer noch kein IE, wenn Sie es brauchen) ist
Wussten Sie, dass Sie eine Mapping-Funktion an
Array.fromübergeben können? Sie können diese verwenden, um über die Werte zu iterieren.Ich verwende oft
Array.from(buttons), danach habe ich ein normalesarray– schauen Sie hier für mehr Informationen und auch einen Polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from[…document.querySelectorAll(‘.my-item’)].forEach((item) => {item.doSomething()});
ES6 hat auch die Methode Array.from eingeführt. Sie können ihr ein Array-ähnliches Objekt (wie eine NodeList) übergeben.
[].forEach.callist unnötig ineffizient, da ein Array ohne Grund instanziiert wird; verwenden Sie stattdessenArray.prototype.forEach.call. Für eine geringfügige weitere Effizienzsteigerung und wahrscheinlich bessere Ergonomie:const forEach = Array.prototype.forEach;undforEach.call(…). Wenn Sie.callnicht mögen, würdeconst forEach = Function.prototype.call.bind(Array.prototype.forEach);es Ihnen erlauben, nurforEach(…)zu verwenden.)Aber wirklich, in fast allen Situationen würde ich dringend empfehlen, Bibliotheksfunktionen bei Bedarf einfach mit einem Polyfill zu versehen, eine gute Basis auszuwählen, sodass nur sehr wenige Benutzer den Polyfill benötigen, aber alte Browser einfach unterstützt werden können. Ich fand
Object.valuesals ein gutes Feature zur Überprüfung, wenn es um Sprachbibliotheksfunktionen geht. Für NodeList.prototype.forEach (eine Bibliotheksfunktion, keine der Sprache selbst) können Sie ganz einfach tun:if (!('forEach' in NodeList.prototype) { NodeList.prototype.forEach = Array.prototype.forEach; }.Ich bin überrascht, dass keine Rede von der Leistungsauswirkung ist, ein Event-Handler an jedem Knoten in der Liste anzubringen. Nun, meistens ist das vielleicht kein Problem, aber manchmal könnte es das sein, und dann ist der bessere Weg, einfach einen Event-Handler am Elternteil anzubringen und Aktionen am relevanten Knoten über Event.target auszuführen.
Ein kostengünstigerer Weg zum Schleifen könnte auf einer while-Schleife basieren, sodass der Code 50 % schneller ausgeführt wird, da er in jeder Iteration einfach einen Wert von "i" subtrahiert, der mehr als "0" ist, also nicht falsy ist, sodass die Schleife einfach weiterläuft; Dann könnte die Logik mit einer Funktion zum Ausblenden gefundener Knoten dank Callback- und anonymen Funktionen optimiert werden. Die Idee stammt aus dem 4. Kapitel des Callback-Patterns aus „JavaScript Patterns“ von Stoyan Stefanov
Gute Optionen.
Vergessen Sie Object.values() nicht
Ich mag es, den alten Weg zu gehen, um so viel wie möglich zu unterstützen, aber ich verwende eine Closure für den Geltungsbereich, wenn ich komplizierte Dinge innerhalb der Schleife tun werde.
Delegierte Event-Handler sind oft ebenfalls erwähnenswert.
Sie erfordern nicht das Warten auf das vollständige Laden und Parsen der Seite. Stattdessen können Sie den Event-Handler am Dokument anbringen. Das bedeutet, Sie vermeiden das Problem von Elementen auf der Seite, die sichtbar sind, aber noch nicht funktionieren, bis JS ankommt (vorausgesetzt, es wird das tun...).
Sie erfordern auch keine synchrone DOM-Traversal über querySelector. Sie erfordern nicht das Anhängen vieler Event-Handler.
https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/