web-dev-qa-db-fra.com

Qu'est-ce que la délégation d'événement DOM?

Quelqu'un peut-il s'il vous plaît expliquer la délégation d'événement en JavaScript et comment est-ce utile?

176
Xenon

La délégation d'événements DOM est un mécanisme permettant de répondre aux événements ui via un seul parent commun plutôt que chaque enfant, grâce à la magie de l'événement "bouillonnant" (ou propagation de l'événement).

Lorsqu'un événement est déclenché sur un élément, les événements suivants se produisent :

L'événement est envoyé à sa cible EventTarget et tous les écouteurs d'événements trouvés dans cette zone sont déclenchés. Bubbling Les événements déclencheront alors tous les écouteurs d'événements supplémentaires trouvés en suivant la chaîne parentale de EventTarget vers le haut, en recherchant les écouteurs d'événements enregistrés chaque fois. EventTarget. Cette propagation vers le haut se poursuivra jusqu’à Document inclus.

La propagation d'événements constitue la base de la délégation d'événements dans les navigateurs. Vous pouvez maintenant lier un gestionnaire d'événements à un seul élément parent. Ce gestionnaire sera exécuté chaque fois que l'événement se produira sur l'un de ses nœuds enfants (et ses enfants à tour de rôle). Il s'agit d'une délégation d'événements. En voici un exemple concret:

<ul onclick="alert(event.type + '!')">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
</ul>

Avec cet exemple si vous deviez cliquer sur l’un des enfants <li> nœuds, une alerte de "click!", même s’il n’existe aucun gestionnaire de clics lié à la <li> vous avez cliqué sur. Si nous avons lié onclick="..." pour chaque <li> vous obtiendrez le même effet.

Alors, quel est l'avantage?

Imaginez que vous ayez maintenant besoin d'ajouter dynamiquement de nouveaux <li> éléments de la liste ci-dessus via la manipulation du DOM:

var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);

Sans en utilisant la délégation d'événements, vous devez "relier" le "onclick" gestionnaire d’événements vers le nouveau <li> élément, afin qu’il agisse de la même manière que ses frères et soeurs. Avec délégation d'événements, vous n'avez rien à faire. Ajoutez simplement le nouveau <li> à la liste et vous avez terminé.

C'est absolument fantastique pour les applications Web avec des gestionnaires d'événements liés à de nombreux éléments, où de nouveaux éléments sont créés et/ou supprimés dynamiquement dans le DOM. Avec la délégation d'événements, vous pouvez réduire considérablement le nombre de liaisons d'événements en les déplaçant vers un élément parent commun. Le code qui crée dynamiquement de nouveaux éléments à la volée peut être découplé de la logique de liaison de leurs gestionnaires d'événements.

Un autre avantage de la délégation d'événements est que l'empreinte mémoire totale utilisée par les écouteurs d'événements diminue (car le nombre de liaisons d'événements diminue). Cela peut ne pas faire beaucoup de différence avec les petites pages à déchargement fréquent (c.-à-d. Que l’utilisateur navigue souvent vers des pages différentes). Mais pour les applications à longue durée de vie, cela peut être important. Il existe des situations très difficiles à dépister où des éléments retirés du DOM réclament toujours de la mémoire (c'est-à-dire qu'ils fuient), et cette fuite de mémoire est souvent liée à une liaison d'événement. Avec la délégation d'événements, vous êtes libre de détruire des éléments enfants sans risquer d'oublier de "dissocier" leurs écouteurs d'événements (puisque l'auditeur est sur l'ancêtre). Ces types de fuites de mémoire peuvent ensuite être contenus (s'ils ne sont pas éliminés, ce qui est difficile à faire de temps en temps.) IE Je vous regarde).

Voici quelques exemples de code concrets de délégation d’événement:

289
Crescent Fresh

La délégation d'événements vous permet d'éviter d'ajouter des écouteurs d'événements à des nœuds spécifiques. à la place, l'écouteur d'événements est ajouté à un parent. Cet écouteur d'événements analyse les événements en bulle pour trouver une correspondance sur les éléments enfants.

Exemple JavaScript:

Disons que nous avons un élément UL parent avec plusieurs éléments enfants:

<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>

Disons également que quelque chose doit se produire lorsque chaque élément enfant est cliqué. Vous pouvez ajouter un écouteur d'événement distinct à chaque élément LI, mais que se passe-t-il si les éléments LI sont fréquemment ajoutés et supprimés de la liste? Ajouter et supprimer des écouteurs d'événements serait un cauchemar, surtout si le code d'addition et de suppression se trouve à différents endroits de votre application. La meilleure solution consiste à ajouter un écouteur d'événement à l'élément UL parent. Mais si vous ajoutez l'écouteur d'événement au parent, comment saurez-vous sur quel élément a été cliqué?

Simple: lorsque l'événement se propage jusqu'à l'élément UL, vous vérifiez la propriété cible de l'objet événement pour obtenir une référence au noeud sur lequel vous avez cliqué. Voici un extrait de code JavaScript très basique illustrant la délégation d'événements:

// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
if(e.target && e.target.nodeName == "LI") {
    // List item found!  Output the ID!
    console.log("List item ", e.target.id.replace("post-"), " was clicked!");
       }
 });

