034.4 Lección 1
Certificación: |
Conceptos básicos de desarrollo web |
---|---|
Versión: |
1.0 |
Tema: |
034 Programación JavaScript |
Objetivo: |
034.4 Manipulación JavaScript del contenido y estilo del sitio web |
Lección: |
1 de 1 |
Introducción
HTML, CSS y JavaScript son tres tecnologías distintas que se unen en la Web. Para crear páginas verdaderamente dinámicas e interactivas, el programador de JavaScript debe combinar componentes de HTML y CSS en tiempo de ejecución, una tarea que se facilita enormemente mediante el uso del Document Object Model (DOM).
Interactuar con el DOM
El DOM es una estructura de datos que funciona como una interfaz de programación para el documento, donde cada aspecto del documento se representa como un nodo en el DOM y cada cambio realizado en el DOM repercutirá inmediatamente en el documento. Para mostrar cómo usar DOM en JavaScript, guarde el siguiente código HTML en un archivo llamado example.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first">
<p>The dynamic content goes here</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section</p>
</div><!-- #content_second -->
</body>
</html>
El DOM estará disponible solo después de que se cargue el HTML, así que escriba el siguiente código JavaScript al final del cuerpo de la página (antes de la etiqueta final </body>
):
<script>
let body = document.getElementsByTagName("body")[0];
console.log(body.innerHTML);
</script>
El objeto document
es el elemento DOM superior, todos los demás elementos se derivan de él. El método getElementsByTagName()
lista todos los elementos que descienden de document
que tienen el nombre de etiqueta dado. Aunque la etiqueta body
se usa solo una vez en el documento, el método getElementsByTagName()
siempre devuelve una colección similar a un arreglo de elementos encontrados, de ahí el uso del índice [0]
para devolver el primero (y único) elemento encontrado.
Contenido HTML
Como se muestra en el ejemplo anterior, el elemento DOM devuelto por document.getElementsByTagName("body")[0]
se asignó a la variable body
. La variable body
se puede usar para manipular el elemento del cuerpo de la página, ya que hereda todos los métodos y atributos DOM de ese elemento. Por ejemplo, la propiedad innerHTML
contiene el código de marcado HTML completo escrito dentro del elemento correspondiente, por lo que puede usarse para leer el marcado interno. Nuestra llamada console.log(body.innerHTML)
imprime el contenido dentro de <body></body>
en la consola web. La variable también se puede usar para reemplazar ese contenido, como en body.innerHTML = "<p>Contenido borrado</p>"
.
En lugar de cambiar porciones enteras del marcado HTML, es más práctico mantener inalterada la estructura del documento y simplemente interactuar con sus elementos. Una vez que el navegador representa el documento, todos los elementos son accesibles mediante métodos DOM. Es posible, por ejemplo, listar y acceder a todos los elementos HTML usando la cadena especial *
en el método getElementsByTagName()
del objeto document
:
let elements = document.getElementsByTagName("*");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Este código colocará todos los elementos que se encuentran en document
en la variable elements
. La variable elements
es un objeto similar a un arreglo, por lo que podemos iterar a través de cada uno de sus elementos con un ciclo for
. Si la página HTML donde se ejecuta este código tiene un elemento con un atributo id
establecido en content_first
(consulte la página HTML de muestra que se muestra al comienzo de la lección), la declaración if
coincide con ese elemento y su contenido de marcado se cambia a <p>New content</p>
. Tenga en cuenta que se puede acceder a los atributos de un elemento HTML en el DOM utilizando la notación dot de las propiedades del objeto JavaScript: por lo tanto, element.id
se refiere al atributo id
del elemento actual del bucle for
. El método getAttribute()
también podría usarse, como en element.getAttribute("id")
.
No es necesario recorrer todos los elementos si desea inspeccionar solo un subconjunto de ellos. Por ejemplo, el método document.getElementsByClassName()
limita los elementos coincidentes a aquellos que tienen una clase específica:
let elements = document.getElementsByClassName("content");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Sin embargo, iterar a través de muchos elementos del documento usando un bucle no es la mejor estrategia cuando tiene que cambiar un elemento específico en la página.
Seleccionar elementos específicos
JavaScript proporciona métodos optimizados para seleccionar el elemento exacto en el que desea trabajar. El ciclo anterior podría ser reemplazado por completo por el método document.getElementById()
:
let element = document.getElementById("content_first");
element.innerHTML = "<p>New content</p>";
Cada atributo id
en el documento debe ser único, por lo que el método document.getElementById()
devuelve solo un objeto DOM. Incluso la declaración de la variable element
se puede omitir, porque JavaScript nos permite encadenar métodos directamente:
document.getElementById("content_first").innerHTML = "<p>New content</p>";
El método getElementById()
es el método preferible para localizar elementos en el DOM, porque su rendimiento es mucho mejor que los métodos iterativos cuando se trabaja con documentos complejos. Sin embargo, no todos los elementos tienen un ID explícito, y el método devuelve un valor null si ningún elemento coincide con el ID proporcionado (esto también evita el uso de funciones o atributos encadenados, como el innerHTML
utilizado en el ejemplo anterior). Además, es más práctico asignar atributos de ID solo a los componentes principales de la página y luego usar selectores CSS para ubicar sus elementos secundarios.
Los selectores, presentados en una lección anterior sobre CSS, son patrones que coinciden con elementos del DOM. El método querySelector()
devuelve el primer elemento coincidente en el árbol del DOM, mientras que querySelectorAll()
devuelve todos los elementos que coinciden con el selector especificado.
En el ejemplo anterior, el método getElementById()
selecciona el elemento que lleva el ID content_first
. El método querySelector()
puede realizar la misma tarea:
document.querySelector("#content_first").innerHTML = "<p>New content</p>";
Debido a que el método querySelector()
usa la sintaxis del selector, la ID proporcionada debe comenzar con un caracter hash. Si no se encuentra ningún elemento coincidente, el método querySelector()
devuelve null.
En el ejemplo anterior, todo el contenido del div content_first
se reemplaza por la cadena de texto proporcionada. La cadena contiene código HTML, lo que no se considera una práctica recomendada. Debe tener cuidado al agregar marcado HTML codificado de forma rígida al código JavaScript, ya que los elementos de seguimiento pueden resultar difíciles cuando se requieren cambios en la estructura general del documento.
Los selectores no están restringidos al ID del elemento. El elemento interno p
se puede direccionar directamente:
document.querySelector("#content_first p").innerHTML = "New content";
El selector #content_first p
coincidirá solo con el primer elemento p
dentro del div #content_first
. Funciona bien si queremos manipular el primer elemento. Sin embargo, es posible que deseemos cambiar el segundo párrafo:
<div class="content" id="content_first">
<p>Don't change this paragraph.</p>
<p>The dynamic content goes here.</p>
</div><!-- #content_first -->
En este caso, podemos usar la pseudoclase :nth-child(2)
para que coincida con el segundo elemento p
:
document.querySelector("#content_first p:nth-child(2)").innerHTML = "New content";
El número 2
en p:nth-child(2)
indica el segundo párrafo que coincide con el selector. Vea la lección de selectores CSS para saber más sobre los selectores y cómo usarlos.
Trabajar con atributos
La capacidad de JavaScript para interactuar con DOM no se limita a la manipulación de contenido. De hecho, el uso más generalizado de JavaScript en el navegador es modificar los atributos de los elementos HTML existentes.
Digamos que nuestra página de ejemplo HTML original ahora tiene tres secciones de contenido:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first" hidden>
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third" hidden>
<p>Third section.</p>
</div><!-- #content_third -->
</body>
</html>
Es posible que desee hacer que solo uno de ellos sea visible a la vez, de ahí el atributo hidden
en todas las etiquetas div
. Esto es útil, por ejemplo, para mostrar solo una imagen de una galería de imágenes. Para que uno de ellos sea visible cuando se cargue la página, agregue el siguiente código JavaScript a la página:
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).removeAttribute("hidden");
La expresión evaluada por la declaración switch
devuelve aleatoriamente el número 0, 1 o 2. El selector de ID correspondiente se asigna a la variable content_visible
, que utiliza el método querySelector(content_visible)
. La llamada encadenada removeAttribute("hidden")
elimina el atributo hidden
del elemento.
El enfoque opuesto también es posible: todas las secciones podrían ser inicialmente visibles (sin el atributo hidden
) y el programa JavaScript puede asignar el atributo hidden
a cada sección excepto a la de content_visible
. Para hacerlo, debe iterar a través de todos los elementos div de contenido que son diferentes del elegido, lo cual se puede hacer usando el método querySelectorAll()
:
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
// Hide all content divs, except content_visible
for ( element of document.querySelectorAll(".content:not("+content_visible+")") )
{
// Hidden is a boolean attribute, so any value will enable it
element.setAttribute("hidden", "");
}
Si la variable content_visible
se estableció en #content_first
, el selector será .content:not(#content_first)
, que se lee como todos los elementos que tienen la clase content
excepto los que tienen el ID content_first
. El método setAttribute()
agrega o cambia atributos de elementos HTML. Su primer parámetro es el nombre del atributo y el segundo es el valor del atributo.
Sin embargo, la forma correcta de cambiar la apariencia de los elementos es con CSS. En este caso, podemos establecer la propiedad CSS display
en hidden
y luego cambiarla a block
usando JavaScript:
<style>
div.content { display: none }
</style>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
<script>
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).style.display = "block";
</script>
Las mismas buenas prácticas que se aplican a la mezcla de etiquetas HTML con JavaScript se aplican también a CSS. Por lo tanto, no se recomienda escribir propiedades CSS directamente en código JavaScript. La forma correcta de alternar el estilo visual es seleccionar una clase CSS predefinida para el elemento.
Trabajar con clases
Los elementos pueden tener más de una clase asociada, lo que facilita la escritura de estilos que se pueden agregar o eliminar cuando sea necesario. Sería agotador cambiar muchos atributos CSS directamente en JavaScript, por lo que puede crear una nueva clase CSS con esos atributos y luego agregar la clase al elemento. Los elementos DOM tienen la propiedad classList
, que se puede usar para ver y manipular las clases asignadas al elemento correspondiente.
Por ejemplo, en lugar de cambiar la visibilidad del elemento, podemos crear una clase CSS adicional para resaltar nuestro div content
:
div.content {
border: 1px solid black;
opacity: 0.25;
}
div.content.highlight {
border: 1px solid red;
opacity: 1;
}
Esta hoja de estilo agregará un borde negro delgado y semitransparencia a todos los elementos que tengan la clase content
. Solo los elementos que también tienen la clase highlight
serán completamente opacos y tendrán el borde rojo fino. Entonces, en lugar de cambiar las propiedades CSS directamente como lo hicimos antes, podemos usar el método classList.add("highlight")
en el elemento seleccionado:
// Which content to highlight
let content_highlight;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_highlight = "#content_first";
break;
case 1:
content_highlight = "#content_second";
break;
case 2:
content_highlight = "#content_third";
break;
}
// Highlight the selected div
document.querySelector(content_highlight).classList.add("highlight");
Todas las técnicas y ejemplos que hemos visto hasta ahora se realizaron al final del proceso de carga de la página, pero no se limitan a esta etapa. De hecho, lo que hace que JavaScript sea tan útil para los desarrolladores web es su capacidad para reaccionar a los eventos en la página, que veremos a continuación.
Controladores de eventos
Todos los elementos visibles de la página son susceptibles a eventos interactivos, como el clic o el movimiento del mouse. Podemos asociar acciones personalizadas a estos eventos, lo que amplía enormemente lo que puede hacer un documento HTML.
Probablemente el elemento HTML más obvio que se beneficia de una acción asociada es el elemento button
. Para mostrar cómo funciona, agregue tres botones sobre el primer elemento div
de la página de ejemplo:
<p>
<button>First</button>
<button>Second</button>
<button>Third</button>
</p>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
Los botones no hacen nada por sí mismos, pero suponga que desea resaltar el div
correspondiente al botón presionado. Podemos usar el atributo onClick
para asociar una acción a cada botón:
<p>
<button onClick="document.getElementById('content_first').classList.toggle('highlight')">First</button>
<button onClick="document.getElementById('content_second').classList.toggle('highlight')">Second</button>
<button onClick="document.getElementById('content_third').classList.toggle('highlight')">Third</button>
</p>
El método classList.toggle()
agrega la clase especificada al elemento si no está presente, y elimina esa clase si ya está presente. Si ejecuta el ejemplo, notará que se puede resaltar más de un div
al mismo tiempo. Para resaltar solo el div
correspondiente al botón presionado, es necesario eliminar la clase highlight
de los otros elementos div
. No obstante, si la acción personalizada es demasiado larga o involucra más de una línea de código, es más práctico escribir una función aparte de la etiqueta del elemento:
function highlight(id)
{
// Remove the "highlight" class from all content elements
for ( element of document.querySelectorAll(".content") )
{
element.classList.remove('highlight');
}
// Add the "highlight" class to the corresponding element
document.getElementById(id).classList.add('highlight');
}
Como en los ejemplos anteriores, esta función se puede colocar dentro de una etiqueta <script>
o en un archivo JavaScript externo asociado con el documento. La función highlight
primero elimina la clase highlight
de todos los elementos div
asociados con la clase content
, luego agrega la clase highlight
al elemento elegido. Cada botón debería llamar a esta función desde su atributo onClick
, usando el ID correspondiente como argumento de la función:
<p>
<button onClick="highlight('content_first')">First</button>
<button onClick="highlight('content_second')">Second</button>
<button onClick="highlight('content_third')">Third</button>
</p>
Además del atributo onClick
, podríamos usar el atributo onMouseOver
(que se activa cuando el dispositivo señalador se usa para mover el cursor sobre el elemento), el atributo onMouseOut
(que se activa cuando el dispositivo señalador ya no está contenido en el elemento), etc. Además, los controladores de eventos no están restringidos a botones, por lo que puede asignar acciones personalizadas a estos controladores de eventos para todos los elementos HTML visibles.
Ejercicios guiados
-
Usando el método
document.getElementById()
, ¿cómo podría insertar la frase “Dynamic content” en el contenido interno del elemento cuyo ID esmessage
? -
¿Cuál es la diferencia entre hacer referencia a un elemento por su ID usando el método
document.querySelector()
y hacerlo a través del métododocument.getElementById()
? -
¿Cuál es el propósito del método
classList.remove()
? -
¿Cuál es el resultado de usar el método
myelement.classList.toggle("active")
simyelement
no tiene la claseactive
asignada?
Ejercicios de exploración
-
¿Qué argumento del método
document.querySelectorAll()
hará que imite el métododocument.getElementsByTagName("input")
? -
¿Cómo se puede utilizar la propiedad
classList
para listar todas las clases asociadas con un elemento dado?
Resumen
Esta lección cubre cómo usar JavaScript para cambiar el contenido HTML y sus propiedades CSS usando DOM (Modelo de objetos de documento). Estos cambios pueden ser provocados por eventos de usuario, lo cual es útil para crear interfaces dinámicas. La lección abarca los siguientes conceptos y procedimientos:
-
Cómo inspeccionar la estructura del documento usando métodos como
document.getElementById()
,document.getElementsByClassName()
,document.getElementsByTagName()
,document.querySelector()
ydocument.querySelectorAll()
. -
Cómo cambiar el contenido del documento con la propiedad
innerHTML
. -
Cómo agregar y modificar los atributos de los elementos de la página con los métodos
setAttribute()
yremoveAttribute()
. -
La forma correcta de manipular las clases de elementos usando la propiedad
classList
y su relación con los estilos CSS. -
Cómo vincular funciones a eventos del mouse en elementos específicos.
Respuestas a los ejercicios guiados
-
Usando el método
document.getElementById()
, ¿cómo podría insertar la frase “Dynamic content” en el contenido interno del elemento cuyo ID esmessage
?Se puede hacer con la propiedad
innerHTML
:document.getElementById("message").innerHTML = "Dynamic content"
-
¿Cuál es la diferencia entre hacer referencia a un elemento por su ID usando el método
document.querySelector()
y hacerlo a través del métododocument.getElementById()
?La ID debe ir acompañada del caracter hash en las funciones que utilizan selectores, como
document.querySelector()
. -
¿Cuál es el propósito del método
classList.remove()
?Elimina la clase (cuyo nombre se da como argumento de la función) del atributo
class
del elemento correspondiente. -
¿Cuál es el resultado de usar el método
myelement.classList.toggle("active")
simyelement
no tiene la claseactive
asignada?El método asignará la clase
active
amyelement
.
Respuestas a los ejercicios de exploración
-
¿Qué argumento del método
document.querySelectorAll()
hará que imite el métododocument.getElementsByTagName("input")
?El uso de
document.querySelectorAll("input")
coincidirá con todos los elementosinput
de la página, al igual quedocument.getElementsByTagName("input")
. -
¿Cómo se puede utilizar la propiedad
classList
para listar todas las clases asociadas con un elemento dado?La propiedad
classList
es un objeto similar a un arreglo, por lo que se puede usar un buclefor
para recorrer todas las clases que contiene.