Was ist los mit der Erweiterung des DOM

Ich war kürzlich überrascht, wie wenig das Thema DOM-Erweiterungen im Web behandelt wird. Beunruhigend ist, dass die Nachteile dieser anscheinend nützlichen Praxis nur in bestimmten abgelegenen Kreisen bekannt zu sein scheinen . Der Mangel an Informationen könnte durchaus erklären, warum heute Skripte und Bibliotheken gebaut werden , die immer noch in diese Falle tappen. Ich möchte erklären, warum das Erweitern von DOM im Allgemeinen eine schlechte Idee ist , indem ich einige der damit verbundenen Probleme zeige. Wir werden auch mögliche Alternativen zu dieser schädlichen Übung untersuchen.

Was genau ist eine DOM-Erweiterung? Und wie funktioniert das alles?

So funktioniert die DOM-Erweiterung

Bei der DOM-Erweiterung werden DOM-Objekten einfach benutzerdefinierte Methoden / Eigenschaften hinzugefügt. Benutzerdefinierte Eigenschaften sind diejenigen, die in einer bestimmten Implementierung nicht vorhanden sind. Und was sind die DOM-Objekte? Dies sind Host - Objekte Implementierung Element, Event, Documentoder irgendwelchen von Dutzenden von anderen DOM - Schnittstellen. Während der Erweiterung können Methoden / Eigenschaften Objekten direkt oder ihren Prototypen hinzugefügt werden (jedoch nur in Umgebungen, die dies ordnungsgemäß unterstützen).

Die am häufigsten erweiterten Objekte sind wahrscheinlich DOM-Elemente (solche, die die ElementSchnittstelle implementieren ), die von Javascript-Bibliotheken wie Prototype und Mootools populär gemacht werden. Ereignisobjekte (solche, die die EventSchnittstelle implementieren ) und Dokumente ( DocumentSchnittstelle ) werden häufig ebenfalls erweitert.

In einer Umgebung, in der Prototypen von Element-Objekten verfügbar gemacht werden, sieht ein Beispiel für eine DOM-Erweiterung folgendermaßen aus:

  Element.prototype.hide = function() {
    this.style.display = 'none';
  };
  ...
  var element = document.createElement('p');

  element.style.display; // ''
  element.hide();
  element.style.display; // 'none'

Wie Sie sehen, wird die Funktion "Ausblenden" zunächst einer hideEigenschaft von zugewiesen Element.prototype. Es wird dann direkt für ein Element aufgerufen, und der Anzeigestil des Elements wird auf "none" gesetzt.

Der Grund, warum dies funktioniert, ist, dass das Objekt, auf das von verwiesen Element.prototypewird, tatsächlich eines der Objekte in der Prototypkette des P-Elements ist . Wenn eine hideEigenschaft darauf aufgelöst wird, wird sie in der gesamten Prototypkette durchsucht, bis sie auf diesem Element.prototypeObjekt gefunden wird.

Wenn wir die Prototypkette von P-Elementen in einigen modernen Browsern untersuchen würden, würde dies normalerweise so aussehen:

  // "^" denotes connection between objects in prototype chain

  document.createElement('p');
    ^
  HTMLParagraphElement.prototype
    ^
  HTMLElement.prototype
    ^
  Element.prototype
    ^
  Node.prototype
    ^
  Object.prototype
    ^
  null

Beachten Sie, wie der nächste Vorfahr in der Prototypkette des PElements das Objekt ist, auf das durch verwiesen wird HTMLParagraphElement.prototype. Dies ist ein Objekt, das für den Typ eines Elements spezifisch ist. Für Pelement ist es HTMLParagraphElement.prototype; für DIVelement ist es HTMLDivElement.prototype; für Aelement ist es HTMLAnchorElement.prototypeund so weiter.

Aber warum so seltsame Namen?

Diese Namen entsprechen tatsächlich den in der DOM Level 2-HTML-Spezifikation definierten Schnittstellen . Dieselbe Spezifikation definiert auch die Vererbung zwischen diesen Schnittstellen. Darin heißt es beispielsweise: „… Die HTMLParagraphElement-Schnittstelle verfügt über alle Eigenschaften und Funktionen der HTMLElement-Schnittstelle…“ ( Quelle ) und „… Die HTMLElement-Schnittstelle verfügt über alle Eigenschaften und Funktionen der Element-Schnittstelle…“ ( Quelle ) usw.

