Erstellen Sie Ihre erste JavaScript-Bibliothek

Schon immer die Magie der Mootools bestaunt? Haben Sie sich jemals gefragt, wie Dojo das macht? Schon mal neugierig auf die Gymnastik von jQuery gewesen? In diesem Tutorial werden wir uns hinter die Kulissen schleichen und versuchen, eine sehr einfache Version Ihrer Lieblingsbibliothek zu erstellen.

Wir verwenden fast täglich JavaScript-Bibliotheken. Wenn Sie gerade erst anfangen, ist es so fantastisch, so etwas wie jQuery zu haben, hauptsächlich wegen des DOM. Erstens kann das DOM für einen Anfänger ziemlich hart sein; Es ist eine ziemlich schlechte Entschuldigung für eine API. Zweitens ist es nicht einmal in allen Browsern konsistent.

Wir wickeln die Elemente in ein Objekt ein, weil wir Methoden für das Objekt erstellen möchten.

In diesem Lernprogramm versuchen wir, eine dieser Bibliotheken von Grund auf neu zu bauen. Ja, es wird Spaß machen, aber bevor Sie zu aufgeregt sind, lassen Sie mich ein paar Punkte klarstellen:

  • Dies ist keine vollständig ausgestattete Bibliothek. Oh, wir haben ein solides Set an Methoden zum Schreiben, aber es ist kein jQuery. Wir werden genug tun, um Ihnen ein gutes Gefühl für die Art von Problemen zu geben, auf die Sie beim Erstellen von Bibliotheken stoßen werden.
  • Wir setzen hier nicht generell auf vollständige Browserkompatibilität. Was wir heute schreiben, sollte auf Internet Explorer 8+, Firefox 5+, Opera 10+, Chrome und Safari funktionieren.
  • Wir werden nicht jede mögliche Nutzung unserer Bibliothek abdecken. Zum Beispiel funktionieren unsere Append- und Prepend-Methoden nur, wenn Sie ihnen eine Instanz unserer Bibliothek übergeben. Sie arbeiten nicht mit rohen DOM-Knoten oder Knotenlisten.

Eine weitere Sache: Während wir keine Tests für diese Bibliothek schreiben werden, habe ich das getan, als ich sie zuerst entwickelte. Sie können die Bibliothek und Tests auf Github erhalten.


Wie Sie sehen, nennen wir unsere Bibliothek Dome, weil es sich in erster Linie um eine DOM-Bibliothek handelt. Ja, es ist lahm

Wir haben hier einiges zu tun. Erstens haben wir eine Funktion; es wird schließlich eine Konstruktorfunktion für die Instanzen unserer Bibliothek sein; Diese Objekte werden die ausgewählten oder erstellten Elemente umschließen.

Dann haben wir unser dome objekt, das unser eigentliches Bibliotheksobjekt ist; wie Sie sehen, wird es am Ende dort zurückgegeben. Es gibt eine leere Get-Funktion, mit der wir Elemente auf der Seite auswählen können. Lasst uns das jetzt ausfüllen.


Die Funktion dome.get nimmt einen Parameter in Anspruch, kann jedoch eine Reihe von Dingen sein. Wenn es sich um eine Zeichenfolge handelt, nehmen wir an, dass es sich um eine CSS-Auswahl handelt. Wir können aber auch einen einzelnen DOM-Knoten oder eine Knotenliste verwenden.

Wir verwenden document.querySelectorAll, um das Auffinden von Elementen zu vereinfachen: Natürlich beschränkt dies die Browser-Unterstützung, aber für diesen Fall ist das in Ordnung. Wenn der Selektor keine Zeichenfolge ist, prüfen wir, ob eine Längeseigenschaft vorhanden ist. Wenn es existiert, wissen wir, dass wir eine NodeList haben. Andernfalls haben wir ein einzelnes Element und fügen dieses in ein Array ein. Das liegt daran, dass wir ein Array benötigen, um zu unserem Anruf nach Dome unten weiterzuleiten. Wie Sie sehen, senden wir ein neues Dome-Objekt zurück. Gehen wir also zu dieser leeren Dome-Funktion zurück und füllen Sie sie aus.


Hier ist diese Dome-Funktion:

Ich empfehle Ihnen wirklich, einige Ihrer Lieblingsbibliotheken zu durchsuchen.

Das ist ganz einfach: Wir durchlaufen einfach die ausgewählten Elemente und kleben sie mit numerischen Indizes auf das neue Objekt. Dann fügen wir eine length-Eigenschaft hinzu.

Aber was ist der Punkt hier? Warum nicht einfach die Elemente zurückgeben? Wir wickeln die Elemente in ein Objekt ein, weil wir Methoden für das Objekt erstellen können. Dies sind die Methoden, mit denen wir mit diesen Elementen interagieren können. Dies ist in der Tat eine verkürzte Version von jQuery.

Jetzt, da unser Dome-Objekt zurückgegeben wird, fügen wir dem Prototyp einige Methoden hinzu. Ich werde diese Methoden direkt unter die Dome-Funktion stellen.


Die ersten Funktionen, die wir schreiben werden, sind einfache Hilfsfunktionen. Da unsere Dome-Objekte mehr als ein DOM-Element einfassen könnten, müssen wir jedes Element in nahezu jeder Methode durchlaufen. Diese Dienstprogramme sind also praktisch.

Beginnen wir mit einer Kartenfunktion:

Natürlich benötigt die Map-Funktion einen einzigen Parameter, eine Callback-Funktion. Wir werden die Elemente im Array durchlaufen und sammeln, was vom Rückruf im Ergebnis-Array zurückgegeben wird. Beachten Sie, wie wir diese Rückruffunktion aufrufen:

Auf diese Weise wird die Funktion im Kontext unserer Dome-Instanz aufgerufen und erhält zwei Parameter: das aktuelle Element und die Indexnummer.

We also want a forEach function. This is actually really simple:

Der einzige Unterschied zwischen map und forEach besteht darin, dass map etwas zurückgeben muss. Wir können unseren Callback einfach an this.map übergeben und das zurückgegebene Array ignorieren. Stattdessen geben wir this zurück, um unsere Bibliothek verketten zu können. Wir werden ziemlich oft für jeden forEach machen. Beachten Sie also, dass wir bei Rückgabe unseres this.forEach-Aufrufs von einer Funktion dies tatsächlich zurückgeben. this Methoden geben beispielsweise dasselbe zurück: Diese Methoden geben beispielsweise dasselbe zurück:

Noch eine: mapOne. Es ist leicht zu sehen, was diese Funktion bewirkt, aber die eigentliche Frage ist, warum brauchen wir sie? Dies erfordert etwas von dem, was Sie als "Bibliotheksphilosophie" bezeichnen könnten.

Erstens kann das DOM für einen Anfänger ziemlich hart sein; Es ist eine ziemlich schlechte Entschuldigung für eine API.

Wenn Sie beim Erstellen einer Bibliothek nur den Code schreiben würden, wäre dies nicht zu schwierig. Als ich an diesem Projekt arbeitete, stellte ich fest, dass es schwieriger war zu entscheiden, wie bestimmte Methoden funktionieren sollten.

In Kürze werden wir eine Textmethode erstellen, die den Text unserer ausgewählten Elemente zurückgibt. Wenn unser Dome-Objekt mehrere DOM-Knoten umschließt (z. B. dome.get ("li")), was soll dies zurückgeben? Wenn Sie in jQuery ($("li").text()) etwas Ähnliches ausführen, erhalten Sie eine einzelne Zeichenfolge mit dem Text aller Elemente, die miteinander verkettet sind. Ist das nützlich? Ich glaube nicht, aber ich bin mir nicht sicher, was eine bessere Rendite wäre.

Für dieses Projekt gebe ich den Text mehrerer Elemente als Array zurück, es sei denn, es gibt nur ein Element im Array. dann geben wir nur die Textzeichenfolge zurück, kein Array mit einem einzelnen Element. Ich denke, Sie erhalten meistens den Text eines einzelnen Elements. Daher optimieren wir für diesen Fall. Wenn Sie jedoch den Text mehrerer Elemente erhalten, geben wir etwas zurück, mit dem Sie arbeiten können.

Advertisement

Daher führt die mapOne-Methode einfach map aus und gibt dann entweder das Array oder das einzelne Element im Array zurück. Wenn Sie sich immer noch nicht sicher sind, wie das nützlich ist, bleiben Sie dabei: Sie werden sehen!


Als Nächstes fügen wir diese Textmethode hinzu. Genau wie bei jQuery können wir ihm eine Zeichenkette übergeben und den Text des Elements festlegen oder keine Parameter verwenden, um den Text zurückzuholen.

Wie Sie vielleicht erwarten, müssen wir nach einem Wert im Text suchen, um zu sehen, ob wir Einstellungen vornehmen oder abrufen. Nur wenn if (Text) nicht funktionieren würde, da eine leere Zeichenfolge ein falscher Wert ist.

Wenn wir Einstellungen vornehmen, führen wir eine forEach über den Elementen aus und setzen ihre innerText-Eigenschaft auf den Text. Wenn wir bekommen, geben wir die innerText-Eigenschaft der Elemente zurück. Beachten Sie unsere Verwendung der mapOne-Methode: Wenn wir mit mehreren Elementen arbeiten, wird ein Array zurückgegeben. Andernfalls ist es nur die Zeichenfolge.

Die html-Methode verhält sich fast genauso wie text, außer dass sie die innerHTML-Eigenschaft anstelle von innerText verwendet.

Wie gesagt: fast identisch.


Als nächstes möchten wir Klassen hinzufügen und entfernen können. Schreiben wir also die addClass- und removeClass-Methoden.

Unsere addClass-Methode nimmt entweder einen String oder ein Array von Klassennamen an. Damit dies funktioniert, müssen wir den Typ dieses Parameters überprüfen. Wenn es sich um ein Array handelt, führen wir eine Schleife aus und erstellen eine Zeichenfolge mit Klassennamen. Andernfalls fügen wir nur ein einzelnes Leerzeichen vor dem Klassennamen hinzu, sodass die vorhandenen Klassen im Element nicht beeinträchtigt werden. Dann werden die Elemente einfach durchlaufen und die neuen Klassen an die className-Eigenschaft angehängt.

Ziemlich unkompliziert, wie?

Nun, was ist mit dem Entfernen von Klassen? Um es einfach zu halten, können wir nur jeweils eine Klasse entfernen.

Bei jedem Element teilen wir el.className in ein Array auf. Dann verwenden wir eine while-Schleife, um die fehlerhafte Klasse auszuschneiden, bis cs.indexOf (clazz) -1 zurückgibt. Wir machen dies, um den Randfall abzudecken, bei dem dieselben Klassen mehr als einmal zu einem Element hinzugefügt wurden: Wir müssen sicherstellen, dass es wirklich verschwunden ist. Sobald wir sicher sind, dass wir jede Instanz der Klasse herausgeschnitten haben, verbinden wir das Array mit Leerzeichen und setzen es auf el.className.


Der schlechteste Browser, mit dem wir umgehen, ist IE8. In unserer kleinen Bibliothek gibt es nur einen IE-Fehler, mit dem wir umgehen müssen. Zum Glück ist es ziemlich einfach. IE8 unterstützt die Array-Methode indexOf nicht. Wir verwenden es in removeClass, also lassen wir es polyfill werden:

Es ist ziemlich einfach und es ist keine vollständige Implementierung (unterstützt den zweiten Parameter nicht), aber es funktioniert für unsere Zwecke.


Nun wollen wir eine Attr-Funktion. Das ist einfach, denn es ist praktisch identisch mit unseren Text- oder HTML-Methoden. Genau wie diese Methoden können wir sowohl Attribute abrufen als auch setzen: Wir müssen einen Attributnamen und -wert festlegen, und nur einen Attributnamen, der abgerufen werden soll.

Wenn der val einen Wert hat, durchlaufen wir die Elemente und setzen das ausgewählte Attribut mit diesem Wert. Verwenden Sie dazu die Methode setAttribute des Elements. Andernfalls verwenden wir mapOne, um dieses Attribut über die getAttribute-Methode zurückzugeben.


Wir sollten in der Lage sein, neue Elemente zu erstellen, wie jede gute Bibliothek es kann. Natürlich wäre dies als Methode für eine Dome-Instanz nicht geeignet. Setzen wir sie also direkt auf unser Dome-Objekt.

Wie Sie sehen, haben wir zwei Parameter: den Namen des Elements und ein Objekt mit Attributen. Die meisten Attribute können mit unserer attr-Methode angewendet werden, aber zwei werden gesondert behandelt. Wir verwenden die addClass-Methode für die className-Eigenschaft und die Textmethode für die Texteigenschaft. Natürlich müssen wir zuerst das Element und das Dome-Objekt erstellen. Hier ist alles in Aktion:

Wie Sie sehen, erstellen wir das Element und senden es direkt in ein neues Dome-Objekt. Dann beschäftigen wir uns mit den Attributen. Beachten Sie, dass wir die className- und Textattribute löschen müssen, nachdem wir mit ihnen gearbeitet haben. Dies verhindert, dass sie als Attribute angewendet werden, wenn wir den Rest der Schlüssel in attrs durchlaufen. Natürlich beenden wir mit der Rückgabe des neuen Dome-Objekts.

Aber jetzt, da wir neue Elemente erstellen, werden wir sie in das DOM einfügen wollen, oder?


Als Nächstes schreiben wir Append- und Prepend-Methoden. Nun, das sind eigentlich etwas knifflige Funktionen, hauptsächlich wegen der vielen Anwendungsfälle. Folgendes möchten wir tun können:

Der schlechteste Browser, mit dem wir umgehen, ist IE8.

Die Anwendungsfälle sind wie folgt: Wir möchten vielleicht anhängen oder voranstellen

  • ein neues Element zu einem oder mehreren vorhandenen Elementen.
  • mehrere neue Elemente zu einem oder mehreren vorhandenen Elementen.
  • ein vorhandenes Element zu einem oder mehreren vorhandenen Elementen.
  • mehrere vorhandene Elemente zu einem oder mehreren vorhandenen Elementen.

