handHOME

Event Bubbling y el descontrol en JavaScript | React

Para entender el concepto de bubbling, hay que hablar antes de los eventos

Los eventos son la manera que tenemos de monitorizar el comportamiento de nuestros usuarios, por ejemplo al detectar cuando se hace click en un botón

Para capturar esos eventos se utilizan los listeners, con la siguiente estructura

el.addEventListener(event, () => {})

Donde en este caso el event será del tipo click

const el = document.getElementById('mi_boton')
el.addEventListener('click', () => {
  console.log('elemento clicado!')
})

Dicho esto, el bubbling y el capturing ocurre cuando esos eventos se propagan, por ejemplo aquí que tienes un botón dentro de otro botón y dentro de un div:

<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!'))

Si aprietas el botón 2, tendrás un evento del botón 2, luego uno del botón 1, y luego otro del div, y esto es el bubbling

En realidad no porque los botones no pueden estar anidados, pero se entiende el concepto

Lo contrario del bubbling es el capturing, es decir, es lo mismo pero el evento va en la dirección opuesta, nace en el elemento más general y se va propagando hasta llegar al elemento original

Eso solo ocurre si lo especificamos en el listener

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 })

Pero quién quiere esto? Ni idea, lo habitual es evitar el bubbling de la siguiente manera:

document.getElementById('mi_boton_1').addEventListener('click', e => {
  e.stopPropagation()
  console.log('boton 1 clicado!')
})
document.getElementById('mi_boton_2').addEventListener('click', e => {
  e.stopPropagation()
  console.log('boton 2 clicado!')
})
document.getElementById('mi_div').addEventListener('click', e => {
  e.stopPropagation()
  console.log('div clicado!')
})

Y si te preguntas cómo hacer esto en React, pues sería así

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
El texto está en blanco!
Enviado!