Wenn wir eine Eigenschaft für ein „Prototypobjekt“ eines Absatzelements erstellen würden, wäre diese Eigenschaft beispielsweise für ein Ankerelement nicht verfügbar:

  HTMLParagraphElement.prototype.hide = function() {
    this.style.display = 'none';
  };
  ...
  typeof document.createElement('a').hide; // "undefined"
  typeof document.createElement('p').hide; // "function"

Dies liegt daran, dass die Prototypkette des Ankerelements niemals das Objekt enthält, auf das von verwiesen wird HTMLParagraphElement. prototype, sondern stattdessen das Objekt, auf das von verwiesen wird HTMLAnchorElement.prototype. Zu „reparieren“ Damit können wir auf Eigenschaft des Objekts zuweisen in der Prototypkette positionierte weiter, wie die genannten durch HTMLElement.prototype, Element.prototypeoder Node.prototype.

In ähnlicher Weise würde das Erstellen einer Eigenschaft für Element.prototypenicht alle Knoten verfügbar sein, sondern nur für Knoten des Elementtyps. Wenn wir Eigenschaft für alle Knoten haben möchten (z. B. Textknoten, Kommentarknoten usw.), müssen wir Node.prototypestattdessen Eigenschaft von zuweisen . Apropos Text- und Kommentarknoten: So sieht die Schnittstellenvererbung normalerweise für sie aus:

  document.createTextNode('foo'); // < Text.prototype < CharacterData.prototype < Node.prototype
  document.createComment('bar'); // < Comment.prototype < CharacterData.prototype < Node.prototype

Nun ist es wichtig zu verstehen, dass die Belichtung dieser DOM-Objektprototypen nicht garantiert ist . Die DOM Level 2-Spezifikation definiert lediglich Schnittstellen und die Vererbung zwischen diesen Schnittstellen. Es wird nicht angegeben, dass eine globale ElementEigenschaft vorhanden sein soll, die auf ein Objekt verweist, das ein Prototyp aller Objekte ist, die die ElementSchnittstelle implementieren . Es wird auch nicht angegeben, dass eine globale NodeEigenschaft vorhanden sein soll, die auf ein Objekt verweist, das ein Prototyp aller Objekte ist, die die NodeSchnittstelle implementieren .

Internet Explorer 7 (und höher) ist ein Beispiel für eine solche Umgebung. es aussetzt nicht global Node, Element, HTMLElement, HTMLParagraphElement, oder andere Eigenschaften. Ein weiterer solcher Browser ist Safari 2.x (und höchstwahrscheinlich Safari 1.x).

Was können wir also in Umgebungen tun, in denen diese globalen "Prototyp" -Objekte nicht verfügbar sind ? Eine Problemumgehung besteht darin, DOM-Objekte direkt zu erweitern:

  var element = document.createElement('p');
  ...
  element.hide = function() {
    this.style.display = 'none';
  };
  ...
  element.style.display; // ''
  element.hide();
  element.style.display; // 'none'

Was schief gelaufen ist?

Es klingt erstaunlich, DOM-Elemente durch Prototypobjekte erweitern zu können. Wir nutzen den prototypischen Charakter von Javascript und Scripting DOM wird sehr objektorientiert. Tatsächlich schien die DOM-Erweiterung so verlockend nützlich, dass die Prototype Javascript-Bibliothek sie vor einigen Jahren zu einem wesentlichen Bestandteil ihrer Architektur machte. Aber was sich hinter scheinbar harmlosen Übungen verbirgt, ist eine riesige Menge Ärger. Wie wir gleich sehen werden, überwiegen die Nachteile dieses Ansatzes bei der browserübergreifenden Skripterstellung alle Vorteile bei weitem. Die DOM-Erweiterung ist einer der größten Fehler, die Prototype.js jemals gemacht hat .

Also, was sind diese Probleme?

Fehlende Spezifikation

Wie ich bereits erwähnt habe, ist die Belichtung von "Prototyp-Objekten" nicht Teil einer Spezifikation. DOM Level 2 definiert lediglich Schnittstellen und deren Vererbungsbeziehungen. Um für die Umsetzung 2 bis DOM Level entsprechen voll, gibt es keine Notwendigkeit , diejenigen global zu belichten Node, Element,HTMLElementusw. Objekte. Es besteht auch keine Verpflichtung, sie auf andere Weise freizulegen. Da es immer die Möglichkeit gibt, DOM-Objekte manuell zu erweitern, scheint dies kein großes Problem zu sein. Die Wahrheit ist jedoch, dass die manuelle Erweiterung ein ziemlich langsamer und unbequemer Prozess ist (wie wir gleich sehen werden). Die Tatsache, dass eine schnelle, auf "Prototypenobjekten" basierende Erweiterung nur ein gewisser De-facto-Standard für wenige Browser ist, macht diese Vorgehensweise unzuverlässig, wenn es um die zukünftige Übernahme oder Portabilität über nicht-konventionelle Plattformen (z. B. mobile Geräte) geht.

Hostobjekte haben keine Regeln

Das nächste Problem mit der DOM-Erweiterung ist, dass DOM-Objekte Host-Objekte sind und Host-Objekte die schlimmste Gruppe sind. Gemäß der Spezifikation (ECMA-262 3rd. Ed) dürfen Host-Objekte Dinge tun, von denen keine anderen Objekte träumen können. So zitieren Sie den relevanten Abschnitt [ 8.6.2 ] :

Hostobjekte können diese internen Methoden mit jedem implementierungsabhängigen Verhalten implementieren, oder es kann sein, dass ein Hostobjekt nur einige interne Methoden und keine anderen implementiert.

Die internen Methodenspezifikationen lauten [[Get]], [[Put]], [[Delete]] usw. Beachten Sie, dass das Verhalten interner Methoden von der Implementierung abhängt . Dies bedeutet, dass es absolut normal ist, dass das Host-Objekt beim Aufruf der Methode [[Get]] einen Fehler ausgibt. Und das ist leider nicht nur eine Theorie. In Internet Explorer können wir genau dies leicht beobachten - ein Beispiel für einen Fehler beim Auslösen eines Host-Objekts [[Get]]:

  document.createElement('p').offsetParent; // "Unspecified error."
  new ActiveXObject("MSXML2.XMLHTTP").send; // "Object doesn't support this property or method"

Das Erweitern von DOM-Objekten ähnelt dem Gehen in einem Minenfeld. Per Definition arbeiten Sie mit etwas, das sich unvorhersehbar und völlig unberechenbar verhalten darf. Und nicht nur Dinge können in die Luft jagen; Es besteht auch die Möglichkeit stiller Ausfälle, was ein noch schlimmeres Szenario darstellt. Ein Beispiel für erratisches Verhalten ist applet, objectund embedElemente, die in bestimmten Fällen Fehler auf Zuordnung von Eigenschaften zu werfen . Eine ähnliche Katastrophe passiert mit XML-Knoten:

  var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
  xmlDoc.loadXML('<foo>bar');
  xmlDoc.firstChild.foo = 'bar'; // "Object doesn't support this property or method"

Es gibt andere Fälle von Fehlern im IE , z. B. das document.styleSheets[99999]Auslösen von "Ungültiger Prozeduraufruf oder ungültiges Argument" oder das document.createElement('p').filtersAuslösen von "Mitglied nicht gefunden". Ausnahmen. Aber nicht nur MSHTML DOM ist das Problem. Der Versuch, die "Ziel" event-Eigenschaft eines Objekts in Mozilla zu überschreiben, löst TypeError aus und beklagt, dass die Eigenschaft nur einen Getter enthält (dh, sie ist schreibgeschützt und kann nicht festgelegt werden). Wenn Sie dasselbe in WebKit tun, führt dies zu einem unbeaufsichtigten Fehler, bei dem "Ziel" nach der Zuweisung weiterhin auf das ursprüngliche Objekt verweist.

Wenn Sie eine API für die Arbeit mit Ereignisobjekten erstellen, müssen Sie jetzt alle diese schreibgeschützten Eigenschaften berücksichtigen , anstatt sich auf präzise und beschreibende Namen zu konzentrieren.

Eine gute Faustregel ist es , Host-Objekte so weit wie möglich nicht zu berühren . Es ist kaum eine gute Idee, Architektur auf etwas zu stützen, das sich per Definition so sporadisch verhalten kann.

Möglichkeit von Kollisionen

API, die auf DOM-Elementerweiterungen basiert, ist schwer skalierbar . Es ist für Entwickler der Bibliothek schwer zu skalieren, wenn neue oder geänderte Kern-API-Methoden hinzugefügt werden, und für Bibliotheksbenutzer, wenn domänenspezifische Erweiterungen hinzugefügt werden. Die Wurzel des Problems ist eine wahrscheinliche Kollisionsgefahr . DOM-Implementierungen in gängigen Browsern haben normalerweise alle ordnungsgemäße APIs. Schlimmer ist, dass diese APIs nicht statisch sind, sondern sich ständig ändern, wenn neue Browserversionen herauskommen. Einige Teile sind veraltet. andere werden hinzugefügt oder geändert. Infolgedessen sind die Eigenschaften und Methoden von DOM-Objekten in gewisser Weise ein sich bewegendes Ziel.

In Anbetracht der großen Anzahl von Umgebungen, die heutzutage verwendet werden, ist es unmöglich festzustellen, ob bestimmte Objekte nicht bereits Teil eines DOM sind. Und wenn ja, kann es überschrieben werden? Oder wird es Fehler auslösen, wenn Sie versuchen, dies zu tun? Denken Sie daran, dass es sich um ein Host-Objekt handelt! Und wenn wir es leise überschreiben können, wie würde es sich auf andere Teile von DOM auswirken? Würde alles noch wie erwartet funktionieren? Wenn in einer Version eines solchen Browsers alles in Ordnung ist, gibt es eine Garantie, dass die nächste Version keine gleichnamige Eigenschaft einführt? Die Liste der Fragen geht weiter.

