Detaillierte Erklärung des JS-Browser-Ereignismodells

Detaillierte Erklärung des JS-Browser-Ereignismodells

Was ist ein Ereignis

Ich denke, Sie haben vielleicht schon von ereignisgesteuert gehört, aber was genau ist ereignisgesteuert? Warum ist der Browser ereignisgesteuert?

Einfach ausgedrückt bedeutet ereignisgesteuert, dass alles in Ereignisse abstrahiert wird.

  • Ein Klick ist ein Ereignis
  • Tastendruck ist ein Ereignis
  • Eine erfolgreiche Netzwerkanfrage ist ein Ereignis
  • Das Laden der Seite ist ein Ereignis
  • Seitenfehler ist ein Ereignis

Der Browser verlässt sich auf Ereignisse, um die Ausführung der App zu steuern. Wenn es keine ereignisgesteuerte Ausführung gibt, wird die App direkt von Anfang bis Ende ausgeführt und dann beendet. Die ereignisgesteuerte Ausführung ist der Eckpfeiler des Browsers.

Ein einfaches Beispiel

Tatsächlich stellt die Ampel in Wirklichkeit eine Art Ereignis dar. Sie teilt uns mit, ob sie rot, grün oder gelb ist. Basierend auf diesem Ereignis müssen wir einige Vorgänge ausführen. Beispielsweise müssen wir auf rote und gelbe Ampeln warten und können die Straße überqueren, wenn die Ampel grün ist.

Schauen wir uns das einfachste Browserereignis an:

HTML Quelltext:

<button>Farbe ändern</button>

js-Code:

var btn = document.querySelector('Schaltfläche');

btn.onclick = Funktion() {
  console.log('Schaltfläche angeklickt')
}

Der Code ist sehr einfach. Wir registrieren ein Ereignis auf der Schaltfläche. Der Handler dieses Ereignisses ist eine anonyme Funktion, die wir definiert haben. Wenn der Benutzer auf die für das Ereignis registrierte Schaltfläche klickt, wird die von uns definierte anonyme Funktion ausgeführt.

So binden Sie Ereignisse

Wir haben drei Möglichkeiten, Ereignisse zu binden: Inline-Bindung, direkte Zuweisung und Verwendung von addEventListener.

Das Inlinen dieser Methode wird dringend davon abgeraten

HTML Quelltext:

<button onclick="handleClick()">Drück mich</button>

Schreiben Sie dann in den Skript-Tag:

Funktion handleClick() {
  console.log('Schaltfläche angeklickt')
}

Direkte Zuordnung

Genau wie das Beispiel, das ich oben gegeben habe:

var btn = document.querySelector('Schaltfläche');

btn.onclick = Funktion() {
  console.log('Schaltfläche angeklickt')
}

Dieser Ansatz hat zwei Nachteile

Sie können nicht mehrere Handler desselben Typs hinzufügen.

btn.onclick = FunktionA;
btn.onclick = FunktionB;

Auf diese Weise ist nur Funktion B gültig, was durch addEventListener gelöst werden kann.

Es ist nicht möglich, zu steuern, in welchem ​​Stadium die Ausführung stattfindet. Dies wird später beim Erfassen/Bubbling von Ereignissen besprochen. Dies kann auch durch addEventListener gelöst werden.

Daher entstand addEventListener, was auch die derzeit empfohlene Schreibweise ist.

Ereignislistener hinzufügen

Der dritte Parameter der alten Version von addEventListener ist bool, während der dritte Parameter der neuen Version object ist, was für eine spätere Erweiterung praktisch ist und mehr Funktionen bietet. Konzentrieren wir uns darauf.

addEventListener kann Ereignisse an Elemente, Dokumente, Fenster und sogar XMLHttpRequest binden. Wenn das angegebene Ereignis eintritt, wird die gebundene Rückruffunktion durch einen bestimmten Mechanismus ausgeführt, über den wir später sprechen werden.

Grammatik:

target.addEventListener(Typ, Listener[, Optionen]);
target.addEventListener(Typ, Listener[, useCapture]);
target.addEventListener(Typ, Listener[, useCapture, wantsUntrusted ]); // Nur Gecko/Mozilla

Typ ist der Ereignistyp, den Sie binden möchten. Übliche sind Klicken, Scrollen, Berühren, Mouseover usw. Der dritte Parameter der alten Version ist bool und gibt an, ob es sich um die Erfassungsphase handelt. Der Standardwert ist false, d. h. der Standardwert ist die Sprudelphase. Die neue Version ist ein Objekt, das Capture (gleiche Funktionalität wie oben), Passive und Once enthält. Once wird verwendet, um zu bestimmen, ob nur einmal ausgeführt werden soll. Wenn passive als true angegeben ist, wird preventDefault() nie ausgeführt, was wichtig ist, um einen reibungslosen Bildlaufeffekt zu erzielen. Weitere Informationen finden Sie unter Verbessern der Bildlaufleistung mit passiven Listenern.

Veranstaltungen im Rahmen

Tatsächlich verwenden wir heute in den meisten Fällen Frameworks zum Schreiben von Code, sodass die oben beschriebene Situation in der Realität sehr selten vorkommt. Wir sehen häufig Ereignisse, die von Frameworks gekapselt werden, wie z. B. synthetische Ereignisse von React. Wenn Sie interessiert sind, können Sie diese Artikel lesen.

  • Reagieren Sie auf SyntheticEvent
  • Was sind die Vorteile von Vue und React? Was ist der wesentliche Unterschied zwischen den beiden?

Obwohl wir selten mit nativen Ereignissen in Kontakt kommen, ist es dennoch notwendig, Ereignisobjekte, Ereignismechanismen, Ereignisagenten usw. zu verstehen, da das Ereignissystem des Frameworks zumindest in dieser Hinsicht konsistent ist, worüber wir als Nächstes sprechen werden.

Event-Objekt

Alle Funktionen zur Ereignisbehandlung werden von einem Ereignisobjekt begleitet, wenn sie vom Browser ausgeführt werden. Beispiel:

Funktion handleClick(e) {
  konsole.log(e);
}  

btn.addEventListener('klicken', handleClick);

Dieses e ist das Ereignisobjekt. Dieses Objekt hat einige sehr nützliche Eigenschaften und Methoden. Hier sind einige häufig verwendete Eigenschaften und Methoden.

Eigentum

  • Ziel
  • x, y und andere Positionsinformationen
  • Zeitstempel
  • Ereignisphase

Verfahren

  • preventDefault wird verwendet, um das Standardverhalten des Browsers zu verhindern, z. B. dass ein Tag standardmäßig springt, das Formular standardmäßig überprüft wird und eine Anforderung an die durch die Aktion angegebene Adresse sendet usw.
  • Mit „stopPropagation“ wird ein weiteres Aufsteigen des Ereignisses verhindert. Dies wird später bei der Besprechung der Ereignisausbreitung erwähnt.

Ereignisausbreitung

Wie bereits erwähnt, sind Ereignisse standardmäßig an die Bubbling-Phase gebunden. Wenn Sie useCapture explizit auf true setzen, werden sie an die Capture-Phase gebunden.

Die Ereigniserfassung ist sehr interessant, so sehr, dass ich oft Fragen zu Ereignissen stelle, einige Ereignisverbreitungsmechanismen hinzufüge und die Kandidaten auffordere, diese zu beantworten. Dies kann wirklich das Niveau einer Person widerspiegeln. Das Verständnis des Ausbreitungsmechanismus von Ereignissen ist bei einigen spezifischen Problemen sehr hilfreich.

Wenn ein an ein Element gebundenes Ereignis ausgelöst wird, durchläuft es tatsächlich drei Phasen.

Phase 1 - Erfassungsphase

Beginnend bei der äußersten Ebene, also dem HTML-Tag, wird geprüft, ob an das aktuelle Element ein entsprechendes Ereignis der Erfassungsphase gebunden ist. Wenn ja, wird es ausgeführt; wenn nicht, wird die Weiterleitung nach innen fortgesetzt. Dieser Prozess wird rekursiv ausgeführt, bis das Element erreicht ist, das das Ereignis auslöst.

Pseudocode:

Funktion erfassen(e, aktuellesElement) {
    wenn (currentElement.listners[e.type] !== void 0) {
        aktuellesElement.listners[e.Typ].fürJedes(fn => fn(e))
    }


    // weitergeben
    wenn (aktuellesElement !== e.target) {
        // getActiveChild wird verwendet, um den untergeordneten Knoten capture(e, getActiveChild(currentElement, e)) auf dem aktuellen Event-Propagation-Link abzurufen
    } anders {
        Blase(e, aktuellesElement)
    }
}

// Dieses Event-Objekt wird von der Engine „capture(new Event(), document.querySelector('html'))“ erstellt.

Zweite Phase – Zielphase

Dies wurde oben bereits erwähnt und wird hier weggelassen.

Die dritte Phase - Blasenbildung

Beginnend mit dem Element, das dieses Ereignis auslöst, wird geprüft, ob das aktuelle Element ein entsprechendes Ereignis der Bubbling-Phase gebunden hat. Wenn ja, führen Sie es aus; wenn nicht, geben Sie es weiter. Dieser Prozess wird rekursiv ausgeführt, bis das HTML erreicht ist.

Pseudocode:

Funktion Blase(e, aktuellesElement) {
    wenn (currentElement.listners[e.type] !== void 0) {
        aktuellesElement.listners[e.Typ].fürJedes(fn => fn(e))
    }
    // Rückkehr
    wenn (aktuellesElement !== document.querySelector('html')) {
        Blase(e, aktuellesElement.übergeordnet)
    }
}

Der obige Vorgang wird durch ein Diagramm dargestellt:

Wenn Sie nicht möchten, dass das Ereignis weiter sprudelt, können Sie die zuvor erwähnte StopPropagation verwenden.

Pseudocode:

Funktion Blase(e, aktuellesElement) {
    lass gestoppt = false;
    Funktion cb() {
        gestoppt = wahr;
    }
    wenn (currentElement.listners[e.type] !== void 0) {
        currentElement.listners[e.type].forEach(fn => {
            fn({
                ...e,
                stopPropagation: cb
            });
            wenn (gestoppt) zurück;
        })
    }
    // Rückkehr
    wenn (aktuellesElement !== document.querySelector('html')) {
        Blase(e, aktuellesElement.übergeordnet)
    }
}

Veranstaltungsdelegation

Mithilfe des oben erwähnten Event-Bubbling-Mechanismus können wir einige interessante Dinge tun. Zum Beispiel:

Wir haben eine Liste wie die folgende und möchten ausgeben, welches Element beim Klicken auf das entsprechende Listenelement angeklickt wurde.

HTML Quelltext:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

JS-Code:

document.querySelector('ul').addEventListener('klicken', e => console.log(e.target.innerHTML))

Online-Adresse, wie oben erwähnt, wird addEventListener standardmäßig an die Sprudelphase gebunden, sodass das Ereignis von der Zielphase aus startet und zur äußeren Schicht zur UL sprudelt, an die wir das Ereignis gebunden haben. In UL können wir über das Zielattribut des Ereignisobjekts herausfinden, welches Element es ausgelöst hat.

„Das Ereignis wird ab der Zielphase beginnen“ bedeutet nicht, dass das Ereignis keine Erfassungsphase hat, sondern dass wir die Erfassungsphase nicht gebunden haben, deshalb habe ich sie in der Beschreibung weggelassen.

Wir binden die Ereignisbehandlungsfunktion nur an das äußere UL, aber Sie können sehen, dass beim Klicken auf li tatsächlich der entsprechende li-Inhalt (1, 2, 3 oder 4) gedruckt wird. Wir müssen nicht an jedes li eine Ereignisbehandlungsfunktion binden, was nicht nur die Codemenge, sondern auch die Leistung verbessert.

Wir haben dieser interessanten Sache den schönen Namen „Event-Proxy“ gegeben. Diese Technik kommt im realen Geschäftsleben häufig zum Einsatz und ist auch bei Vorstellungsgesprächen ein häufig verwendeter Testpunkt.

Zusammenfassen

Ereignisse sind nicht browserspezifisch und haben nichts mit der Sprache JS zu tun, weshalb ich sie nicht in den Bereich JS eingeordnet habe. Ereignissysteme gibt es zwar vielerorts, die Ereignismodelle sind jedoch nicht einheitlich.

Worüber wir heute sprechen, ist das Ereignismodell des Browsers. Der Browser ist ereignisgesteuert und abstrahiert viele Dinge in Ereignisse, wie z. B. Benutzerinteraktion, Netzwerkanforderungen, Seitenladen, Fehler usw. Man kann sagen, dass Ereignisse der Eckpfeiler des normalen Betriebs des Browsers sind.

Die von uns verwendeten Frameworks kapseln und verarbeiten alle Ereignisse in unterschiedlichem Ausmaß. Neben dem Verständnis nativer Ereignisse und Prinzipien ist es manchmal notwendig, zu verstehen, wie das Framework selbst Ereignisse verarbeitet.

Wenn ein Ereignis eintritt, initialisiert der Browser ein Ereignisobjekt und gibt das Ereignisobjekt dann nach einer bestimmten Logik weiter. Diese Logik ist der Ereignisweitergabemechanismus. Wir haben erwähnt, dass die Ereignisausbreitung tatsächlich in drei Phasen unterteilt ist, nämlich in chronologischer Reihenfolge die Erfassungsphase, die Zielphase und die Blasenphase. Um den gewünschten Effekt zu erzielen, können Entwickler unterschiedliche Phasen überwachen.

Das Ereignisobjekt verfügt über viele Eigenschaften und Methoden, mit denen Sie die Ereignisverarbeitungsfunktion lesen und bedienen können, z. B. das Lesen der Koordinateninformationen des Klicks, das Verhindern von Blasenbildung usw.

Abschließend veranschaulichen wir anhand eines Beispiels, wie sich mithilfe des Bubbling-Mechanismus die Ereignisdelegation realisieren lässt.

Oben finden Sie eine ausführliche Erläuterung des JS-Browser-Ereignismodells. Weitere Informationen zum JS-Browser-Ereignismodell finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • So verwenden Sie natives JS zum Implementieren von Touch-Sliding-Überwachungsereignissen
  • Detaillierte Erläuterung von JavaScript-WebAPI-, DOM-, Ereignis- und Operationselementbeispielen
  • Detaillierte Analyse des Event-Bubbling-Mechanismus in JavaScript
  • Analyse des Ereignisschleifenmechanismus von js
  • Eine kurze Diskussion über ereignisgesteuerte Entwicklung in JS und Nodejs
  • Zusammenfassung der Ereignisbehandlung im Vue.js-Frontend-Framework
  • Detaillierte Erklärung zur Verwendung von Javascript zur Behandlung allgemeiner Ereignisse
  • Fallstudie zu JavaScript-Ereignisschleifen

<<:  Detaillierte Erläuterung der Funktionen und Verwendung allgemeiner MySQL-Speicher-Engines

>>:  Redission-Tomcat implementiert schnell die Bereitstellung von einer einzelnen Maschine zur Bereitstellung auf mehreren Maschinen

Artikel empfehlen

Reines CSS für eine coole Ladeanimation

Schauen wir uns an, welche Ladeanimationseffekte ...

Gründe und Methoden zum Warten auf die Sperre der Tabellenmetadaten in MySQL

Wenn MySQL DDL-Operationen wie „Alter Table“ ausf...

CSS-Code zur Steuerung der Hintergrundfarbe der Webseite

Ich glaube, jeder macht sich oft Sorgen, ob er Bi...

Tiefgreifendes Verständnis der MySQL-Selbstverbindung und Join-Assoziation

1. MySQL-Selbstverbindung MySQL muss beim Abfrage...

HTML löst das Problem ungültiger Tabellenbreiteneinstellungen

Wenn Sie den Stil „table-layer:fixed“ für eine Ta...

Detaillierte Erklärung zur Verwendung verschiedener MySQL-Indizes

1. Langsames Abfrageprotokoll 1.1 MySQL-Protokoll...

Detaillierte Erläuterung des verschachtelten Routings im Vue-Router

Inhaltsverzeichnis Schritt 1. Konfigurieren Sie R...

So erhalten Sie Datums-/Uhrzeitdaten in MySQL, gefolgt von .0

Der Datentyp von MySQL ist datetime. Die in der D...

Web-Standardanwendung: Neugestaltung der Tencent QQ-Homepage

Die Homepage von Tencent QQ wurde neu gestaltet un...

Tutorial zur Installation von VMware Workstation 14 Pro unter Ubuntu 16.04

In diesem Artikel wird die spezifische Methode zu...

So verwenden Sie Makros in JavaScript

In Sprachen werden häufig Makros zur Implementier...