¿Alguien puede explicar la delegación de eventos en JavaScript y cómo es útil?
¿Alguien puede explicar la delegación de eventos en JavaScript y cómo es útil?
Respuestas:
La delegación de eventos DOM es un mecanismo para responder a los eventos ui a través de un solo padre común en lugar de cada hijo, a través de la magia del evento "burbujeando" (también conocido como propagación de eventos).
Cuando se desencadena un evento en un elemento, ocurre lo siguiente :
El evento se envía a su destino
EventTarget
y se activa cualquier escucha de eventos que se encuentre allí. Los eventos burbujeantes activarán a los oyentes de eventos adicionales que se encuentren siguiendo laEventTarget
cadena principal de la cadena ascendente , verificando si hay oyentes de eventos registrados en cada EventTarget sucesivo. Esta propagación ascendente continuará hasta e incluyendo elDocument
.
El burbujeo de eventos proporciona la base para la delegación de eventos en los navegadores. Ahora puede vincular un controlador de eventos a un solo elemento padre, y ese controlador se ejecutará cada vez que el evento ocurra en cualquiera de sus nodos secundarios (y en cualquiera de sus hijos a su vez). Esta es la delegación de eventos. Aquí hay un ejemplo de ello en la práctica:
<ul onclick="alert(event.type + '!')">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
Con ese ejemplo, si <li>
hiciera clic en cualquiera de los nodos secundarios , vería una alerta de "click!"
, a pesar de que no hay un controlador de clic vinculado al <li>
que hizo clic. Si nos unimos onclick="..."
a cada <li>
uno obtendrá el mismo efecto.
Entonces, ¿cuál es el beneficio?
Imagine que ahora necesita agregar dinámicamente nuevos <li>
elementos a la lista anterior a través de la manipulación DOM:
var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);
Sin utilizar la delegación de eventos, tendría que "volver a vincular" el "onclick"
controlador de eventos al nuevo <li>
elemento para que actúe de la misma manera que sus hermanos. Con la delegación de eventos no necesitas hacer nada. Simplemente agregue el nuevo <li>
a la lista y listo.
Esto es absolutamente fantástico para aplicaciones web con controladores de eventos vinculados a muchos elementos, donde los elementos nuevos se crean y / o eliminan dinámicamente en el DOM. Con la delegación de eventos, el número de enlaces de eventos se puede reducir drásticamente moviéndolos a un elemento principal común, y el código que crea dinámicamente nuevos elementos sobre la marcha se puede desacoplar de la lógica de enlace de sus controladores de eventos.
Otro beneficio de la delegación de eventos es que la huella de memoria total utilizada por los oyentes de eventos disminuye (ya que el número de enlaces de eventos disminuye). Puede que no haya mucha diferencia en las páginas pequeñas que se descargan con frecuencia (es decir, el usuario navega a diferentes páginas con frecuencia). Pero para aplicaciones de larga duración puede ser significativo. Hay algunas situaciones realmente difíciles de rastrear cuando los elementos eliminados del DOM aún reclaman memoria (es decir, pierden), y a menudo esta memoria perdida está vinculada a un enlace de evento. Con la delegación de eventos, puede destruir elementos secundarios sin riesgo de olvidarse de "desvincular" a sus oyentes de eventos (ya que el oyente está en el antepasado). Estos tipos de pérdidas de memoria se pueden contener (si no se eliminan, lo cual es muy difícil de hacer a veces. IE te estoy mirando).
Aquí hay algunos ejemplos de códigos concretos mejores de delegación de eventos:
focus
yblur
(que no burbujean)<li>
cuando debería detenerse <ul>
? Si mi pregunta aún no está clara o necesita un hilo separado, me complacería responder.
La delegación de eventos le permite evitar agregar oyentes de eventos a nodos específicos; en su lugar, el detector de eventos se agrega a un padre. Ese oyente de eventos analiza eventos burbujeados para encontrar una coincidencia en elementos secundarios.
Ejemplo de JavaScript:
Digamos que tenemos un elemento UL primario con varios elementos secundarios:
<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>
Digamos también que algo debe suceder cuando se hace clic en cada elemento secundario. Puede agregar un detector de eventos separado a cada elemento LI individual, pero ¿qué sucede si los elementos LI se agregan y eliminan con frecuencia de la lista? Agregar y eliminar oyentes de eventos sería una pesadilla, especialmente si el código de adición y eliminación se encuentra en diferentes lugares dentro de su aplicación. La mejor solución es agregar un detector de eventos al elemento UL primario. Pero si agrega el detector de eventos al padre, ¿cómo sabrá en qué elemento se hizo clic?
Simple: cuando el evento aparece en el elemento UL, verifica la propiedad de destino del objeto del evento para obtener una referencia al nodo real en el que se hizo clic. Aquí hay un fragmento de JavaScript muy básico que ilustra la delegación de eventos:
// 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!");
}
});
Comience agregando un detector de eventos de clic al elemento principal. Cuando se activa el detector de eventos, verifique el elemento del evento para asegurarse de que sea el tipo de elemento al que reaccionar. Si es un elemento LI, boom: ¡tenemos lo que necesitamos! Si no es un elemento que queremos, el evento puede ser ignorado. Este ejemplo es bastante simple: UL y LI son una comparación directa. Probemos algo más difícil. Tengamos un DIV padre con muchos hijos, pero todo lo que nos importa es una etiqueta A con la clase classA CSS:
// 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....
}
}
}
}
});
La delegación de eventos dom es algo diferente de la definición de informática.
Se refiere al manejo de eventos burbujeantes de muchos elementos, como celdas de tabla, desde un objeto primario, como la tabla. Puede mantener el código más simple, especialmente al agregar o eliminar elementos, y ahorra algo de memoria.
La delegación es una técnica en la que un objeto expresa cierto comportamiento al exterior pero en realidad delega la responsabilidad de implementar ese comportamiento a un objeto asociado. Esto suena al principio muy similar al patrón proxy, pero tiene un propósito muy diferente. La delegación es un mecanismo de abstracción que centraliza el comportamiento del objeto (método).
En términos generales: use la delegación como alternativa a la herencia. La herencia es una buena estrategia, cuando existe una estrecha relación entre el objeto padre y el hijo, sin embargo, la herencia une los objetos muy de cerca. A menudo, la delegación es la forma más flexible de expresar una relación entre clases.
Este patrón también se conoce como "cadenas proxy". Varios otros patrones de diseño usan delegación: el Estado, la Estrategia y los Patrones de visitante dependen de ello.
Si hay muchos elementos dentro de un elemento primario y desea controlar los eventos en ellos, no asocie controladores a cada elemento. En su lugar, vincule el controlador único a su padre y obtenga el hijo de event.target. Este sitio proporciona información útil sobre cómo implementar la delegación de eventos. http://javascript.info/tutorial/event-delegation
La delegación de eventos está manejando un evento que burbujea usando un controlador de eventos en un elemento contenedor, pero solo activa el comportamiento del controlador de eventos si el evento ocurrió en un elemento dentro del contenedor que coincide con una condición dada. Esto puede simplificar el manejo de eventos en elementos dentro del contenedor.
Por ejemplo, suponga que desea manejar un clic en cualquier celda de una tabla grande. Usted podría escribir un bucle para conectar un controlador de clic a cada célula ... o se puede conectar un controlador de clic en la delegación de la tabla de eventos y utilizar para activar sólo para celdas de la tabla (y no los encabezados de tabla, o el espacio en blanco dentro de una remar alrededor de las celdas, etc.).
También es útil cuando va a agregar y eliminar elementos del contenedor, ya que no tiene que preocuparse por agregar y eliminar controladores de eventos en esos elementos; simplemente enganche el evento en el contenedor y maneje el evento cuando haga burbujas.
Aquí hay un ejemplo simple (es intencionalmente detallado para permitir una explicación en línea): Manejar un clic en cualquier td
elemento en una tabla de contenedor:
// 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>
Antes de entrar en los detalles de eso, recordemos cómo funcionan los eventos DOM.
Los eventos DOM se envían desde el documento al elemento de destino (la fase de captura ), y luego burbujean desde el elemento de destino al documento (la fase de burbujeo ). Este gráfico en la antigua especificación de eventos DOM3 (ahora reemplazado, pero el gráfico aún es válido) lo muestra realmente bien:
No todos los eventos burbujean, pero la mayoría sí, incluidos click
.
Los comentarios en el ejemplo de código anterior describen cómo funciona. matches
verifica si un elemento coincide con un selector CSS, pero, por supuesto, puede verificar si algo coincide con sus criterios de otras maneras si no desea utilizar un selector CSS.
Ese código está escrito para llamar los pasos individuales de forma detallada, pero en navegadores vagamente modernos (y también en IE si usa un polyfill), puede usar closest
y en contains
lugar del bucle:
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");
}
Ejemplo en vivo:
closest
comprueba el elemento al que lo llama para ver si coincide con el selector CSS dado y, si lo hace, devuelve ese mismo elemento; si no, comprueba el elemento padre para ver si coincide y devuelve el padre si es así; si no, comprueba el padre del padre, etc. Entonces encuentra el elemento "más cercano" en la lista de antepasados que coincide con el selector. Dado que puede pasar el elemento contenedor, el código anterior usa contains
para verificar que si se encontró un elemento coincidente, está dentro del contenedor, ya que al enganchar el evento en el contenedor, has indicado que solo quieres manejar elementos dentro de ese contenedor .
Volviendo a nuestro ejemplo de tabla, eso significa que si tiene una tabla dentro de una celda de tabla, no coincidirá con la celda de tabla que contiene la tabla:
Básicamente se trata de cómo se asocia al elemento. .click
se aplica al DOM actual, mientras que .on
(mediante delegación) seguirá siendo válido para los nuevos elementos agregados al DOM después de la asociación de eventos.
Cuál es mejor usar, diría que depende del caso.
Ejemplo:
<ul id="todo">
<li>Do 1</li>
<li>Do 2</li>
<li>Do 3</li>
<li>Do 4</li>
</ul>
.Haga clic en Evento:
$("li").click(function () {
$(this).remove ();
});
Evento .on:
$("#todo").on("click", "li", function () {
$(this).remove();
});
Tenga en cuenta que he separado el selector en el .on. Te explicaré por qué.
Supongamos que después de esta asociación, hagamos lo siguiente:
$("#todo").append("<li>Do 5</li>");
Ahí es donde notarás la diferencia.
Si el evento se asoció a través de .click, la tarea 5 no obedecerá al evento de clic y, por lo tanto, no se eliminará.
Si se asoció a través de .on, con el selector separado, obedecerá.
Para comprender primero la delegación de eventos, necesitamos saber por qué y cuándo realmente necesitamos o queremos la delegación de eventos.
Puede haber muchos casos, pero analicemos dos grandes casos de uso para la delegación de eventos. 1. El primer caso es cuando tenemos un elemento con muchos elementos secundarios que nos interesan. En este caso, en lugar de agregar un controlador de eventos a todos estos elementos secundarios, simplemente lo agregamos al elemento principal y luego determinamos en qué elemento hijo se activó el evento.
2. El segundo caso de uso para la delegación de eventos es cuando queremos un controlador de eventos adjunto a un elemento que aún no está en el DOM cuando se carga nuestra página. Eso es, por supuesto, porque no podemos agregar un controlador de eventos a algo que no está en nuestra página, por lo que en caso de desaprobación que estamos codificando.
Suponga que tiene una lista de 0, 10 o 100 elementos en el DOM cuando carga su página, y hay más elementos esperando en su mano para agregar en la lista. Por lo tanto, no hay forma de adjuntar un controlador de eventos para los elementos futuros o esos elementos aún no se agregan en el DOM, y también puede haber muchos elementos, por lo que no sería útil tener un controlador de eventos adjunto a cada uno de ellos.
Delegación de eventos
Muy bien, entonces, para hablar sobre la delegación de eventos, el primer concepto del que realmente necesitamos hablar es el burbujeo de eventos.
Evento burbujeante: el evento burbujeante significa que cuando un evento se dispara o se activa en algún elemento DOM, por ejemplo haciendo clic en nuestro botón aquí en la imagen de abajo, entonces el mismo evento exacto también se activa en todos los elementos principales.
El evento se dispara primero en el botón, pero luego también se disparará en todos los elementos principales, uno a la vez, por lo que también se disparará en el párrafo a la sección del elemento principal y, de hecho, en un árbol DOM. hasta el elemento HTML que es la raíz. Entonces decimos que el evento aparece dentro del árbol DOM, y por eso se llama burbujeo.
Elemento objetivo: El elemento en el que el evento se disparó primero se llama elemento objetivo, por lo que el elemento que causó el evento se llama elemento objetivo. En nuestro ejemplo anterior aquí es, por supuesto, el botón en el que se hizo clic. La parte importante es que este elemento objetivo se almacena como una propiedad en el objeto del evento. Esto significa que todos los elementos primarios en los que también se disparará el evento conocerán el elemento objetivo del evento, por lo que el evento se disparó por primera vez.
Eso nos lleva a la delegación de eventos. porque si el evento aparece en el árbol DOM y si sabemos dónde se activó el evento, simplemente podemos adjuntar un controlador de eventos a un elemento padre y esperar a que el evento se desarrolle, y podemos luego haga lo que pretendemos hacer con nuestro elemento objetivo. Esta técnica se llama delegación de eventos. En este ejemplo aquí, podríamos simplemente agregar el controlador de eventos al elemento principal.
Muy bien, de nuevo, la delegación de eventos es no configurar el controlador de eventos en el elemento original que nos interesa, sino adjuntarlo a un elemento padre y, básicamente, capturar el evento allí porque brota. Entonces podemos actuar sobre el elemento que nos interesa usar la propiedad del elemento de destino.
Ejemplo: Ahora supongamos que tenemos dos elementos de lista en nuestra página, después de agregar elementos en esas listas programáticamente, queremos eliminar uno o más elementos de ellos. Usando la técnica de delegación de eventos podemos lograr nuestro propósito fácilmente.
<div class="body">
<div class="top">
</div>
<div class="bottom">
<div class="other">
<!-- other bottom elements -->
</div>
<div class="container clearfix">
<div class="income">
<h2 class="icome__title">Income</h2>
<div class="income__list">
<!-- list items -->
</div>
</div>
<div class="expenses">
<h2 class="expenses__title">Expenses</h2>
<div class="expenses__list">
<!-- list items -->
</div>
</div>
</div>
</div>
</div>
Agregar elementos en esa lista:
const DOMstrings={
type:{
income:'inc',
expense:'exp'
},
incomeContainer:'.income__list',
expenseContainer:'.expenses__list',
container:'.container'
}
var addListItem = function(obj, type){
//create html string with the place holder
var html, element;
if(type===DOMstrings.type.income){
element = DOMstrings.incomeContainer
html = `<div class="item clearfix" id="inc-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}else if (type ===DOMstrings.type.expense){
element=DOMstrings.expenseContainer;
html = ` <div class="item clearfix" id="exp-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__percentage">21%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}
var htmlObject = document.createElement('div');
htmlObject.innerHTML=html;
document.querySelector(element).insertAdjacentElement('beforeend', htmlObject);
}
Eliminar ítems:
var ctrlDeleteItem = function(event){
// var itemId = event.target.parentNode.parentNode.parentNode.parentNode.id;
var parent = event.target.parentNode;
var splitId, type, ID;
while(parent.id===""){
parent = parent.parentNode
}
if(parent.id){
splitId = parent.id.split('-');
type = splitId[0];
ID=parseInt(splitId[1]);
}
deleteItem(type, ID);
deleteListItem(parent.id);
}
var deleteItem = function(type, id){
var ids, index;
ids = data.allItems[type].map(function(current){
return current.id;
});
index = ids.indexOf(id);
if(index>-1){
data.allItems[type].splice(index,1);
}
}
var deleteListItem = function(selectorID){
var element = document.getElementById(selectorID);
element.parentNode.removeChild(element);
}
Un delegado en C # es similar a un puntero de función en C o C ++. El uso de un delegado permite al programador encapsular una referencia a un método dentro de un objeto delegado. El objeto delegado se puede pasar al código que puede llamar al método referenciado, sin tener que saber en el momento de la compilación qué método se invocará.
Vea este enlace -> http://www.akadia.com/services/dotnet_delegates_and_events.html
La delegación de eventos utiliza dos características de los eventos de JavaScript que a menudo se pasan por alto: el burbujeo de eventos y el elemento de destino. Cuando un evento se activa en un elemento, por ejemplo, al hacer clic con el mouse en un botón, el mismo evento también se activa en todos los antepasados de ese elemento . Este proceso se conoce como burbujeo de eventos; el evento emerge desde el elemento de origen hasta la parte superior del árbol DOM.
Imagine una tabla HTML con 10 columnas y 100 filas en las que desea que suceda algo cuando el usuario hace clic en una celda de la tabla. Por ejemplo, una vez tuve que hacer que cada celda de una tabla de ese tamaño fuera editable al hacer clic. Agregar controladores de eventos a cada una de las 1000 celdas sería un problema de rendimiento importante y, potencialmente, una fuente de pérdidas de memoria que bloquea el navegador. En cambio, utilizando la delegación de eventos, agregaría solo un controlador de eventos al elemento de la tabla, interceptaría el evento de clic y determinaría en qué celda se hizo clic.
Adjunte un detector de eventos a un elemento primario que se active cuando se produzca un evento en un elemento secundario.
Propagación de eventosCuando un evento se mueve a través del DOM desde un elemento secundario a uno primario, eso se llama Propagación de eventos , porque el evento se propaga o se mueve a través del DOM.
En este ejemplo, un evento (onclick) de un botón se pasa al párrafo principal.
$(document).ready(function() {
$(".spoiler span").hide();
/* add event onclick on parent (.spoiler) and delegate its event to child (button) */
$(".spoiler").on( "click", "button", function() {
$(".spoiler button").hide();
$(".spoiler span").show();
} );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<p class="spoiler">
<span>Hello World</span>
<button>Click Me</button>
</p>