Einige Beispiele für proprietäre Erweiterungen, die Prototype brachen, sind Wrap-Eigenschaften für Textbereiche in IE (Kollision mit der Wrap-Methode Element # ) und selectMethoden für Formularsteuerelemente in Opera (Kollision mit der Select-Methode Element # ). Obwohl beide Fälle dokumentiert sind, ist es ärgerlich, sich an diese kleinen Ausnahmen erinnern zu müssen.

Proprietäre Erweiterungen sind nicht das einzige Problem. HTML5 bringt neue Methoden und Eigenschaften in die Tabelle. Und die meisten gängigen Browser haben bereits damit begonnen, sie zu implementieren. An einem gewissen Punkt WebForms definierte replaceEigenschaft auf Eingabeelemente, die Opera beschlossen, ihren Browser hinzuzufügen. Und wieder einmal hat es Prototype aufgrund eines Konflikts mit der Ersetzungsmethode "Element #" beschädigt .

Aber warte, es gibt noch mehr!

Aufgrund der langjährigen Tradition von DOM Level 0 gibt es diese "bequeme" Möglichkeit, auf Formularsteuerelemente zuzugreifen, die aus Formularelementen bestehen , und zwar einfach anhand ihres Namens. Dies bedeutet, dass Sie anstelle der Standardauflistung wie folgt elementsauf die Formularsteuerung zugreifen können:

  <form action="">
    <input name="foo">
  </form>
  ...
  <script type="text/javascript">
    document.forms[0].foo; // non-standard access
    // compare to
    document.forms[0].elements.foo; // standard access
  </script>

Angenommen, Sie erweitern Formularelemente mit einer loginMethode, die beispielsweise die Validierung überprüft und das Anmeldeformular sendet. Wenn Sie auch zufällig eine Formularsteuerung mit "Anmeldename" haben (was ziemlich wahrscheinlich ist, wenn Sie mich fragen), ist das, was als nächstes passiert, nicht hübsch:

  <form action="">
    <input name="login">
    ...
  </form>
  ...
  <script type="text/javascript">
    HTMLFormElement.prototype.login = function(){
      return 'logging in';
    };
    ...
    $(myForm).login(); // boom!
    // $(myForm).login references input element, not `login` method
  </script>

Jedes benannte Formularsteuerelement beschattet Eigenschaften, die über die Prototypenkette vererbt wurden . Die Wahrscheinlichkeit von Kollisionen und unerwarteten Fehlern bei Formularelementen ist noch höher.

Ähnlich verhält es sich mit benannten formElementen, auf diedocument über ihren Namen direkt zugegriffen werden kann :

  <form name="foo">
    ...
  </form>
  ...
  <script type="text/javascript">
    document.foo; // [object HTMLFormElement]
  </script>

Beim Erweitern von Dokumentobjekten besteht jetzt ein zusätzliches Risiko, dass Formularnamen mit Erweiterungen in Konflikt stehen. Und was ist, wenn ein Skript in Legacy-Anwendungen mit viel rostigem HTML ausgeführt wird, bei denen das Ändern / Entfernen solcher Namen keine triviale Aufgabe ist?

Der Einsatz einer Präfixierungsstrategie kann das Problem lindern. Wird aber wohl auch extra Lärm bringen.

Das Ändern von Objekten, die Sie nicht besitzen, ist ein ultimatives Rezept, um Kollisionen zu vermeiden. Das Brechen dieser Regel hat Prototype bereits in Schwierigkeiten gebracht, als es mit einer eigenen, benutzerdefinierten Implementierung überschriebendocument.getElementsByClassName wurde. Dem zu folgen bedeutet auch, mit anderen Skripten zu spielen, die in derselben Umgebung ausgeführt werden - egal, ob sie DOM-Objekte ändern oder nicht.

Leistungsaufwand

Wie wir bereits gesehen haben, benötigen Browser, die keine Elementerweiterungen unterstützen (z. B. IE 6, 7, Safari 2.x usw.), eine manuelle Objekterweiterung. Das Problem ist, dass die manuelle Erweiterung langsam, unpraktisch und nicht skalierbar ist. Es ist langsam, weil das Objekt mit einer großen Anzahl von Methoden / Eigenschaften erweitert werden muss. Und ironischerweise sind diese Browser die langsamsten, die es gibt. Dies ist unpraktisch, da das Objekt erst erweitert werden muss, um bearbeitet zu werden. Stattdessen document.createElement('p').hide()müssten Sie also so etwas tun $(document.createElement('p')).hide(). Dies ist im Übrigen einer der häufigsten Hindernisse für Anfänger von Prototype. Schließlich lässt sich die manuelle Erweiterung nicht gut skalieren, da sich das Hinzufügen von API-Methoden nahezu linear auf die Leistung auswirkt. Wenn es 100 Methoden gibtElement.prototypeEs müssen 100 Zuordnungen zu einem Element vorgenommen werden. Wenn es 200 Methoden gibt, müssen 200 Zuweisungen für ein Element vorgenommen werden, und so weiter.

Ein weiterer Leistungstreffer betrifft Ereignisobjekte. Prototype verfolgt einen ähnlichen Ansatz bei Ereignissen und erweitert sie mit einer Reihe von Methoden. Leider können einige Ereignisse in Browsern - Mausbewegung, Mouseover, Mouseout, Größenänderung, um nur einige zu nennen - buchstäblich Dutzende Male pro Sekunde ausgelöst werden. Die Erweiterung jedes einzelnen von ihnen ist ein unglaublich teurer Prozess. Und wofür? Nur um aufzurufen, was eine einzelne Methode für ein Ereignisobjekt sein könnte?

Schließlich, sobald Sie ausdehnenden Elemente beginnen, Bibliothek API am meisten braucht wahrscheinlich zurückkehren erweitert überall Elemente . Infolgedessen können $$Abfragemethoden wie jedes einzelne Element in einer Abfrage erweitern. Es ist leicht vorstellbar, dass die Leistung über einen solchen Prozess hinausgeht, wenn wir über Hunderte oder Tausende von Elementen sprechen.

IE DOM ist ein Durcheinander

Wie im vorherigen Abschnitt gezeigt, ist die manuelle DOM-Erweiterung ein Durcheinander. Die manuelle DOM-Erweiterung im IE ist jedoch noch schlimmer .

Wir alle wissen, dass im Internet Explorer Zirkelverweise zwischen Host- und nativen Objekten lecken und am besten vermieden werden. Das Hinzufügen von Methoden zu DOM-Elementen ist jedoch ein erster Schritt zur Erstellung solcher Zirkelverweise. Und da ältere IE-Versionen keine „Objektprototypen“ verfügbar machen, müssen Elemente nur direkt erweitert werden. Zirkelverweise und Undichtigkeiten sind fast unvermeidlich. Tatsächlich litt Prototype die meiste Zeit seines Lebens darunter.

Ein weiteres Problem ist die Art und Weise, wie IE DOM Eigenschaften und Attribute einander zuordnet. Die Tatsache, dass sich Attribute im selben Namespace wie Eigenschaften befinden, erhöht die Wahrscheinlichkeit von Kollisionen und allerlei unerwarteten Inkonsistenzen. Was passiert, wenn das Element ein benutzerdefiniertes Attribut "show" hat und dann um Prototype erweitert wird? Sie werden überrascht sein, aber die Anzeige "Attribut" würde von der Prototype- Element#showMethode überschrieben . extendedElement.getAttribute('show')würde einen Verweis auf eine Funktion zurückgeben, nicht den Wert des Attributs "show". In ähnlicher Weise extendedElement.hasAttribute('hide')würde man "true" sagen, auch wenn es für ein Element nie ein benutzerdefiniertes "hide" -Attribut gab. Beachten Sie, dass IE <8 fehlt hasAttribute, aber wir können noch Attribut / Eigenschaft Konflikt sehen: typeof extendedElement.attributes['show'] != "undefined".

Schließlich ist einer der weniger bekannten Nachteile die Tatsache, dass das Hinzufügen von Eigenschaften zu DOM-Elementen einen Rückfluss im IE verursacht, so dass die bloße Erweiterung eines Elements zu einem recht teuren Vorgang wird. Dies ist angesichts der unzureichenden Zuordnung von Attributen und Eigenschaften in seinem DOM tatsächlich sinnvoll.

Bonus: Browser-Fehler

Wenn alles, was wir bisher hinter uns haben, nicht ausreicht (in diesem Fall sind Sie wahrscheinlich ein Masochist), haben wir noch ein paar Bugs, um das Ganze zu übertreffen.

In einigen Versionen von Safari 3.x gibt es einen Fehler, durch den beim Navigieren zu einer vorherigen Seite über die Schaltfläche "Zurück" alle Hostobjekterweiterungen gelöscht werden . Leider ist der Fehler nicht erkennbar. Um das Problem zu umgehen, muss Prototype etwas Schreckliches tun. Es durchsucht den Browser nach dieser Version von WebKit und deaktiviert bfcache explizit, indem es den Ereignis-Listener "unload" anhängt window. Deaktivierter bfcache bedeutet, dass der Browser beim Navigieren über die Schaltflächen "Zurück" / "Vor" die Seite erneut abrufen muss , anstatt die Seite aus dem zwischengespeicherten Zustand wiederherzustellen.

Ein weiterer Fehler ist mit HTMLObjectElement.prototypeund HTMLAppletElement.prototypein IE8, und die Art und Weise, wie Objekt- und Applet-Elemente nicht von diesen Prototypobjekten erben . Sie können einer Eigenschaft von zuweisen HTMLObjectElement.prototype, diese Eigenschaft wird jedoch niemals für ein Objektelement „aufgelöst“. Das Gleiche gilt für Applets. Infolgedessen müssen diese Elemente immer manuell erweitert werden, was einen weiteren Mehraufwand darstellt.

IE8 stellt im Vergleich zu anderen gängigen Implementierungen nur eine Teilmenge der Prototypobjekte zur Verfügung. Zum Beispiel gibt es HTMLParagraphElement.prototype(sowie andere typspezifische) und Element.prototype, aber keine HTMLElement(und so HTMLElement.prototype) oder Node(und so Node.prototype). Element.prototypeim IE8 erbt auch nicht von Object.prototype. Dies sind per se keine Bugs, sollten aber dennoch beachtet werden: Es ist nichts Gutes, zu versuchen, nicht existierende zu erweitern Node.

Wrapper zur Rettung

Eine der häufigsten Alternativen zu diesem ganzen Durcheinander von DOM-Erweiterungen sind Objekt-Wrapper . Dies ist der Ansatz, den jQuery von Anfang an eingeschlagen hat, und einige andere Bibliotheken folgten später. Die Idee ist einfach. Erstellen Sie einen Wrapper, anstatt Elemente oder Ereignisse direkt zu erweitern, und delegieren Sie die Methoden entsprechend. Keine Kollisionen, keine Notwendigkeit, mit dem Wahnsinn von Host-Objekten umzugehen, einfacher Leckagen zu verwalten und in einem dysfunktionalen MSHTML-DOM zu arbeiten, bessere Leistung, einfachere Wartung und schmerzlose Skalierung.

Und Sie vermeiden immer noch den prozeduralen Ansatz.

Prototyp 2.0

Die gute Nachricht ist, dass Prototypfehler in der nächsten Hauptversion der Bibliothek behoben werden . Aus meiner Sicht verstehen alle Kernentwickler die oben genannten Probleme, und dieser Wrapper-Ansatz ist der vernünftigere Weg, um voranzukommen. Ich bin mir nicht sicher, wie die Pläne in anderen DOM-Bibliotheken wie Mootools aussehen. Soweit ich sehen kann, verwenden sie bereits Wrapper mit Ereignissen, erweitern jedoch weiterhin Elemente. Ich hoffe mit Sicherheit, dass sie sich in naher Zukunft von diesem Wahnsinn entfernen.

Kontrollierte Umgebungen

Bisher haben wir die DOM-Erweiterung aus der Sicht der browserübergreifenden Skriptbibliothek betrachtet . In diesem Zusammenhang ist klar, wie schwierig diese Idee wirklich ist. Aber was ist mit kontrollierten Umgebungen? Wenn das Skript nur in einer oder zwei Umgebungen ausgeführt wird, z. B. in Umgebungen, die auf Gecko, WebKit oder einem anderen modernen Nicht-MSHTML-DOM basieren. Vielleicht ist es eine Intranet-Anwendung, auf die über bestimmte Browser zugegriffen wird. Oder eine WebKit-basierte Desktop-App.

In diesem Fall ist die Situation definitiv besser . Schauen wir uns die oben aufgeführten Punkte an.

Fehlende Spezifikationen spielen keine Rolle mehr , da Sie sich keine Gedanken über die Kompatibilität mit anderen Plattformen oder zukünftigen Editionen machen müssen. In den meisten Nicht-MSHTML-DOM-Umgebungen werden DOM-Objektprototypen für eine Weile verfügbar gemacht, und es ist unwahrscheinlich, dass sie in naher Zukunft gelöscht werden. Es besteht jedoch immer noch die Möglichkeit einer Änderung.

Ein Hinweis auf die Unzuverlässigkeit von Hostobjekten verliert ebenfalls an Gewicht , da DOM-Objekte in Gecko- oder WebKit-basierten DOMs sehr viel sicherer sind als solche in MSHTML DOM. Es gibt aber noch Host-Objekte, die mit Sorgfalt behandelt werden sollten. Außerdem wurden zuvor nur schreibgeschützte Eigenschaften behandelt, die die Flexibilität der API leicht beeinträchtigen könnten.

Der Punkt über Kollisionen hat immer noch Gewicht . Diese Umgebungen unterstützen nicht standardmäßige Formularsteuerungszugriffe, verfügen über eine proprietäre API und implementieren ständig neue HTML5-Funktionen. Das Ändern von Objekten, die Sie nicht besitzen, ist immer noch eine schlechte Idee und kann zu schwer zu findenden Fehlern und Inkonsistenzen führen.

Ein Performance-Overhead ist praktisch nicht vorhanden , da diese DOM prototypbasierte DOM-Erweiterungen unterstützen. Die Leistung kann im Vergleich zum Wrapper-Ansatz sogar noch besser sein, da keine zusätzlichen Objekte erstellt werden müssen, um Methoden (oder Zugriffseigenschaften) von DOM-Objekten aufzurufen.

Die Erweiterung des DOM in einer kontrollierten Umgebung scheint eine absolut gesunde Sache zu sein. Obwohl das Hauptproblem darin besteht, dass bei Kollisionen immer noch Wrapper empfohlen werden . Dies ist eine sicherere Methode, um voranzukommen, und erspart Ihnen in Zukunft den Wartungsaufwand.

Nachwort

Hoffentlich können Sie jetzt die ganze Wahrheit hinter einer eleganten Herangehensweise klar erkennen. Wenn Sie das nächste Mal ein Javascript-Framework entwerfen, lehnen Sie DOM-Erweiterungen einfach ab . Sagen Sie "Nein" und ersparen Sie sich die Mühe, eine umständliche API zu warten und unnötigen Leistungsaufwand zu erleiden. Wenn Sie andererseits überlegen, eine Javascript-Bibliothek zu verwenden, die DOM erweitert, halten Sie eine Sekunde inne und fragen Sie sich, ob Sie bereit sind, ein Risiko einzugehen. Lohnt sich die Mühe, die DOM-Erweiterung zu bieten, wirklich?

Add Comment

* Required information
1000
Drag & drop images (max 1)
Powered by Commentics

Comments

No comments yet. Be the first!

Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0