Saltar al contenido principal
Logo de JSCamp

Optional Chaining

Aprende a usar el operador optional chaining (?.) para evitar errores al acceder a propiedades que pueden no existir

midudev

Más clases y directos en YouTube

Suscribirse

Optional Chaining

Cuando trabajamos con el DOM, a veces un elemento puede no existir. Si intentamos acceder a métodos o propiedades de un elemento que es null, obtenemos un error. El optional chaining (?.) nos ayuda a evitar estos errores.

El problema: Elementos que no existen

Imagina que intentamos seleccionar un botón que no está en la página:

<!DOCTYPE html><html lang="es">  <head>    <script type="module">      const button = document.querySelector('#boton-importante')      button.addEventListener('click', function () {        console.log('Click')      })    </script>  </head>  <body>    <h1>Mi página</h1>    <!-- ❌ No hay ningún botón con id="boton-importante" -->  </body></html>

❌ Error:

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')

¿Por qué falla?

const button = document.querySelector('#boton-importante')console.log(button) // null// Intentamos hacer null.addEventListener()button.addEventListener('click', function () {  /* ... */})// ❌ Error: No puedes llamar métodos en null

Solución tradicional: Verificar con if

La forma tradicional es verificar si el elemento existe antes de usarlo:

const button = document.querySelector('#boton-importante')if (button) {  button.addEventListener('click', function () {    console.log('Click en el botón')  })}

✅ Ahora funciona:

  • Si button existe → Se añade el evento
  • Si button es null → No hace nada (no hay error)

Ejemplo con múltiples verificaciones:

const button = document.querySelector('#boton-importante')if (button) {  button.addEventListener('click', function () {    console.log('Click')  })}const input = document.querySelector('#mi-input')if (input) {  input.addEventListener('input', function () {    console.log('Escribiendo...')  })}const form = document.querySelector('#mi-formulario')if (form) {  form.addEventListener('submit', function (event) {    event.preventDefault()  })}

Problema: Mucho código repetitivo con los if.

Optional Chaining: La solución moderna

El optional chaining (?.) es un operador que verifica automáticamente si algo existe antes de acceder a sus propiedades o métodos.

Sintaxis:

objeto?.propiedadobjeto?.metodo()

¿Cómo funciona?

const button = document.querySelector('#boton-importante')// Con optional chainingbutton?.addEventListener('click', function () {  console.log('Click')})

Comportamiento:

  1. Si button existe (no es null ni undefined) → Ejecuta addEventListener
  2. Si button es null o undefinedNo hace nada (no hay error)

Comparación: Con y sin optional chaining

Sin optional chaining:

const button = document.querySelector('#boton-importante')if (button) {  button.addEventListener('click', function () {    console.log('Click')  })}

Con optional chaining:

const button = document.querySelector('#boton-importante')button?.addEventListener('click', function () {  console.log('Click')})

Resultado: Exactamente el mismo comportamiento, pero con menos código.

Optional chaining con propiedades

No solo funciona con métodos, también con propiedades:

const button = document.querySelector('#boton-importante')// Acceder a propiedadesconsole.log(button?.textContent) // undefined si no existeconsole.log(button?.id) // undefined si no existeconsole.log(button?.disabled) // undefined si no existe// Cambiar propiedadesbutton?.classList.add('activo')button?.setAttribute('data-applied', 'true')

Optional chaining encadenado

Puedes usar ?. múltiples veces en cadena:

const container = document.querySelector('#container')// Acceder a propiedades anidadas de forma seguraconst width = container?.firstChild?.getBoundingClientRect()?.widthconsole.log(width) // undefined si cualquier parte es null

Sin optional chaining:

let widthif (container) {  if (container.firstChild) {    const rect = container.firstChild.getBoundingClientRect()    if (rect) {      width = rect.width    }  }}console.log(width)