Commencez par ajouter un écouteur d'événements click à l'élément parent. Lorsque l'écouteur d'événement est déclenché, vérifiez l'élément event pour vous assurer qu'il s'agit du type d'élément auquel réagir. Si c'est un élément LI, boum: nous avons ce qu'il nous faut! Si ce n'est pas un élément que nous voulons, l'événement peut être ignoré. Cet exemple est assez simple - UL et LI est une comparaison simple. Essayons quelque chose de plus difficile. Ayons une division parent avec de nombreux enfants, mais tout ce qui nous importe est une balise A avec la classe CSS classA:

  // Get the parent DIV, add click listener...
  document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target was the clicked element
if(e.target && e.target.nodeName == "A") {
    // Get the CSS classes
    var classes = e.target.className.split(" ");
    // Search for the CSS class!
    if(classes) {
        // For every CSS class the element has...
        for(var x = 0; x < classes.length; x++) {
            // If it has the CSS class we want...
            if(classes[x] == "classA") {
                // Bingo!
                console.log("Anchor element clicked!");
                // Now do something here....
            }
        }
    }

  }
});

http://davidwalsh.name/event-delegate

27
Osama AbuSitta

délégation d'événement dom est quelque chose de différent de la définition de l'informatique.

Il fait référence à la gestion des événements de bullage de nombreux éléments, tels que les cellules de tableau, d'un objet parent, comme le tableau. Cela peut simplifier le code, en particulier lors de l'ajout ou de la suppression d'éléments, et économiser de la mémoire.

7
kennebec

Délégation est une technique dans laquelle un objet exprime certains comportements à l'extérieur mais délègue en réalité la responsabilité de la mise en œuvre de ce comportement à un objet associé. Cela ressemble beaucoup au modèle de proxy, mais son objectif est très différent. La délégation est un mécanisme d'abstraction qui centralise le comportement d'un objet (méthode).

En règle générale: utilisez la délégation comme alternative à l'héritage. L'héritage est une bonne stratégie, lorsqu'il existe une relation étroite entre un objet parent et un objet enfant, cependant, les objets d'héritage couplent très étroitement les objets. La délégation est souvent le moyen le plus souple d’exprimer une relation entre les classes.

Ce modèle est également appelé "chaînes de proxy". Plusieurs autres modèles de conception utilisent la délégation - les modèles d'état, de stratégie et de visiteur en dépendent.

6
Ewan Todd

Le concept de délégation

S'il existe de nombreux éléments dans un parent et que vous souhaitez gérer des événements sur eux, ne liez pas de gestionnaires à chaque élément. Liez plutôt le gestionnaire unique à son parent et récupérez l'enfant à partir de event.target. Ce site fournit des informations utiles sur la mise en œuvre de la délégation d'événements. http://javascript.info/tutorial/event-delegation

5
Joseph

La délégation d'événements gère un événement qui bubbles utilise un gestionnaire d'événements sur un élément conteneur, mais n'active le comportement du gestionnaire d'événements que si l'événement s'est produit sur un élément du conteneur qui correspond à une condition donnée. Cela peut simplifier la gestion des événements sur les éléments du conteneur.

Par exemple, supposons que vous souhaitiez gérer un clic sur une cellule de tableau dans un grand tableau. Vous pouvez écrivez une boucle pour connecter un gestionnaire de clics à chaque cellule ... ou vous pouvez connecter un gestionnaire de clics sur la table et utiliser la délégation d'événements pour la déclencher uniquement pour les cellules de la table (et pas les en-têtes de tableau, ni les espaces dans une ligne autour des cellules, etc.).

C'est également utile lorsque vous allez ajouter et supprimer des éléments du conteneur, car vous n'avez pas à vous soucier de l'ajout et de la suppression de gestionnaires d'événements sur ces éléments; accrochez simplement l'événement au conteneur et gérez-le lorsqu'il fait des bulles.

Voici un exemple simple (il est intentionnellement verbeux pour permettre une explication en ligne): Gérer un clic sur un élément td dans une table de conteneur:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    // Find out if the event targeted or bubbled through a `td` en route to this container element
    var element = event.target;
    var target;
    while (element && !target) {
        if (element.matches("td")) {
            // Found a `td` within the container!
            target = element;
        } else {
            // Not found
            if (element === this) {
                // We've reached the container, stop
                element = null;
            } else {
                // Go to the next parent in the ancestry
                element = element.parentNode;
            }
        }
    }
    if (target) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<table id="container">
    <thead>
        <tr>
            <th>Language</th>
            <th>1</th>
            <th>2</th>
            <th>3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th class="rowheader">English</th>
            <td>one</td>
            <td>two</td>
            <td>three</td>
        </tr>
        <tr>
            <th class="rowheader">Español</th>
            <td>uno</td>
            <td>dos</td>
            <td>tres</td>
        </tr>
        <tr>
            <th class="rowheader">Italiano</th>
            <td>uno</td>
            <td>due</td>
            <td>tre</td>
        </tr>
    </tbody>
</table>

Avant d’entrer dans les détails, rappelons-nous comment fonctionnent les événements DOM.

Les événements DOM sont envoyés du document à l'élément cible (la phase capture), puis sont renvoyés de l'élément cible au document (la phase bouillonnante ) Ce graphique dans l'ancien spécification d'événements DOM (maintenant remplacé, mais le graphique est toujours valide) le montre très bien:

enter image description here

Tous les événements ne sont pas affichés, mais la plupart le font, y compris click.

Les commentaires dans l'exemple de code ci-dessus décrivent comment cela fonctionne. matches vérifie si un élément correspond à un sélecteur CSS, mais vous pouvez bien sûr vérifier si quelque chose correspond à vos critères autrement si vous ne souhaitez pas utiliser de sélecteur CSS .

Ce code est écrit pour appeler verbalement les différentes étapes, mais sur des navigateurs vaguement modernes (et aussi sur IE si vous utilisez un polyfill)), vous pouvez utiliser closest et contains au lieu de la boucle:

var target = event.target.closest("td");
    console.log("You clicked a td: " + target.textContent);
} else {
    console.log("That wasn't a td in the container table");
}

Exemple en direct:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    var target = event.target.closest("td");
    if (target && this.contains(target)) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<table id="container">
    <thead>
        <tr>
            <th>Language</th>
            <th>1</th>
            <th>2</th>
            <th>3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th class="rowheader">English</th>
            <td>one</td>
            <td>two</td>
            <td>three</td>
        </tr>
        <tr>
            <th class="rowheader">Español</th>
            <td>uno</td>
            <td>dos</td>
            <td>tres</td>
        </tr>
        <tr>
            <th class="rowheader">Italiano</th>
            <td>uno</td>
            <td>due</td>
            <td>tre</td>
        </tr>
    </tbody>
</table>

closest vérifie l'élément sur lequel vous l'avez appelé pour voir s'il correspond au sélecteur CSS donné et, le cas échéant, renvoie le même élément; sinon, il vérifie si l'élément parent correspond et renvoie le parent si c'est le cas; sinon, il vérifie le parent du parent, etc. Il recherche donc l'élément "le plus proche" de la liste des ancêtres correspondant au sélecteur. Comme cela peut dépasser l’élément conteneur, le code ci-dessus utilise contains pour vérifier que si un élément correspondant est trouvé, il se trouve dans le conteneur - car en accrochant l’événement au conteneur, vous avez indiqué que vous ne souhaitiez que manipuler des éléments dans ce conteneur.

Pour revenir à notre exemple de table, cela signifie que si vous avez une table dans une cellule de table, elle ne correspondra pas à la cellule de table contenant la table:

// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
    var target = event.target.closest("td");
    if (target && this.contains(target)) {
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
});
table {
    border-collapse: collapse;
    border: 1px solid #ddd;
}
th, td {
    padding: 4px;
    border: 1px solid #ddd;
    font-weight: normal;
}
th.rowheader {
    text-align: left;
}
td {
    cursor: pointer;
}
<!-- The table wrapped around the #container table -->
<table>
    <tbody>
        <tr>
            <td>
                <!-- This cell doesn't get matched, thanks to the `this.contains(target)` check -->
                <table id="container">
                    <thead>
                        <tr>
                            <th>Language</th>
                            <th>1</th>
                            <th>2</th>
                            <th>3</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <th class="rowheader">English</th>
                            <td>one</td>
                            <td>two</td>
                            <td>three</td>
                        </tr>
                        <tr>
                            <th class="rowheader">Español</th>
                            <td>uno</td>
                            <td>dos</td>
                            <td>tres</td>
                        </tr>
                        <tr>
                            <th class="rowheader">Italiano</th>
                            <td>uno</td>
                            <td>due</td>
                            <td>tre</td>
                        </tr>
                    </tbody>
                </table>
            </td>
            <td>
                This is next to the container table
            </td>
        </tr>
    </tbody>
</table>
4
T.J. Crowder

C'est fondamentalement la façon dont l'association est faite à l'élément. .click s'applique au DOM actuel, alors que .on (utilisant la délégation) restera valable pour les nouveaux éléments ajoutés au DOM après l'association d'événement.

Ce qui est préférable d'utiliser, je dirais que cela dépend du cas.

Exemple:

<ul id="todo">
   <li>Do 1</li>
   <li>Do 2</li>
   <li>Do 3</li>
   <li>Do 4</li>
</ul>

.Cliquez sur l'événement:

$("li").click(function () {
   $(this).remove ();
});

Événement .on:

$("#todo").on("click", "li", function () {
   $(this).remove();
});

Notez que j'ai séparé le sélecteur dans le fichier .on. Je vais expliquer pourquoi.

Supposons qu'après cette association, procédons comme suit:

$("#todo").append("<li>Do 5</li>");

C'est là que vous remarquerez la différence.

Si l'événement était associé via .click, la tâche 5 n'obéira pas à l'événement click et ne sera donc pas supprimée.

S'il était associé via .on, avec le sélecteur séparé, il obéira.

2
Lucas

Un délégué en C # est similaire à un pointeur de fonction en C ou C++. L'utilisation d'un délégué permet au programmeur d'encapsuler une référence à une méthode dans un objet délégué. L'objet délégué peut ensuite être transmis à un code qui peut appeler la méthode référencée, sans avoir à savoir au moment de la compilation quelle méthode sera appelée.

Voir ce lien -> http://www.akadia.com/services/dotnet_delegates_and_events.html

1
Bhaskar

La délégation d'événements utilise deux fonctionnalités souvent ignorées des événements JavaScript: l'événement bouillonnant et l'élément cible. Lorsqu'un événement est déclenché sur un élément, par exemple un clic de souris sur un bouton, le même événement est également déclenché sur tous les ancêtres de cet élément. . Ce processus est connu sous le nom d'événement bouillonnant; l'événement monte de l'élément d'origine au sommet de l'arborescence DOM.

Imaginez un tableau HTML à 10 colonnes et 100 lignes dans lequel vous souhaitez que quelque chose se produise lorsque l'utilisateur clique sur une cellule du tableau. Par exemple, j’ai dû une fois rendre chaque cellule d’un tableau de cette taille modifiable lorsque l’utilisateur clique dessus. L'ajout de gestionnaires d'événements à chacune des 1 000 cellules constituerait un problème majeur de performances et, potentiellement, une source de fuites de mémoire affectant le navigateur. Au lieu de cela, en utilisant la délégation d’événements, vous n’ajouteriez qu’un seul gestionnaire d’événements à l’élément table, intercepteriez l’événement click et détermineriez la cellule sur laquelle vous avez cliqué.

1
Priti jha