Hinweis: Ich verwende "neu" für Elemente, die noch nicht im DOM enthalten sind. vorhandene Elemente befinden sich bereits im DOM.

Lass uns jetzt durchgehen:

Wir erwarten, dass der Parameter els ein Dome-Objekt ist. Eine vollständige DOM-Bibliothek würde dies als Knoten oder Knotenliste akzeptieren, aber das tun wir nicht. Wir müssen jedes Element überlaufen und dann innerhalb jedes Elementes, das wir anfügen möchten.

Wenn wir die els an mehr als einem Element anhängen, müssen wir sie klonen. Wir möchten die Knoten jedoch nicht beim ersten Anhängen klonen, sondern nur zu einem späteren Zeitpunkt. Also machen wir das:

Das i stammt aus der äußeren forEach-Schleife: Es ist der Index des aktuellen übergeordneten Elements. Wenn wir nicht an das erste übergeordnete Element anhängen, klonen wir den Knoten. Auf diese Weise geht der eigentliche Knoten in den ersten übergeordneten Knoten und jeder andere übergeordnete Knoten erhält eine Kopie. Dies funktioniert gut, da das Dome-Objekt, das als Argument übergeben wurde, nur die ursprünglichen (nicht geklonten) Knoten hat. Wenn wir also nur ein einzelnes Element an ein einzelnes Element anhängen, sind alle beteiligten Knoten Teil ihrer jeweiligen Dome-Objekte.

Zum Schluss fügen wir das Element tatsächlich an:

Insgesamt haben wir also folgendes:

Wir möchten die gleichen Fälle für die Methode prepend behandeln, daher ist die Methode sehr ähnlich:

Wenn Sie ein vorangestelltes Element voranstellen, ändern Sie das Element in umgekehrter Reihenfolge. Da wir nicht rückwärts gehen können, gehe ich mit einer for-Schleife rückwärts durch die Schleife. Wir klonen den Knoten erneut, wenn dies nicht das erste übergeordnete übergeordnete Element ist, an das wir anhängen.


Für unsere letzte Knotenmanipulationsmethode möchten wir Knoten aus dem DOM entfernen können. Ganz einfach:

Durchlaufen Sie einfach die Knoten und rufen Sie die removeChild-Methode für den ParentNode jedes Elements auf. Die Schönheit hier (alles dank des DOM) ist, dass dieses Dome-Objekt noch gut funktioniert; Wir können jede Methode verwenden, die wir möchten, einschließlich dem Anhängen oder Voranfügen an das DOM. Schön, wie?


Zu guter Letzt werden wir einige Funktionen für Event-Handler schreiben.

Wie Sie wahrscheinlich wissen, verwendet der IE8 die alten IE-Ereignisse, daher müssen wir dies überprüfen. Außerdem werden wir die DOM 0-Ereignisse einspielen, nur weil wir es können.

Schauen Sie sich die Methode an und wir besprechen sie dann:

Hier haben wir eine IIFE, in deren Inneren wir die Funktionsprüfung durchführen. Wenn document.addEventListener vorhanden ist, verwenden wir das. Andernfalls prüfen wir nach document.attachEvent oder greifen auf DOM 0-Ereignisse zurück. Beachten Sie, wie wir die endgültige Funktion aus dem IIFE zurückgeben: Dies wird am Ende Dome.prototype.on zugewiesen. Bei der Funktionserkennung ist es sehr praktisch, die entsprechende Funktion so zuweisen zu können, anstatt bei jeder Ausführung der Funktion nach den Funktionen zu suchen.

Die off-Funktion, die die Ereignishandler aushängt, ist ziemlich identisch:


Ich hoffe, Sie probieren unsere kleine Bibliothek aus und erweitern sie vielleicht sogar ein wenig. Fühlen Sie sich frei, um es zu spalten, herumzuspielen und eine Pull-Anfrage zu senden.

Lassen Sie mich noch einmal klarstellen: Der Zweck dieses Tutorials besteht nicht darin, dass Sie immer Ihre eigenen Bibliotheken schreiben sollten.

Es gibt engagierte Teams, die zusammenarbeiten, um die großen etablierten Bibliotheken so gut wie möglich zu machen. Der Punkt hier war, einen kleinen Einblick in das zu geben, was in einer Bibliothek vor sich gehen könnte; Ich hoffe, Sie haben hier ein paar Tipps bekommen.

Ich empfehle Ihnen wirklich, einige Ihrer Lieblingsbibliotheken zu durchsuchen. Sie werden feststellen, dass sie nicht so kryptisch sind, wie Sie vielleicht gedacht haben, und Sie werden wahrscheinlich viel lernen.