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 enscript
- 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();
}
});