El JavaScript de una página va en etiquetas <script>. Dónde las pongas decide si el código funciona o falla, porque el navegador ejecuta cada script en cuanto lo encuentra, y en ese momento la parte de la página que viene después todavía no existe.
También te puede interesar
El orden, comprobado
Una página con un script en el <head>, otro al final del <body>, uno con defer y un módulo. Cada uno anota cuándo se ejecuta y si encuentra el párrafo #demo:
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Orden de los scripts</title>
<script>
window.registro = [];
function anota(t) { registro.push(t); }
anota("1. script normal en <head>: demo = " + document.getElementById("demo"));
</script>
<script src="diferido.js" defer></script>
<script src="modulo.js" type="module"></script>
</head>
<body>
<p id="demo">Párrafo</p>
<script>anota("2. script normal al final del <body>: demo = " + document.getElementById("demo").tagName);</script>
</body>
</html>salida1. script normal en <head>: demo = null 2. script normal al final del <body>: demo = P 3. defer: se ejecuta tras leer todo el HTML; demo = P 4. type=module: también diferido por omisión; demo = P
El script del <head> busca el párrafo y obtiene null: cuando se ejecuta, el navegador aún no ha llegado a esa línea del HTML. Todos los demás lo encuentran.
Lo que pasa si no lo tienes en cuenta
const boton = document.getElementById("boton");
const demo = document.getElementById("demo");
boton.addEventListener("click", () => {
demo.textContent = "Cambiando el contenido del párrafo";
});Este app.js, cargado con un <script src="app.js"> normal en el <head>:
salidaUncaught TypeError: Cannot read properties of null (reading 'addEventListener')
El botón todavía no existe, getElementById devuelve null y la primera línea que lo usa revienta. Es el error más común al empezar con JavaScript.
La solución: defer
<head> <meta charset="utf-8"> <title>Página de ejemplo</title> <script src="app.js" defer></script> </head> <body> <p id="demo">Párrafo</p> <button id="boton" type="button">Cambiar contenido</button> </body>
el DOM tras pulsar el botón<p id="demo">Cambiando el contenido del párrafo
Con defer, el archivo se descarga a la vez que se lee el HTML, pero no se ejecuta hasta que la página está entera. Así el script puede ir en el <head>, donde se encuentra sin buscarlo, y funciona.
| Atributo | Cuándo se ejecuta | Orden |
|---|---|---|
| ninguno | en cuanto lo encuentra; para la lectura de la página mientras | el del documento |
defer | con la página entera leída | el del documento |
async | en cuanto termina de descargarse | el que toque |
type="module" | como defer, por omisión | el del documento |
async es para scripts independientes que no tocan la página ni dependen de otros, como un contador de visitas. Para el código de tu web, defer, o type="module" si usas import.
onclick="myFunction()" en el HTML, y además el código HTML de los ejemplos no se veía: las etiquetas no estaban escapadas y el navegador las interpretaba en lugar de mostrarlas. En el ejemplo del archivo externo se definía miFuncion y el botón llamaba a myFunction. Hoy lo habitual es no mezclar JavaScript en el HTML y enlazar los eventos desde el script con addEventListener, como arriba.Todo lo de esta página se ejecutó en Chromium 154 sin pantalla, en un contenedor limpio; las salidas son las de esa ejecución.