Bloque de datos desconocido

Etiqueta <script> con información desconocida


Si has visto el artículo de la etiqueta <script>, habrás observado que al atributo type le podemos indicar varios valores, entre los que se encuentran module, importmap o incluso omitir el atributo. En cada uno de ellos, las etiquetas <script> se comportan de una forma diferente.

Sin embargo, existe la posibilidad de indicar un valor type diferente a los anteriores, en cuyo caso, el navegador simplemente entenderá que la etiqueta <script> es un contenedor de datos de texto con un formato específico, pero que el navegador desconoce.

Para entenderlo mejor, veamos algunos ejemplos.

Ejemplo 1: Bloque JSON

En este fragmento de código podemos ver un fichero JSON que se indica en una etiqueta <script> que tiene el atributo type a application/json, que es el MIME de los ficheros JSON:

<script id="info" type="application/json">
  {
    "name": "Manz",
    "role": "streamer"
  }
</script>

Los navegadores no están preparados para que las etiquetas <script> soporten ese valor type, por lo que entienden que el contenido está en un formato de texto que no pueden procesar, y ofrece una forma genérica al desarrollador de acceder a esos datos, mediante la propiedad .text de ese elemento Javascript.

Nosotros podemos aprovechar esta característica para trabajar con esta información con Javascript. Por ejemplo:

const script = document.querySelector("#info");
const data = JSON.parse(script.text);
console.log(data);

// ► {name: 'Manz', role: 'teacher'}

En este ejemplo, hemos localizado el elemento <script> desde Javascript y lo hemos parseado como un fichero .json, ya que es exactamente eso. Los bloques de datos nos permiten acceder a su contenido de texto mediante la propiedad script.text.

Ejemplo 2: Bloque con mapa

Otro ejemplo con un formato desconocido. Tenemos una etiqueta <script> con type con valor text/x-game-map. Este formato es un formato inventado donde tenemos los datos de un supuesto mapa de un juego:

<script id="map" type="text/x-game-map">
11111111111111
12000011100001
11010101110103
10010101000111
11000000101011
11101010000001
11111111111111
</script>

Los números 0 indican una celda vacía del mapa, mientras que el 1 es un muro, el 2 el jugador y el 3 la salida del nivel. Estos datos podríamos procesarlos con Javascript para luego construir nuestro mapa del juego.

Observa el siguiente fragmento de código Javascript. En él, hacemos lo siguiente:

const script = document.querySelector("#map");
const textWithoutSpaces = script.text.trim();
const linesMap = textWithoutSpaces.split("\n");
const map = linesMap.map(line => line.trim().split(""));

console.log(map);
  • Localizamos el elemento <script> y lo guardamos en script
  • Accedemos al contenido de texto del bloque de datos con script.text
  • Eliminamos los espacios al inicio y final del texto con trim()
  • Dividimos el texto por los \n (saltos de línea), es decir, por líneas
  • Por cada línea, eliminamos espacios y dividimos cada caracter

Al final, obtenemos un array de arrays, con toda la información del mapa:

(14) ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1']
(14) ['1', '2', '0', '0', '0', '0', '1', '1', '1', '0', '0', '0', '0', '1']
(14) ['1', '1', '0', '1', '0', '1', '0', '1', '1', '1', '0', '1', '0', '3']
(14) ['1', '0', '0', '1', '0', '1', '0', '1', '0', '0', '0', '1', '1', '1']
(14) ['1', '1', '0', '0', '0', '0', '1', '0', '1', '0', '1', '0', '1', '1']
(14) ['1', '1', '1', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', '1']
(14) ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1']

Esto podría ser interesante. Ahora vamos a añadir un poco de CSS para convertir los números en cuadrados de colores y darle tamaño, modificar el código anterior para hacerlo más simple, y añadir algunas lineas que gestionen los cursores del teclado:

<script id="map" type="text/x-game-map">
11111111111111
12000011100001
11010101110103
10010101000111
11000000101011
11101010000001
11111111111111
</script>
.map {
  display: grid;
  grid-template-columns: repeat(14, 50px);
  grid-auto-rows: 50px;

  .wall { background: darkred; }
  .floor { background: grey; }
  .me { background: deeppink; }
  .exit { background: lime; }
}
// Genera la celda
const cellHTML = type => `<div class="cell ${type}"></div>`;
const CELLTYPE = ["floor", "wall", "me", "exit"].map(cellHTML);

// Genera el mapa
const mapData = document.querySelector("#map").text.trim();
const mapHTML = mapData.replace(/[0-9]/g, cell => CELLTYPE[cell] || "");
document.body.insertAdjacentHTML("beforeend", `<div class="map">${mapHTML}</div>`);

// Direcciones posibles para moverse
const OFFSETS = { ArrowRight: 1, ArrowLeft: -1, ArrowDown: 14, ArrowUp: -14 };

// Mueve el jugador por la cuadricula
const moveTo = (me, direction) => {
  const offset = OFFSETS[direction];
  const map = me.parentElement.children;
  const target = map[[...map].indexOf(me) + offset];
  if (target?.classList.contains("exit")) { alert("¡Ganaste!"); }
  if (target?.classList.contains("floor")) {
    me.classList.replace("me", "floor");
    target?.classList.replace("floor", "me");
  }
}

// Gestiona los cursores
addEventListener("keydown", (ev) => {
  const player = document.querySelector(".me");
  if (Object.keys(OFFSETS).includes(ev.key)) {
    moveTo(player, ev.key);
    ev.preventDefault();
  }
});

¿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