Maison > interface Web > js tutoriel > le corps du texte

Comprendre le processus de bouillonnement du routage d'événements et le mécanisme de délégation en JavaScript

黄舟
Libérer: 2017-02-27 14:05:52
original
1203 Les gens l'ont consulté

Quand j'implémente cela en utilisant du CSS pur. J'ai commencé à utiliser JavaScript et des classes de style pour compléter les fonctionnalités.

Ensuite, j'ai quelques idées, je souhaite utiliser des événements délégués (délégation d'événements) mais je ne veux pas avoir de dépendances, brancher des bibliothèques, y compris jQuery. Je dois mettre en œuvre moi-même la délégation d’événements.

Voyons d’abord ce qu’est la délégation événementielle ? Comment fonctionnent-ils et comment mettre en œuvre ce mécanisme.

OK, quel problème ça résout ?

Regardons d’abord un exemple simple.

Supposons que nous ayons un ensemble de boutons. Je clique sur un bouton à la fois, puis je souhaite que l'état cliqué soit défini sur "actif". Annuler l'actif lorsque vous cliquez à nouveau.

Ensuite, nous pouvons écrire du HTML :

<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>
Copier après la connexion
Copier après la connexion

Je peux utiliser certains événements Javascript standard pour gérer la logique ci-dessus :

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");
  });
}
Copier après la connexion

Cela a l'air bien, mais Cela ne fonctionne pas réellement comme vous l'espériez.

Pièges de fermetures

Si vous avez une certaine expérience en développement JavaScript, ce problème sera évident.

Pour les non-initiés, la variable bouton est fermée, et le bouton correspondant sera retrouvé à chaque fois... mais en fait il n'y a qu'un seul bouton ici ; il sera réaffecté à chaque boucle.

La première boucle pointe vers le premier bouton, puis le deuxième. Mais lorsque vous cliquez, la variable bouton pointe toujours vers le dernier élément du bouton. C'est le problème.

Ce dont nous avons besoin, c'est d'une portée stable ;

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]));
}
Copier après la connexion

Remarque* La structure du code ci-dessus est un peu compliquée. Vous pouvez également simplement utiliser une fermeture pour fermer et enregistrer la variable de bouton actuelle, comme indiqué ci-dessous :

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])
}
Copier après la connexion
<🎜. > Maintenant, ça marche bien maintenant. Pointer vers le bon bouton est toujours

Alors quel est le problème avec cette solution ?

Cette solution semble correcte, mais nous pouvons effectivement faire mieux.

D'abord, nous avons créé trop de fonctions de traitement. Un écouteur d'événement et un gestionnaire de rappel sont liés à chaque bouton .toolbar correspondant. S'il n'y a que trois boutons, cette allocation de ressources peut être ignorée.

Cependant, et si nous en avions 1 000 ?

<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>
Copier après la connexion
Il ne plantera pas, mais ce n'est pas la meilleure solution. Nous attribuons beaucoup de fonctions inutiles. Refactorisons-le pour qu'il ne soit attaché qu'une seule fois, en liant une seule fonction, afin de gérer potentiellement des milliers d'appels.

Plutôt que de fermer la variable bouton pour stocker l'objet sur lequel nous avons cliqué à ce moment-là, nous pouvons utiliser l'objet événement pour obtenir l'objet sur lequel nous avons cliqué à ce moment-là.

L'objet événement a des métadonnées. Dans le cas de liaisons multiples, nous pouvons utiliser currentTarget pour obtenir l'objet actuellement lié. Le code dans l'exemple ci-dessus peut être modifié en :

< 🎜. > Pas mal ! Mais cela simplifie simplement une seule fonction et la rend plus lisible, mais elle est toujours liée plusieurs fois.
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);
}
Copier après la connexion

Cependant, nous pouvons faire mieux.

Supposons que nous ajoutions dynamiquement quelques boutons à cette liste. Ensuite, nous ajouterons et supprimerons également des liaisons d'événements pour ces éléments dynamiques. Ensuite, nous devons conserver les variables utilisées par ces fonctions de traitement et le contexte actuel, ce qui semble peu fiable.

Il existe peut-être d'autres moyens.

Commençons par bien comprendre comment fonctionnent les événements et comment ils sont diffusés dans les DOM.

Comment fonctionnent les événements

Lorsque l'utilisateur clique sur un élément, un événement est généré pour informer l'utilisateur du comportement actuel. Il y a trois étapes lorsque les événements sont distribués :

    Étape de capture : Capture
  • Étape de déclenchement : Cible
  • Étape de bouillonnement : bouillonnement
  • Cet événement commence avant le document puis descend jusqu'en bas pour trouver l'objet sur lequel l'événement en cours a cliqué. Lorsque l'événement atteint l'objet cliqué, il reviendra le long du chemin d'origine (processus de bouillonnement) jusqu'à ce qu'il quitte toute l'arborescence DOM.

Voici un exemple HTML :

Lorsque vous cliquez sur le bouton A, le chemin de l'événement ressemblera à ceci :
<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>
Copier après la connexion

START
| #document  \
| HTML        |
| BODY         } CAPTURE PHASE
| UL          |
| LI#li_1    /
| BUTTON     <-- TARGET PHASE
| LI#li_1    \
| UL          |
| BODY         } BUBBLING PHASE 
| HTML        |
v #document  /
END
Copier après la connexion
Notez que cela signifie que vous pouvez capturer l'événement généré par votre clic sur le chemin de l'événement. Nous sommes très sûrs que cet événement passera définitivement par leur élément parent ul. Nous pouvons lier notre traitement d'événements à l'élément parent et simplifier notre solution. C'est ce qu'on appelle la délégation d'événements et le proxy (événements délégués).

Remarque* En fait, les mécanismes événementiels développés par Flash/Silverlight/WPF sont très similaires. Voici leur organigramme d'événements. Sauf que Silverlight 3 utilise l'ancienne version du modèle d'événement d'IE avec uniquement l'étape de bouillonnement, il comporte essentiellement ces trois étapes. (Le traitement des événements de l'ancienne version d'IE et SL3 n'a qu'un processus de bouillonnement de l'objet déclencheur vers l'objet racine, ce qui peut simplifier le mécanisme de traitement des événements.)

  事件委托代理

  委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。

  让我们看一个具体的例子,我们看看上文的那个工具栏的例子:

<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>
Copier après la connexion
Copier après la connexion

  因为我们知道单击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");
});
Copier après la connexion

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


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal