Este post es una pequeña referencia de las funciones principales que podemos usar para manipular el DOM desde JavaScript, sin usar frameworks ni librerías externas.
¿Qué es el DOM?
El DOM es la representación de la página web en la memoria del navegador, a la que podemos acceder a través de JavaScript. El DOM es un árbol donde cada nodo es un objeto con todas sus propiedades y métodos que nos permiten modificarlo. Estas son algunas funciones que nos permiten acceder y modificar los elementos del DOM:
Acceso a elementos del DOM
// Obtiene un elemento por id
document.getElementById('someid');
// Obtinee una lista con los elementos que tienen esa clase
document.getElementsByClassName('someclass');
// Obtiene una HTMLCollection con los todos los elementos 'li'
document.getElementsByTagName('LI');
// Devuelve el primer elemento del documento que cumpla la selección (la notación es como en CSS)
document.querySelector('.someclass');
// Devuelve una lista de elementos que cumplen con la selección (notación como en CSS)
document.querySelectorAll('div.note, div.alert');
Acceder a hijos/padres de un elemento
// Obtener los hijos de un elemento
varelem
=
document.getElementById('someid');
varhijos
=
elem.childNodes;
// Su nodo padre
varpadre
=
elem.parentNode;
Crear nuevos elementos en el DOM
// Para crear elementos llamamos a createElement con el nombre del elemento
varnuevoH1
=
document.createElement('h1');
varnuevoParrafo
=
document.createElement('p');
// Crear nodos de texto para un elemento
vartextoH1
=
document.createTextNode('Hola mundo!');
vartextoParrafo
=
document.createTextNode('lorem ipsum...');
// Añadir el texto a los elementos
nuevoH1.appendChild(textoH1);
nuevoParrafo.appendChild(textoParrafo);
// también podemos asignar directamente el valor a la propiedad innerHTML
nuevoH1.innerHTML=
textoH1
nuevoParrafo.innerHTML=
textoParrafo
// los elementos estarían listos para añadirlos al DOM, ahora mismo solo existen en memoria, pero no serán visibles hasta que no los añadamos a un elemento del DOM
Añadir elementos al DOM
// seleccionamos un elemento
varcabecera
=
document.getElementById('cabecera');
// Añadir elementos hijos a un elemento
cabecera.appendChild(nuevoH1);
cabecera.appendChild(nuevoParrafo);
// También podemos añadir elementos ANTES del elemento seleccionado
// Tomamos el padre
varpadre
=
cabecera.parentNode;
// Insertamos el h1 antes de la cabecera
padre.insertBefore(nuevoH1,cabecera);
También podemos añadir directamente un trozo de HTML antes o después de un elemento del DOM, supongamos que tenemos estos elementos en la página:
<divid='box1'>
<p>
aquí algo de texto
</p>
</div>
<divid='box2'>
<p>
otro parrafo bla bla bla
</p>
</div>
Podemos hacer:
varbox2
=
document.getElementById('box2');
box2.insertAdjacentHTML('beforebegin','<div><p>un parrafo nuevo.</p></div>');
// beforebegin - El nuevo HTML es insertado justo antes del elemento, a la misma altura (hermano).
// afterbegin - El nuevo HTML se inserta dentro del elemento, antes del primer hijo.
// beforeend - El nuevo HTML se inserta dentro del elemento, después del último hijo.
// afterend - El nuevo HTML es insertado justo después del elemento, a la misma altura (hermano).
Añadir/eliminar/modificar Clases
// Tomamos un elemento
varcabecera
=
document.getElementById('cabecera');
// elimina una clase del elemento
cabecera.classList.remove('foo');
// Añade una clase si no existe
cabecera.classList.add('otra');
// añade o elimina varias clases a la vez
cabecera.classList.add('foo','bar');
cabecera.classList.remove('foo','bar');
// Si la clase existe la elimina, si no existe, la crea
cabecera.classList.toggle('visible');
// Devuelve true si el elemento contiene esa clase
cabecera.classList.contains('foo');
Por otro lado, el manejo de eventos permite interactuar con el usuario dando más dinamismo a la página.
Podemos asociar un manejador de eventos de nivel 1 a un elemento de diferentes formas:
Asociación de eventos a elementos como atributo HTML
Como podemos ver lo que hacemos es añadir un atributo XHTML al elemento con el mismo nombre del evento. El contenido del atributo son las instrucciones que se ejecutarán cuando se produzca el evento, es decir, cuando el usuario click sobre el botón. Este método no suele utilizarse debido a que mezcla la lógica de la programación JavaScript con el marcado XHTML con las consecuencias que eso conlleva.
Utilizando este método es posible acceder al elemento que ha desencadenado el evento a través de la variable this. <input
type="button" value="Pulsa" onclick="alert('Hola mundo');" />
<input type="button" value="Pulsa
" onclick="this.value='Pulsado';" />
Asociación de eventos a elementos como atributo HTML pasándole una función Javascript
De esta forma se consigue mejorar la anterior forma de asociar manejadores, dado que, si tuviéramos que ejecutar mucho código JavaScript en respuesta, podemos escribirlo todo en una función externa y llamarla.
El inconveniente de utilizar esta forma de asociar manejadores de eventos como funciones externas es que no podemos acceder al elemento que ha desencadenado el evento a través de la variable this (ejemplo 1). Aun así, podemos pasarle la variable this como argumento de la función (Ejemplo 2).
function holaMundo() {
alert('Hola Mundo.');
}
<input type="button" value="Pulsa" onclick="holaMundo()" />
function holaMundo(elemento) {
alert('Hola Mundo.'
);
elemento.value='Pulsado';
}
<input type="button" value="Pulsa" onclick="holaMundo(this)" />
Asociación de eventos a través de los manejadores de eventos separando los contenidos de la programación JavaScript.
Por último, la mejor forma es asociar los manejadores de eventos separando los contenidos de la programación JavaScript. Para eso asignas un id o un class al elemento que quieras que desencadene un evento. Después lo seleccionas utilizando las funciones de la API de DOM que hemos visto y asignas una función manejadora al evento.
Podemos asignar una función manejadora anónima o el nombre de la función que queremos que se ejecute cuando se produzca el evento.
document.getElementById('boton').onclick = function () { alert('Hola Mundo.'); }
<input type="button" value="Pulsa" id="boton" />
El inconveniente que tenemos con este método es que siempre que tratemos con el DOM tenemos que esperar a que el árbol de nodos se construya. Para asegurarnos que siempre se ejecutará una vez se haya construido el árbol de nodos podemos encerrar todo el código que haga uso de DOM en una función que se ejecute cuando se cargue la página, utilizando el evento
onload:window.onload = function{ //TODO EL CODIGO }
El flujo de eventos
Si vemos la estructura en árbol de los nodos nos damos cuenta que los elementos hijos están contenidos dentro de un padre. Si hacemos click por ejemplo en un enlace que está contenido dentro de un párrafo estaremos desencadenando el evento onclick en dos elementos del documento.
Ejemplo de evento
<html onclick="evento()">
<head>
<title>Event bubbling
</title>
</head>
<body onclick="evento()">
<p onclick="evento()">
<a href="#" onclick="evento()">Dispara el evento</a> </p>
</body>
</html>
El flujo de eventos establece el orden en que se ejecutan los eventos y como ya te puedes imaginar, suele ser diferente en cada navegador.
Event bubbling: En este modelo de flujo de eventos se produce primero el evento en el elemento más interno de la estructura de árbol y va subiendo jerárquicamente hasta llegar al nodo raíz.
En este ejemplo se ejecutaría primero el evento del enlace, seguido del evento del párrafo, seguido del evento del body y por último el evento del documento html.
Event capturing: En este modelo de flujo de eventos se prodce primero el evento del elemento más externo de la estructura de árbol y va bajando jerárquicamente hasta llegar al elemento más interno. En el mismo ejemplo de antes se ejecutaría primero el evento del documento html, seguido del evento del elemento body, seguido del evento del párrafo y por último el evento del enlace.
El flujo de eventos del DOM: El flujo de eventos definido en el estandar del DOM soporta los dos modelos anteriores, pero el event capturing se ejecuta en primer lugar, seguido del event bubbling. Además, incluyen el objeto window del BOM.
El modelo de eventos de DOM level 2
Ejemplo Modelo eventos DOM nivel 2
function holaMundo(){ alert('Hola mundo.'); }
var boton = document.getElementById('boton');
boton.addEventListener('click', holaMundo, false);
boton.removeEventListener('click', holaMundo, false);
<input type="button" value="Pulsa" id="boton" />
La especificación de nivel 2 de DOM define dos métodos para asignar y quitar manejadores de eventos, addEventListener() y removeEventListener(). Estos dos métodos aceptan tres parámetros:
el tipo de evento que se quiere capturar sin el prefijo on.
el nombre de la función encargada de manejar el evento.
un booleano para indicar el tipo de flujo de eventos al que se aplica. Si es true se aplica el event capturing, si es false se emplea el event bubbling.
Vamos a ver un ejemplo para asignar y quitar manejadores de eventos:
El objeto event
Ejemplo Objeto event y sus propiedades
function holaMundo(event){
var evento = window.event || event;
alert('Hola Mundo.');
}
var boton = document.getElementById('boton');
boton.addEventListener('click', holaMundo, false);
Cuando un evento se produce, la funcion manejadora suele necesitar información sobre el evento que se ha producido. El objeto event es el objeto que representa el evento que se ha producido. Se crea automáticamente cuando se produce el evento y se destruye una vez se haya ejecutado su función manejadora. Para complicarnos las cosas aún más, algunas versiones de Internet Explorer no tienen acceso automático del objeto event, sino que se accede a él a través del objeto window.
Este objeto tiene un conjunto de propiedades con información sobre el evento producido, y como no, vuelta al dilema, ya que Internet Explorer implementa las mismas propiedades con nombres distintos. Así que solo vamos a ver las definidas por DOM, si se quiere desarrollar una solución cross-browser lo mejor es utilizar un framework como jQuery, ya que el mismo framework se encarga de lidiar con estos problema de incompatibilidades.
Os adjunto la tabla del libro de Javier Eguíluz con las propiedades especificadas por DOM:
Propiedad/Método | Devuelve | Descripción |
altKey | Boolean | Devuelve true si se ha pulsado la tecla ALT y false en otro caso |
bubbles | Boolean | Indica si el evento pertenece al flujo de eventos debubbling |
button | Número entero | El botón del ratón que ha sido pulsado. Posibles valores: 0 – Ningún botón pulsado 1 – Se ha pulsado el botón izquierdo2 – Se ha pulsado el botón derecho 3 – Se pulsan a la vez el botón izquierdo y el derecho 4 – Se ha pulsado el botón central 5 – Se pulsan a la vez el botón izquierdo y el central 6 – Se pulsan a la vez el botón derecho y el central 7 – Se pulsan a la vez los 3 botones |
cancelable | Boolean | Indica si el evento se puede cancelar |
cancelBubble | Boolean | Indica si se ha detenido el flujo de eventos de tipobubbling |
charCode | Número entero | El código unicode del carácter correspondiente a la tecla pulsada |
clientX | Número entero | Coordenada X de la posición del ratón respecto del área visible de la ventana |
clientY | Número entero | Coordenada Y de la posición del ratón respecto del área visible de la ventana |
ctrlKey | Boolean | Devuelve true si se ha pulsado la tecla CTRL y false en otro caso |
currentTarget | Element | El elemento que es el objetivo del evento |
detail | Número entero | El número de veces que se han pulsado los botones del ratón |
eventPhase | Número entero | La fase a la que pertenece el evento: 0 – Fase capturing 1 – En el elemento destino 2 – Fase bubbling |
isChar | Boolean | Indica si la tecla pulsada corresponde a un carácter |
keyCode | Número entero | Indica el código numérico de la tecla pulsada |
metaKey | Número entero | Devuelve true si se ha pulsado la tecla META y false en otro caso |
pageX | Número entero | Coordenada X de la posición del ratón respecto de la página |
pageY | Número entero | Coordenada Y de la posición del ratón respecto de la página |
preventDefault() | Función | Se emplea para cancelar la acción predefinida del evento |
relatedTarget | Element | El elemento que es el objetivo secundario del evento (relacionado con los eventos de ratón) |
screenX | Número entero | Coordenada X de la posición del ratón respecto de la pantalla completa |
screenY | Número entero | Coordenada Y de la posición del ratón respecto de la pantalla completa |
shiftKey | Boolean | Devuelve true si se ha pulsado la tecla SHIFT y false en otro caso |
stopPropagation() | Función | Se emplea para detener el flujo de eventos de tipobubbling |
target | Element | El elemento que origina el evento |
timeStamp | Número | La fecha y hora en la que se ha producido el evento |
type | Cadena de texto | El nombre del evento |
Para una introducción más completa en forma de tutorial puedes ir a
Fuentes diversas