Heim > Web-Frontend > js-Tutorial > Hauptteil

Verstehen Sie den Event-Routing-Bubbling-Prozess und den Delegationsmechanismus in JavaScript

黄舟
Freigeben: 2017-02-27 14:05:52
Original
1197 Leute haben es durchsucht

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?

OK, welches Problem wird damit gelöst?

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>
Nach dem Login kopieren
Nach dem Login kopieren

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");
  });
}
Nach dem Login kopieren

Es sieht gut aus, aber Es funktioniert tatsächlich nicht so, wie Sie es erwarten.

Fallen von Schließungen

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]));
}
Nach dem Login kopieren

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])
}
Nach dem Login kopieren

Jetzt funktioniert es jetzt einwandfrei. Auf die richtige Schaltfläche zu zeigen ist immer

Was ist also falsch an dieser Lösung?

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>
Nach dem Login kopieren

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);
}
Nach dem Login kopieren

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.

Wie Ereignisse funktionieren

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>
Nach dem Login kopieren

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
Nach dem Login kopieren

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>
Nach dem Login kopieren
Nach dem Login kopieren

  因为我们知道单击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");
});
Nach dem Login kopieren

  这样我们清理了大量的代码,再也没有循环了。注意我们使用了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)!


Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage