Nous avons oublié la délégation en JavaScript. Délégation d'événement en réaction

Bonjour à tous. Cet article concerne la délégation d'événements en JavaScript et son implémentation dans react.js.



De quoi s'agit-il réellement? Pourquoi et pourquoi?


Pour commencer, discutons brièvement:


  1. qu'est-ce qu'un événement;
  2. comment se produit la distribution;
  3. Traitement DOM niveau 2 avec un exemple en JavaScript;

Et enfin: pourquoi ne pas oublier la délégation dans React.


Événement


JavaScript et HTML interagissent entre eux à travers des événements. Chaque événement sert à indiquer à JavaScript que quelque chose s'est produit dans la fenêtre du document ou du navigateur. Afin d'attraper ces événements, nous avons besoin d'écouteurs (écouteurs), ces gestionnaires qui sont déclenchés en cas d'événement.


Propagation de l'événement


Commandez. Résoudre le problème: comment comprendre à quelle partie de la page appartient l'événement? Deux méthodes ont été implémentées: dans Internet Explorer, «bulles d’événement» et dans Netscape Communicator, «accrochage d’événements».


Bulle d'événement


Dans ce cas, l'événement est déclenché au niveau du nœud le plus profond de l'arborescence de documents, après quoi il monte dans la hiérarchie jusqu'à la fenêtre elle-même.


<!DOCTYPE html> <html> <head> <title>Some title</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html> 

Dans ce cas, il y aura l'ordre suivant:


  1. élément div
  2. élément de corps
  3. élément html
  4. document
  5. fenêtre

Désormais, la navigation est prise en charge par tous les navigateurs modernes, bien qu'avec différentes implémentations.

Dans le cas d'une interception d'événement, l'inverse est vrai:


  1. fenêtre
  2. document
  3. élément html
  4. élément de corps
  5. élément div

On pensait que l'événement pouvait être traité avant qu'il n'atteigne l'élément cible (comme décidé dans Netscape, il a ensuite repris tous les navigateurs modernes).

Par conséquent, nous avons la structure suivante pour distribuer les événements DOM:


  1. fenêtre
  2. document
  3. élément html
  4. élément de corps // la phase d'interception se termine
  5. élément div // phase cible
  6. élément du corps // la phase de montée commence
  7. élément html
  8. document
  9. fenêtre

Ce schéma est divisé en trois phases: la phase d'interception - l'événement peut être intercepté avant qu'il n'atteigne l'élément, la phase cible - le traitement par l'élément cible et la phase de remontée - pour effectuer les dernières actions en réponse à l'événement.


Nous nous tournons donc vers le traitement des événements


Voyons un exemple typique de gestion d'événements en JavaScript.


 const btn = document.getElementById('myDiv') btn.addEventListener("click", handler) // some code btn.removeEventListener("click", handler) 

Tout ne serait rien, mais ici, nous rappelons notre bien-aimé IE qui souscrit aux événements en utilisant attachEvent, et pour supprimer detachEvent. Et vous pouvez vous abonner à l'événement plusieurs fois. Et n'oubliez pas qu'en signant une fonction anonyme, nous n'avons pas la possibilité de nous désinscrire.


Mais nous ne sommes pas des codeurs externes g *. Faisons tout selon le canon:


 var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } } } 

Si bien, mais qu'en est-il de l'objet événement? Après tout, dans IE, il n'y a pas de .target .srcElement, preventDefault? no returnValue = false. Mais il n'y a rien à ajouter quelques méthodes:


 var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, getEvent: function (event) { return event ? event : window.event }, getTarget: function (event) { return event.target || event.srcElement }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault() } else { event.returnValue = false } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } }, stopPropagation: function (event) { if (event.stopPropagation) { event.stopPropagation() } else { event.cancelBubble = true } } } 

Etc. etc. et ce sont toutes des danses.


Eh bien, nous sommes bien faits, nous avons résolu tous les problèmes, tout va bien. Certes, le code est sorti assez lourd. Imaginez maintenant que nous avons besoin de beaucoup d'abonnements à de nombreux éléments. Wow, cela prendra pas mal de lignes de code. Un exemple:


 <ul> <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var item1 = document.getElementById('id1') var item2 = document.getElementById('id2') var itemNext = document.getElementById('some-next-id') EventUtil.addHandler(item1, "click", someHandle) EventUtil.addHandler(item2, "click", someHandle2) EventUtil.addHandler(itemNext, "click", someHandle3) 

Et donc pour chaque élément, et nous ne devons pas oublier de supprimer, de travailler avec la cible et similaires


Et ici, la délégation de l'événement vient à notre aide.


Tout ce que nous devons faire est de connecter un seul gestionnaire au point le plus élevé de l'arborescence DOM:


 <ul id="main-id"> //  id    <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var list = document.getElementById('main-id') EventUtil.addHandler(list, "click", function(event) { event = EventUtil.getEvent(event) var target = EventUtil.getTarget(event) switch(target.id) { case "id1": //  -    id1 break case "id2": //  -    id1 break case "some-next-id": //  -    break } }) 

Par conséquent, nous n'avons qu'un seul gestionnaire en mémoire et la propriété id peut être utilisée pour l'action souhaitée. Une consommation de mémoire moindre améliore les performances globales des pages. L'enregistrement d'un gestionnaire d'événements nécessite moins de temps et moins d'appels vers le DOM. L'exception est peut-être mouseover et mouseout, avec eux tout est un peu plus compliqué.


Et qu'en est-il de React


Tout sur la compatibilité entre navigateurs pour nous a déjà été fait par des gars de Facebook. Tous nos gestionnaires d'événements reçoivent une instance de SyntheticEvent . Ce qui nous permet de réutiliser les événements du pool, en supprimant toutes les propriétés après avoir appelé le gestionnaire.


Bon.


Néanmoins, un gestionnaire supplémentaire est un gestionnaire supplémentaire. Je me suis rencontré plusieurs fois, et je me repens, j'ai écrit ce genre de code:


 class Example extends React.Component { handleClick () { console.log('click') } render () { return ( <div> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id onClick={() => console.log('click')} /> )} </div> ) } } 

L'exemple montre le cas où il existe une sorte de feuille avec n-nombre d'éléments, et donc avec n-nombre d'enregistrements de gestionnaires.


Courons, allons sur la page et vérifions combien de gestionnaires sont en action en ce moment. Pour cela, j'ai trouvé un bon script:


 Array.from(document.querySelectorAll('*')) .reduce(function(pre, dom){ var clks = getEventListeners(dom).click; pre += clks ? clks.length || 0 : 0; return pre }, 0) 

Fonctionne dans chrome dev-tool.


Et maintenant, nous déléguons tout cela aux div parent et acclamations, nous venons d'optimiser notre application n = array.length fois. Exemple de code ci-dessous:


 class Example extends React.Component { constructor () { super() this.state = { useElem: 0 } } handleClick (elem) { var id = elem.target.id this.setState({ useElem: id }) } render () { return ( <div onClick={this.handleClick}> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id useElem={index === this.state.useElem} /> )} </div> ) } } 

La délégation est un bon outil pour traiter un grand nombre d'abonnements, et dans le cas d'un rendu dynamique et de redessins fréquents, elle est tout simplement irremplaçable. Dommage pour les ressources de l'utilisateur, elles ne sont pas illimitées.


Cet article est basé sur un livre JavaScript pour les développeurs Web professionnels, écrit par Nicholas Zakas.


Merci beaucoup pour votre attention. Si vous avez quelque chose à partager ou trouvez une sorte de faille, peut-être une erreur ou une question, écrivez dans les commentaires. Je serai heureux de toute rétroaction!

Source: https://habr.com/ru/post/fr467361/


All Articles