Heim >Web-Frontend >js-Tutorial >Verstehen Sie den Event-Routing-Bubbling-Prozess und den Delegationsmechanismus in JavaScript
Wenn ich dies mit reinem CSS umsetze. Ich begann, JavaScript und Stilklassen zu verwenden, um die Funktionalität abzurunden.
Dann habe ich einige Ideen: Ich möchte Delegierte Ereignisse (Ereignisdelegation) verwenden, aber keine Abhängigkeiten haben und beliebige Bibliotheken, einschließlich jQuery, einbinden. Ich muss die Ereignisdelegation selbst implementieren.
Werfen wir zunächst einen Blick darauf, was Event-Delegation ist? Wie funktionieren sie und wie wird dieser Mechanismus implementiert?
Schauen wir uns zunächst ein einfaches Beispiel an.
Nehmen wir an, wir haben eine Reihe von Schaltflächen. Ich klicke jeweils auf eine Schaltfläche und möchte dann, dass der angeklickte Status auf „aktiv“ gesetzt wird. Bei erneutem Klick aktiv abbrechen.
Dann können wir etwas HTML schreiben:
<ul class="toolbar"> <li><button class="btn">Pencil</button></li> <li><button class="btn">Pen</button></li> <li><button class="btn">Eraser</button></li> </ul>
Ich kann einige Standard-Javascript-Ereignisse verwenden, um die obige Logik zu verarbeiten:
var buttons = document.querySelectorAll(".toolbar .btn"); for(var i = 0; i < buttons.length; i++) { var button = buttons[i]; button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }); }
Es sieht gut aus, aber Es funktioniert tatsächlich nicht so, wie Sie es erwarten.
Wenn Sie Erfahrung in der JavaScript-Entwicklung haben, wird dieses Problem offensichtlich sein.
Für Uneingeweihte ist die Schaltflächenvariable geschlossen und die entsprechende Schaltfläche wird jedes Mal gefunden ... aber tatsächlich gibt es hier nur eine Schaltfläche, die bei jeder Schleife neu zugewiesen wird.
Die erste Schleife zeigt auf die erste Schaltfläche, dann auf die zweite. Wenn Sie jedoch klicken, zeigt die Schaltflächenvariable immer auf das letzte Schaltflächenelement. Dies ist das Problem.
Was wir brauchen, ist ein stabiler Bereich.
var buttons = document.querySelectorAll(".toolbar button"); var createToolbarButtonHandler = function(button) { return function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }; }; for(var i = 0; i < buttons.length; i++) { buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i])); }
Hinweis* Die Struktur des obigen Codes ist etwas kompliziert. Sie können die aktuelle Schaltflächenvariable auch einfach mit einem Abschluss schließen und speichern, wie unten gezeigt:
var buttons = document.querySelectorAll(".toolbar .btn"); for(var i = 0; i < buttons.length; i++) { (function(button) { button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }); })(buttons[i]) }
Jetzt funktioniert es jetzt einwandfrei. Auf die richtige Schaltfläche zu zeigen ist immer
Diese Lösung sieht in Ordnung aus, aber wir können es tatsächlich besser machen.
Zuerst haben wir zu viele Verarbeitungsfunktionen erstellt. An jede passende .toolbar-Schaltfläche sind ein Ereignis-Listener und ein Callback-Handler gebunden. Wenn nur drei Schaltflächen vorhanden sind, kann diese Ressourcenzuweisung ignoriert werden.
Was aber, wenn wir 1.000 haben?
<ul class="toolbar"> <li><button id="button_0001">Foo</button></li> <li><button id="button_0002">Bar</button></li> // ... 997 more elements ... <li><button id="button_1000">baz</button></li> </ul>
Es wird nicht abstürzen, aber das ist nicht die beste Lösung. Wir weisen viele unnötige Funktionen zu. Lassen Sie es uns so umgestalten, dass es nur einmal angehängt wird und nur eine Funktion bindet, um potenziell Tausende von Aufrufen verarbeiten zu können.
Anstatt die Schaltflächenvariable zu schließen, um das Objekt zu speichern, auf das wir zu diesem Zeitpunkt geklickt haben, können wir das Ereignisobjekt verwenden, um das Objekt abzurufen, auf das wir zu diesem Zeitpunkt geklickt haben.
Das Ereignisobjekt verfügt über einige Metadaten. Bei mehreren Bindungen können wir currentTarget verwenden, um das aktuell gebundene Objekt abzurufen:
var buttons = document.querySelectorAll(".toolbar button"); var toolbarButtonHandler = function(e) { var button = e.currentTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }; for(var i = 0; i < buttons.length; i++) { button.addEventListener("click", toolbarButtonHandler); }
Nicht schlecht! Dies vereinfacht jedoch nur eine einzelne Funktion und macht sie besser lesbar, ist aber dennoch mehrfach gebunden.
Allerdings können wir es besser machen.
Nehmen wir an, dass wir dieser Liste dynamisch einige Schaltflächen hinzufügen. Anschließend fügen wir auch Ereignisbindungen für diese dynamischen Elemente hinzu und entfernen sie. Dann müssen wir die von diesen Verarbeitungsfunktionen verwendeten Variablen und den aktuellen Kontext beibehalten, was unzuverlässig klingt.
Vielleicht gibt es auch andere Möglichkeiten.
Lassen Sie uns zunächst vollständig verstehen, wie Ereignisse funktionieren und wie sie im DOM bereitgestellt werden.
Wenn der Benutzer auf ein Element klickt, wird ein Ereignis generiert, um den Benutzer über das aktuelle Verhalten zu informieren. Es gibt drei Phasen, in denen Ereignisse ausgelöst werden:
Erfassungsphase: Erfassung
Auslösephase: Ziel
Bubbling-Phase: Bubbling
Dieses Ereignis beginnt vor dem Dokument und geht dann ganz nach unten, um das Objekt zu finden, auf das das aktuelle Ereignis geklickt hat. Wenn das Ereignis das angeklickte Objekt erreicht, kehrt es entlang des ursprünglichen Pfads zurück (Blasenprozess), bis es den gesamten DOM-Baum verlässt.
Hier ist ein HTML-Beispiel:
<html> <body> <ul> <li id="li_1"><button id="button_1">Button A</button></li> <li id="li_2"><button id="button_2">Button B</button></li> <li id="li_3"><button id="button_3">Button C</button></li> </ul> </body> </html>
Wenn Sie auf Schaltfläche A klicken, sieht der Pfad des Ereignisses wie folgt aus:
START | #document \ | HTML | | BODY } CAPTURE PHASE | UL | | LI#li_1 / | BUTTON <-- TARGET PHASE | LI#li_1 \ | UL | | BODY } BUBBLING PHASE | HTML | v #document / END
Beachten Sie, dass Sie das durch Ihren Klick erzeugte Ereignis auf dem Pfad des Ereignisses erfassen können. Wir sind sehr sicher, dass dieses Ereignis definitiv das übergeordnete Element ul durchlaufen wird. Wir können unsere Ereignisverarbeitung an das übergeordnete Element binden und unsere Lösung vereinfachen. Dies wird als Ereignisdelegation und Proxy (delegierte Ereignisse) bezeichnet.
Hinweis* Tatsächlich sind die von Flash/Silverlight/WPF entwickelten Ereignismechanismen sehr ähnlich. Hier ist ihr Ereignisflussdiagramm. Abgesehen davon, dass Silverlight 3 die alte Version des IE-Ereignismodells nur mit der Bubbling-Phase verwendet, verfügt es grundsätzlich über diese drei Phasen. (Die Ereignisverarbeitung der alten Versionen von IE und SL3 verfügt nur über einen Bubbling-Prozess vom Triggerobjekt zum Stammobjekt, was möglicherweise der Vereinfachung des Ereignisverarbeitungsmechanismus dient.)
委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。
让我们看一个具体的例子,我们看看上文的那个工具栏的例子:
<ul class="toolbar"> <li><button class="btn">Pencil</button></li> <li><button class="btn">Pen</button></li> <li><button class="btn">Eraser</button></li> </ul>
因为我们知道单击button元素会冒泡到UL.toolbar元素,让我们将事件处理放到这里试试。我们需要稍微调整一下:
var toolbar = document.querySelector(".toolbar"); toolbar.addEventListener("click", function(e) { var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); });
这样我们清理了大量的代码,再也没有循环了。注意我们使用了e.target代替了之前的e.currentTarget。这是因为我们在一个不同的层次上面进行了事件侦听。
e.target 是当前触发事件的对象,即用户真正单击到的对象。
e.currentTarget 是当前处理事件的对象,即事件绑定的对象。
在我们的例子中e.currentTarget就是UL.toolbar。
注* 其实不止事件机制,在整个UI构架上FLEX(不是Flash) /Silverlight /WPF /Android的实现跟WEB也非常相似,都使用XML(HTML)实现模板及元素结构组织,Style(CSS)实现显示样式及UI,脚本(AS3,C#,Java,JS)实现控制。不过Web相对其他平台更加开放,不过历史遗留问题也更多。但是几乎所有的平台都支持Web标准,都内嵌有类似WebView这样的内嵌Web渲染机制,相对各大平台复杂的前端UI框架和学习曲线来说,使用Web技术实现Native APP的前端UI是非常低成本的一项选择。
以上就是理解JavaScript中的事件路由冒泡过程及委托代理机制的内容,更多相关内容请关注PHP中文网(www.php.cn)!