document.all
ist ein nicht primitives Objekt im DOM, das falsch ist.
Zum Beispiel macht dieser Code nichts:
if (document.all) {
alert("hello");
}
Kann jemand erklären, warum das so ist?
document.all
ist ein nicht primitives Objekt im DOM, das falsch ist.
Zum Beispiel macht dieser Code nichts:
if (document.all) {
alert("hello");
}
Kann jemand erklären, warum das so ist?
Mathias Bynens
Haftungsausschluss: Ich bin der Typ, der die Frage getwittert hat, die zu diesem Thread geführt hat 🙂 Es war eine Frage, die ich in meinem stellen und beantworten würde Front-Trends sprechen. Ich schrieb diesen Tweet 5 Minuten bevor ich auf die Bühne ging.
Die Frage, die ich gestellt habe, ist die folgende.
Die ECMAScript-Spezifikation definiert ToBoolean()
wie folgt:
Wie Sie sehen können, sind alle nicht-primitiven Objekte (dh alle Objekte, die keine booleschen Werte, Zahlen, Zeichenfolgen, undefined
oder null
) sind wahrheitsgemäß gemäß der Spezifikation. Im DOM gibt es jedoch eine Ausnahme – ein DOM-Objekt, das falsch ist. Weißt du welcher das ist?
Die Antwort ist document.all
. Die HTML-Spezifikation sagt:
Die
all
Attribut muss ein zurückgebenHTMLAllCollection
verwurzelt an derDocument
Knoten, dessen Filter auf alle Elemente passt.Das für alle zurückgegebene Objekt weist mehrere ungewöhnliche Verhaltensweisen auf:
Der Benutzeragent muss sich so verhalten, als ob die
ToBoolean()
-Operator in JavaScript konvertiert das Objekt, für das zurückgegeben wirdall
zumfalse
Wert.Der Benutzeragent muss so handeln, als ob er für die Zwecke der
==
und!=
Operatoren in JavaScript das Objekt, für das zurückgegeben wirdall
ist gleich dem
undefined
Wert.Der Benutzeragent muss so handeln, dass die
typeof
-Operator in JavaScript gibt die Zeichenfolge zurück'undefined'
wenn es auf das zurückgegebene Objekt angewendet wird
all
.Diese Anforderungen stellen einen vorsätzlichen Verstoß gegen die zum Zeitpunkt des Schreibens aktuelle JavaScript-Spezifikation (ECMAScript Edition 5) dar. Die JavaScript-Spezifikation erfordert, dass die
ToBoolean()
Operator konvertiert alle Objekte in dietrue
Wert und hat keine Bestimmungen für Gegenstände, die so tun, als ob sie es wärenundefined
für die Zwecke bestimmter Betreiber. Diese Verletzung wird durch den Wunsch nach Kompatibilität mit zwei Klassen von Legacy-Inhalten motiviert: eine, die das Vorhandensein von verwendetdocument.all
als eine Möglichkeit, alte Benutzeragenten zu erkennen, und eine, die nur diese alten Benutzeragenten unterstützt und die verwendet
document.all
Objekt, ohne vorher auf seine Anwesenheit zu testen.
Damit, document.all
ist die einzige offizielle Ausnahme von dieser ECMAScript-Regel. (In Oper, document.attachEvent
usw. sind auch falsch, aber das ist nirgendwo angegeben.)
Der obige Text erklärt, warum dies getan wurde. Aber hier ist ein Beispiel-Code-Snippet, das auf alten Webseiten sehr häufig vorkommt und das weiter veranschaulichen wird:
if (document.all) {
// code that uses `document.all`, for ancient browsers
} else if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
}
Im Grunde schon lange document.all
wurde auf diese Weise verwendet, um alte Browser zu erkennen. Weil document.all
Getestet wird allerdings erst, modernere Browser, die beide Eigenschaften bieten, würden trotzdem in der landen document.all
Codepfad. In modernen Browsern würden wir lieber verwenden document.getElementById
natürlich, aber da die meisten Browser immer noch haben document.all
(aus anderen Abwärtskompatibilitätsgründen) die else
würde nie zugegriffen werden, wenn document.all
war Wahrheit. Wäre der Code anders geschrieben, wäre dies kein Problem:
if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
} else if (document.all) {
// code that uses `document.all`, for ancient browsers
}
Aber leider macht es ein Großteil des vorhandenen Codes umgekehrt.
Die einfachste Lösung für dieses Problem ist einfach zu machen document.all
in Browsern falsch sein, die es immer noch imitieren.
Ziemlich komplizierte Antwort für eine veraltete Funktion.
– adrianp
23. Mai 2013 um 13:27 Uhr
@adrian Willkommen im Web, wo alles wegen veralteter Funktionen kompliziert ist 🙂
– Mathias Bynens
3. Oktober 2013 um 9:07 Uhr
Aber im Oktober 17 wurde dieser Hinweis entfernt, obwohl das Verhalten bis heute gleich bleibt …
– Jyrkka
2. April 2020 um 6:55 Uhr
D. Pardal
Es gibt jetzt eine [[IsHTMLDDA]]interner Steckplatz für Objekte:
Ein [[IsHTMLDDA]]Interner Slot kann auf implementierungsdefinierten Objekten vorhanden sein. Objekte mit einem [[IsHTMLDDA]]internen Steckplatz verhalten sich wie
undefined
in den abstrakten Operationen ToBoolean und Abstract Equality Comparison und bei Verwendung als Operand für dietypeof
Operator.
Der HTML-Standard wurde ebenfalls aktualisiert, um diesen internen Slot für Objekte hinzuzufügen, die die implementieren HTMLAllCollection
Schnittstelle:
Objekte, die die HTMLAllCollection-Schnittstelle implementieren, sind ältere Plattformobjekte mit einem zusätzlichen [[Call]]interne Methode, die im folgenden Abschnitt beschrieben wird. Sie haben auch eine [[IsHTMLDDA]]interner Steckplatz.
Der Grund für diesen Wahnsinn ist in dieser Anmerkung im HTML-Standard angegeben:
Diese besonderen Verhaltensweisen werden durch den Wunsch nach Kompatibilität mit zwei Klassen von Legacy-Inhalten motiviert: eine, die das Vorhandensein von
document.all
als eine Möglichkeit, alte Benutzeragenten zu erkennen, und eine, die nur diese alten Benutzeragenten unterstützt und die verwendetdocument.all
Objekt, ohne vorher auf seine Anwesenheit zu testen.
Im Grunde möchte der Standard also mit diesen beiden Arten von Code kompatibel sein:
Code, der überprüft, ob er in Internet Explorer ausgeführt wird, um seine nicht standardmäßigen Funktionen zu verwenden, wie z document.all
und Activex;
if (document.all) {
useActiveXStuff();
}
Code, der davon ausgeht, dass er in Internet Explorer ausgeführt wird und verwendet document.all
.
document.all["my-button"].onclick = function () {
alert("hi");
};
Bergi
Moderne Browser implementieren dieses veraltete Ding nicht mehr. Es wurde von IE eingeführt, aber die meisten anderen “shimen” es, um kompatibel zu sein.
Um die Browsererkennung zu ermöglichen (früher konnte man IE von NN unterscheiden, indem man auf document.all
) Während die document.all-Syntax unterstützt wird, haben andere Browser die “seltsame” Implementierung vorgenommen typeof document.all
gibt undefiniert zurück.
Opera> document.all
// prints the array-like object
Opera> typeof document.all
"undefined"
Opera> Boolean(document.all)
false
Bevor FF die Unterstützung dafür eingestellt hat, zeigte es auch ein seltsames Verhalten, wie in angegeben diese Nachricht. Möglicherweise finden Sie weitere Interna in Mozilla-Fehler Nr. 412247.
Da ist auch ein sehr langer Faden im W3C-Mailinglistenarchiv, beginnend mit http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html
Kurz gesagt, es geht darum, dass BEIDE dieser Codebeispiele funktionieren. Browser müssen dies tun, damit alte Webseiten weiterhin funktionieren.
// Internet Explorer
if (document.all) {
useActiveX()
}
// Netscape Navigator
else {
useOldButStillWorkingCode()
}
document.all.output.innerHTML = 'Hello, world!'
Moderne Browser implementieren dieses veraltete Ding nicht mehr. Es ist ein IE “Standard”, Opera “shims” es auch.
– Bergi
27. April 2012 um 11:54 Uhr
@Nanne die Frage ist: kann jemand erklären, warum der Code nichts tut. Wenn es nicht implementiert ist, ist if falsch und es passiert nichts. Ich denke also, es ist eine Antwort.
– Benutzer1150525
27. April 2012 um 12:02 Uhr
Aber die Frage besagte auch, dass wir es mit einem Nicht-Null-Objekt zu tun haben? Vielleicht habe ich das falsch gelesen, aber ich bin davon ausgegangen, dass das im Test bedeutet, dass es da war, aber nur nicht ausgelöst hat?
– Nanne
27. April 2012 um 12:05 Uhr
@Nanne: OK, ich habe die Frage jetzt verstanden. Ich habe eine weitere Antwort hinzugefügt.
– Bergi
27. April 2012 um 12:26 Uhr
In Kyle Simpsons Buch gibt es dazu eine gute tl;dr-Erklärung: github.com/getify/You-Dont-Know-JS/blob/master/… (scrollen Sie nach unten zur Überschrift „Warum?!“).
– imrek
29. Juni 2016 um 14:59 Uhr