Desarrollo front-end, estándares web, accesibilidad y más
Por kcmr, el 12 de agosto de 2008 en Javascript
Event bubbling y event capturing son dos conceptos de javascript relativos al orden en que las funciones asociadas a un mismo tipo de evento deben ejecutarse. Aunque es posible que en la práctica este comportamiento de los eventos no suponga un problema, hay que saber que el burbujeo o event bubbling siempre ocurre y hay que tenerlo en cuenta en eventos de tipo mouseover, mousemove, mouseout, etc.
Para comprenderlo mejor, no hay nada como mostrar ejemplos de cada uno de ellos.
Si hemos asociado una función para un tipo de evento, por ejemplo onclick, en un elemento padre y otra función para el mismo tipo de evento en un elemento hijo, cuando el evento tenga lugar en el elemento hijo, se ejecutará también la función para el elemento padre. Esto es lo que se conoce como event bubbling y es el comportamiento por defecto de los eventos cuando son registrados en el modo tradicional (elemento.onclick = function())
El bloque verde (elemento hijo) está dentro del bloque amarillo (elemento padre). Al hacer click en el bloque amarillo se debe mostrar el mensaje de alerta “Hola mundo” y al hacer click en el bloque verde se debe mostrar el mensaje “Hello world”. Si hacemos click en el bloque verde, veremos ejecutarse las dos funciones, primero la del bloque verde y después la del bloque amarillo.
Elemento padre
Click para ver “Hola mundo”
Elemento hijo
Click para ver “Hello world”
Como suele ser habitual, Microsoft y Netscape apostaron por distintos modelos:
- Netscape said that the event on element1 takes place first. This is called event capturing
- Microsoft maintained that the event on element2 takes precedence. This is called event bubbling
Mientras que navegadores basados en Gecko, Opera y Konkeror soportan los dos modelos, bubbling y capturing, Explorer sólo soporta el primero. Versiones antiguas de Opera no soportan ninguno de los dos.
El método addEventListener() del DOM, permite especificar qué comportamiento (bubbling o capturing) se establecerá para un manejador de evento dependiendo del valor (true o false) de su último argumento. En el siguiente ejemplo se establece en el modelo de captura mediante el valor true.
Elemento padre
Click para ver “Hola mundo”
Elemento hijo
Click para ver “Hello world”
Internet Explorer tiene su propio modelo de registro de eventos y no soporta el método estándar del DOM addEventListener(). En su lugar utiliza el método attachEvent() con importantes limitaciones e inconvenientes:
this siempre hace referencia al objeto window.Recomiendo echarle un vistazo al addEvent de Dean Edwards que soluciona el problema mencionado con this en Explorer.
El método estándar del DOM stopPropagation(), sirve para evitar el burbujeo o event bubbling. Internet Explorer necesita del uso de la propiedad no estándar* cancelBubble del objeto event.
Para cancelar la propagación de un evento podemos usar la siguiente función:
function cancelBubbling(e){
if(!e) e = window.event;
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
}
En este ejemplo se utiliza la función anterior para detener la propagación y se añade otra pequeña función cross-browser para evitar que se ejecute la acción por defecto en un elemento html (seguir destino del enlace en elemento <a>)
function cancelDefault(e){
if(!e) e = window.event;
e.returnValue = false;
if(e.preventDefault) e.preventDefault();
}
* cancelBubble es una propiedad del objeto event definida por el DOM: http://www.w3.org/TR/1999/WD-DOM-Level-2-19990304/events.html
Campos obligatorios marcados con asterisco *
2 comentarios
Por Alberto, el 28 de mayo de 2009
Hola, igual es una pregunta algo tonta. Pero después de un preventDefault al evento como podría restaurar de nuevo el comportamiento inicial?
Gracias
Por kcmr, el 29 de mayo de 2009
Alberto, lo único que se me ocurre, aunque supongo que es un poco cutre, es añadir algún atributo al elemento que recibe el evento en la función en la que se usa preventDefault y ejecutarla sólo si ese atributo no existe, de manera que se cancele la acción por defecto sólo una vez.
Por ejemplo:
function click1(){
document.getElementById(‘test’).addEventListener(‘click’, function(ev){
if(! document.getElementById(‘test’).getAttribute(‘clicked’) ) {
document.getElementById(‘test’).setAttribute(‘clicked’, ‘true’);
ev.preventDefault();
alert(‘Hola mundo’);
}
},false)
};
Supongo que habrá otras formas más elegantes de hacer lo mismo.
Saludos