HTML: innerHTML vs outerHTML

Acceder al contenido de una etiqueta HTML


Es posible que necesitemos añadir contenido HTML a las etiquetas existentes que ya tenemos en el DOM. En el artículo anterior, vimos como añadir contenido de texto a nuestras etiquetas HTML, pero no como añadir contenido HTML. Para ello vamos a utilizar las propiedades y métodos que aprenderemos en este artículo.

Acceder al HTML del DOM

Antes de empezar, echemos un vistazo a las propiedades relacionadas que tenemos para esto:

Propiedades Descripción
.nodeType Devuelve un número del tipo de elemento. Sólo lectura.
Contenido HTML
.innerHTML Devuelve (o cambia) el contenido HTML del interior del elemento.
.outerHTML Idem a .innerHTML pero incluye también la propia etiqueta HTML.
.setHTMLUnsafe(html) Alternativa moderna a innerHTML. Permite Shadow DOM declarativo.

Vamos a ir viéndolas detalladamente para comprender su funcionamiento y características.

La propiedad .nodeType

La propiedad .nodeType no tiene relación directa con este tema, pero es una forma excelente de identificar el tipo de elemento con el que estamos trabajando:

const element = document.querySelector("div");

element.nodeType;    // 1

Ese valor numérico identifica el tipo de elemento con el que estamos trabajando. Los principales y más importantes, son estos tres, aunque existen algunos más que quizás veremos más adelante:

Valor Tipo de Elemento Descripción Ejemplo
1 Element.ELEMENT_NODE Un elemento HTML. <div>Hello!</div>
3 Element.TEXT_NODE Un nodo de texto. Hello!
8 Element.COMMENT_NODE Un comentario HTML. <!-- Hello! -->

Esto nos puede servir en algunos casos donde no sabemos que tipo de elemento tenemos y queremos que actúe en consecuencia, dependiendo de su tipo.

La propiedad .innerHTML

La propiedad .innerHTML nos permite acceder al contenido de un elemento, pero en lugar de devolver su contenido de texto como lo hace .textContent, esta propiedad nos devuelve su contenido HTML, es decir, su marcado HTML. Esto tiene varias implicaciones que explicaremos más adelante.

Primero, un ejemplo con la diferencia entre .textContent y .innerHTML:

const element = document.querySelector(".message");

element.innerHTML;    // "Mi nombre es <strong>Manz</strong>."
element.textContent;  // "Mi nombre es Manz."

Observa que .textContent se enfoca en obtener el contenido de texto, mientras que .innerHTML se enfoca en el contenido HTML.

De la misma forma que .textContent, también podemos usar .innerHTML para modificar el contenido. Ten en cuenta que el contenido HTML suministrado a .innerHTML se interpretará, mientras que el suministrado por .textContent se inserta literalmente como texto:

element.innerHTML = "<strong>Importante</strong>";    // Se lee "Importante" (en negrita)
element.textContent = "<strong>Importante</strong>";  // Se lee "<strong>Importante</strong>"

Ten mucho cuidado a la hora de insertar contenido HTML utilizando .innerHTML puesto que si añades contenido que provenga del usuario sin revisarlo, podrían insertar HTML que realice acciones dañinas como inyección de código malicioso.

Parseo de marcado HTML

Ten en cuenta que la propiedad .innerHTML comprueba y parsea el marcado HTML escrito (corrigiendo si hay errores) antes de realizar la asignación. Por ejemplo, observa el siguiente código:

const container = document.querySelector(".container");
container.innerHTML = "<strong>Manz";
container.innerHTML   // "<strong>Manz</strong>"

Aunque hemos insertado el HTML incompleto con .innerHTML, si examinamos el contenido, podemos ver que está completo. Esto ocurre porque el navegador parsea e intenta que el código HTML sea correcto en todo momento.

Esto puede provocar algunas incongruencias si el código es incorrecto o una disminución de rendimiento en porciones de código muy extensas o que hay que procesar múltiples veces.

La propiedad .outerHTML

La propiedad .outerHTML es muy similar a .innerHTML. Mientras este último devuelve el código HTML del interior de un elemento HTML, .outerHTML devuelve el código HTML desde el exterior, es decir, incluyendo al propio elemento implicado:

const data = document.querySelector(".data");
data.innerHTML = "<h1>Tema 1</h1>";

data.textContent;   // "Tema 1"
data.innerHTML;     // "<h1>Tema 1</h1>"
data.outerHTML;     // "<div class="data"><h1>Tema 1</h1></div>"

Al igual que hemos visto anteriormente, se puede utilizar .outerHTML asignando textos para modificar su contenido. Esto puede ser muy útil para reemplazar un elemento HTML, incluido el propio elemento HTML contenedor.

El método .setHTMLUnsafe()

El método .setHTMLUnsafe() es una alternativa moderna para la propiedad .innerHTML. Su utilización es muy similar, pero tiene como ventaja el soporte de una característica moderna llamada Shadow DOM declarativo.

Observa el siguiente fragmento de código:

const container = document.querySelector(".container");

const html = /* html */`
  <div>
    <template shadowrootmode="open">
      <style>h1 { color: red }</style>
      <h1>Interior</h1>
    </template>
  </div>
  <h1>Exterior</h1>
`;

// Opción 1)
container.setHTMLUnsafe(html);

// Opción 2)
// container.innerHTML = html; // No funciona con Shadow DOM declarativo

El código anterior crea un con código HTML con un <div> que tiene una plantilla con un <h1> y un código CSS que sólo afecta al interior de la plantilla. El método .setHTMLUnsafe() lo procesa y lo inserta en el interior del elemento .container. Si intentaramos hacer la misma operación con .innerHTML no funcionaría.

De la misma forma que tenemos un setter .setHTMLUnsafe() para modificar el contenido, tenemos un getter .getHTML() para obtener el contenido HTML, con la misma ventaja de ser compatible con Shadow DOM declarativo.

El texto unsafe del método es un recordatorio para el programador. Hace referencia a que el código insertado con este método puede ser inseguro, por lo que hay que tener precaución y no confiar en HTML ajeno.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev