El event bubbling y event capturing se refiere a cuando un evento se propaga hacia sus elementos padre, o al revés y hacia sus elementos hijo
Eventos
Los eventos son la manera que tenemos de monitorizar el comportamiento de nuestros usuarios
Por ejemplo, detectar cuando se hace click en un botón
Y para implementar este control sobre los eventos, utilizamos los listeners
Por ejemplo
el.addEventListener(event, () => {})
Donde event
será algo tipo click
const el = document.getElementById('mi_boton')
el.addEventListener('click', () => {
console.log('elemento clicado!')
})
Bubbling y Capturing
Entendido esto, el bubbling y el capturing ocurren cuando tenemos listeners dentro de listeners
<div id="mi_div">
<button id="mi_boton_1">
Boton 1
<button id="mi_boton_2">Boton 2</button>
</button>
</div>
document.getElementById('mi_boton_1').addEventListener('click', () => console.log('boton 1 clicado!'))
document.getElementById('mi_boton_2').addEventListener('click', () => console.log('boton 2 clicado!'))
document.getElementById('mi_div').addEventListener('click', () => console.log('div clicado!'))
En este ejemplo, cuando clicamos el botón 1 o el botón 2 tenemos dos mensajes, el del button
y el del div
Y por qué no tenemos bubbling
dentro de los botones? Porque en html
no puedes meter un botón dentro de otro botón, por lo que el bubbling
ni está ni se le espera
Y si esto es el bubbling
, el capturing
es a la inversa
<div id="mi_div">
<button id="mi_boton_1">
Boton 1
<button id="mi_boton_2">Boton 2</button>
</button>
</div>
document.getElementById('mi_boton_1').addEventListener('click', () => console.log('boton1 clicado!'), { capture: true })
document.getElementById('mi_boton_2').addEventListener('click', () => console.log('boton2 clicado!'), { capture: true })
document.getElementById('mi_div').addEventListener('click', () => console.log('div clicado!'), { capture: true })
Y lo que hace es invertir el orden de los eventos (y en la práctica el de los console.log
)
-
Antes, teníamos primero el
click
del botón, y luego el deldiv
-
Ahora, tenemos primero el
click
deldiv
, y luego el delbutton
Cómo los paro? (React)
Y para pararlo?
Esto es algo que normalmente querremos hacer para evitar eventos descontrolados
Es tan sencillo como añadir un stopPropagation()
Pero mejor lo vemos en React
Aquí, en lugar de acceder al DOM
con el addEventListener
, utilizaremos la manera React a través del Virtual DOM
import React, { useRef } from 'react'
export const App = () => {
const divRef = useRef()
const buttonRef = useRef()
return (
<div ref={divRef} onClick={() => console.log('yo también!')}>
<h1>Hola</h1>
<button ref={buttonRef} onClick={() => console.log('clicando!')}>
clica!
</button>
</div>
)
}
export default App
Si queremos la versión capturing
en lugar de la bubbling
, en lugar de utilizar onClick
utilizaremos onClickCapture
Y si queremos evitarlo?
Añadimos el stopPropagation()
, un método del evento que recibimos por defecto en la función onClick
import React, { useRef } from 'react'
const log = (e, msg) => {
e.stopPropagation()
console.log(msg)
}
export const App = () => {
const div = useRef()
const button = useRef()
return (
<div ref={div} onClick={e => log(e, 'yo también!')}>
<h1>Hola</h1>
<button ref={button} onClick={e => log(e, 'clicando!')}>
clica!
</button>
</div>
)
}
export default App
Listos!
Lista de correo: escribo algo de tanto en cuanto