6. jQuery

A día de hoy, podemos asegurar que jQuery (http://jquery.com) es la librería JavaScript más utilizada ya que facilita mucho el trabajo del desarrollador. Su lema de write less, do more (escribe menos, haz más) resume su propósito.

6.1. Por qué usar jQuery

Entre sus características podemos destacar que:

  • es software libre y gratuito, lo que ha facilitado su implantación.

  • funciona en todos los navegadores modernos

  • abstrae las prestaciones que ofrece cada navegador, lo que permite que nuestro código se centre en el diseño más que en la plataforma de ejecución

  • se centra en simplificar las tareas de scripts comunes como:

    • manipular el contenido de un documento web

    • gestionar los eventos con independencia del navegador

    • añadir atractivos efectos y transiciones.

  • facilita los escenarios de trabajo más comunes como:

    • gestionar las configuraciones que se realizan al cargar una página

    • tras un evento, obtener y manipular/animar el contenido para volverlo a mostrar en la página.

  • aprovecha el conocimiento que ya poseemos de CSS, al utilizar la misma sintaxis de selectores

  • trabaja con conjuntos de elementos, permitiéndonos realizar una acción sobre más de un elemento DOM de manera simultánea

  • permite realizar múltiples operaciones sobre un conjunto de elementos mediante una única línea de código, mediante el encadenamiento de sentencias (statement chaining).

  • es extensible mediante multitud de plugins de terceros, o si queremos, mediante plugins de nuestra propia creación

6.2. Versiones

Si accedemos a la página de jQuery (http://jquery.com) podemos descargar dos versiones: la versión 1.12.1 y la 2.2.1. La principal diferencia es que la versión 2.x deja de dar soporte a las versiones 6, 7 y 8 de Internet Explorer.

Versiones jQuery
Figure 1. Versiones jQuery

Además, a la hora de descargar la versión en la que estemos interesados, podemos bajar uno de los dos siguientes scripts:

  • un script que no está comprimido y que nos permite consultar el código, pero que ocupa más (este es el que usaremos en el módulo)

  • el que está comprimido (minificado) y que se usa en producción para reducir la carga de la página.

Por ejemplo, si nos centramos en la versión 1.12.1, la versión de desarrollo ocupa 287KB mientras que la minificada sólo ocupa 95KB, es decir, casi tres veces más.

Si no queremos descargarla, podemos usar cualquiera de los CDNs que más nos interese:

  • jQuery: <script src="//code.jquery.com/jquery-1.12.1.min.js"></script>

  • Google: <script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.1/jquery.min.js"></script>

  • CDNJS: <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"></script>

Así pues, una vez elegida la versión que mejor nos convenga, la incluiremos dentro de nuestro código, teniendo en cuenta que deberíamos cargarla tras los archivos CSS y antes de todas las librerías que dependan de jQuery:

<link rel="stylesheet" type="text/css" href="estilos.css" />
<script src="jquery.js" />
<script src="libQueUsaJQuery.js" />
En todos los ejemplos que veremos en esta sesión y las posteriores damos por supuesto que se ha incluido correctamente la librería de jQuery.

6.3. Alias: jQuery por $

Así pues, uno de los puntos fuertes de jQuery es que reduce el número de instrucciones que usaríamos con JavaScript:

document.getElementById("miCapa").className = "resaltado";
jQuery("#miCapa").addClass("resaltado");

Además de ser más corto, jQuery nos permitirá acceder a un elemento sin hacerlo por su ID (por ejemplo, por su clase CSS, por el tipo de etiqueta HTML, …​) y filtrar los resultados del selector.

Para reducir el tamaño del código, en vez de utilizar todo el rato la función jQuery, se utiliza el alias $ que viene predefinido en la librería:

$("#miCapa").addClass("resaltado");

Así pues, tras el $, rodeado por paréntesis le pasaremos el selector del elemento sobre el que queremos trabajar, para posteriormente encadenar la acción a realizar sobre la selección.

Si utilizamos otras librerías JavaScript que también utilice el $, se recomienda realizar una llamada a $.noConflict() para evitar conflictos de namespace. Tras la llamada, el alias $ deja de estar disponible, obligándonos a escribir jQuery cada vez que escribiríamos $.

Permitiendo otro alias

Tal como hemos visto, es recomendable agrupar las instrucciones dentro de una función expresión de invocación inmediata. Si ademas queremos permitir que fuera de esta función se use el alias $ para usar otra librería JS como Mootools o Prototype, podemos hacer esto:

(function($) {	(1)
  // código jQuery que usa el $
}) (jQuery);
1 Si quisiéramos cambiar el alias del $ por otro carácter o cadena, en vez de pasar el $ como parámetro, podríamos pasarle, por ejemplo, JQ.

Otra manera es usando el evento document.ready que estudiaremos más adelante, el cual se lanza cuando la página ha cargado y recibe como argumento el objeto global jQuery, el cual podemos renombrar:

jQuery( document ).ready(function( $ ) {
  // código jQuery que usa el $
});

6.4. Seleccionando contenido

Sin duda, una de las operaciones que más vamos a realizar con jQuery es recuperar contenido, mediante selectores y filtros, sobre el cual posteriormente realizaremos alguna acción.

El resultado de aplicar un selector siempre va a ser un array de objetos (no son objetos DOM, sino objetos jQuery que envuelven a los objetos DOM añadiéndoles funcionalidad extra) que cumplen el criterio de la consulta. Posteriormente, podemos aplicar un filtro sobre un selector para refinar el array de resultados que devuelve.

6.4.1. Selectores

Los selectores y filtros que usa jQuery se basan en la sintaxis de CSS. Para aplicar un selector, le tenemos que pasar el selector como un parámetro entre comillas a la función jQuery, o mejor, a su alias $, del siguiente modo:

$('selector');

Si queremos restringir el contexto de aplicación del selector para evitar realizar la búsqueda sobre el documento DOM completo, le podemos pasar el contexto como segundo parámetro:

$('selector', contexto);

Si el contexto que le pasamos contiene muchos elementos, cada elemento se utiliza como punto de inicio en la búsqueda del selector.

De este modo, obtendríamos resultados diferentes:

$('p:even'); (1)
$('p:even', $('.importante')); (2)
1 Obtiene los párrafos pares
2 Dentro de los elementos cuya clase es importante, obtendría los párrafos pares

Si únicamente queremos restringir la búsqueda a un elemento lo mejor es buscar el elemento por su id y pasarle como contexto el HTMLElement:

var contenido = document.getElementById("contenido");
$('p:even', contenido);
Hay que intentar minimizar las llamadas a DOM para mejorar el rendimiento. Si podemos sacar a una variable la referencia al elemento que queremos trabajar ahorraremos llamadas jQuery.
Básicos

A continuación tenemos los selectores que son exactamente igual que en CSS:

Selector Propósito Ejemplo

etiqueta

Encuentra todos los elementos de etiqueta

p

#identificador

Encuentra el elemento cuyo id es identificador

#contenido

.nombreClase

Encuentra todos los elementos cuyo class es nombreClase

.anuncio

etiqueta.nombreClase

Encuentra todos los elementos de tipo etiqueta cuyo class es nombreClase

div.anuncio

etiqueta#ident.nombreClase

Encuentra el elemento de tipo etiqueta cuyo id es ident y su class es nombreClase

div#banner.anuncio

*

Encuentra todos los elementos de la página

*

Para poder probar todos estos selectores vamos a basarnos en la siguiente página:

<!DOCTYPE html>
<html>
<head lang="es">
  <meta charset="UTF-8">
  <title>Selectores</title>
  <style>
  .a { color: red; }
  .b { color: gold; }
  </style>
</head>
<body>
  <ul id="listado">
    <li class="a">elemento 0</li>
    <li class="a">elemento 1</li>
    <li class="b">elemento 2</li>
    <li class="b">elemento 3</li>
  </ul>
  <p id="pa0" class="a" lang="es-AR">Párrafo 0</p>
  <p>Párrafo 1</p>
  <p id="pa2" class="b">Párrafo 2</p>
  <p id="pa3" lang="es-ES">Párrafo 3</p>
</body>
</html>

La cual se visualiza del siguiente modo:

Página de Plantilla para trabajar con Selectores
Figure 2. Página de Plantilla para trabajar con Selectores

Y ahora vamos a comparar como lo haríamos mediante DOM y como con jQuery (http://jsbin.com/modiko/6/edit?html,css,js,output) en los siguientes ejemplos:

Obtener todos los párrafos
document.getElementsByTagName("p");
$("p");
Obtener la etiqueta cuyo id sea listado
document.getElementById("listado");
$("#listado");
Obtener las etiquetas li cuya clase sea a:
var todos = document.getElementsByTagName("li");	(1)
var liClaseA = [];
for (var i = 0, tam = todos.length; i < tam; i++) {
  if (todos[i].className === "a") {
    liClaseA.push(todos[i]);
  }
}
$("li.a");	// jQuery
1 Mediante DOM tenemos que recuperar todas las etiquetas, recorrerlas como un array y seleccionar los elementos deseados
Obtener las etiquetas de clase b, pero solo si están dentro de un lista desordenada
var todos = document.getElementsByTagName("ul");	(1)
...
...
$("ul .b");	// jQuery
1 Mediante DOM tenemos que recuperar todas las etiquetas, recuperar sus hijos, nietos, etc…​ y ver si alguno tiene dicha clase
Jerárquicos

Además de los selectores básicos, podemos hacer consultas dependiendo de las relaciones jerárquicas o una serie de criterios comunes, mediante la siguiente sintaxis:

Selector Propósito Ejemplo

selector1, selector2, …​

Encuentra todos los selectores especificados

p, div

.clase1.clase2

Encuentra todos los elementos cuya class son clase1 y clase2

.anuncio.vip

padre > hijo

Encuentra todos los elementos hijo que son hijos directos del tipo padre

div > img

ascendiente descendiente

Encuentra todos los elementos descendiente contenidos dentro del tipo ascendiente

div img

anterior + posterior

Encuentra todos los elementos posterior que están después de anterior

img + p

anterior ~ hermanos

Encuentra todos los hermanos (siblings) que están tras anterior y que cumplen el selector de hermanos

div.carrusel ~ img

Y algunos ejemplos con estos selectores (http://jsbin.com/modiko/6/edit?html,css,js,output):

Obtener los párrafos y los elementos de lista cuya clase sea b:
$("p, li.b");
Obtener los elementos de lista cuya clase sea a y que sean descendientes de una lista desordenada:
$("ul li.a");
Obtener el primer párrafo que está tras una lista desordenada:
$("ul + p");	// párrafo 0
Obtener los párrafos que son hermanos posteriores del id listado:
$("#listado ~ p");	// párrafo 0

6.4.2. Filtros

Los filtros trabajan de manera conjunta con los selectores para ofrecer un control todavía más preciso a la hora de seleccionar elementos del documento.

Comienzan con :, y se pueden encadenar para crear filtros complejos, como por ejemplo:

$("#noticias tr:has(td):not(:contains('Java'))");
Básicos

Permiten refinar la selección con los elementos que cumplen condiciones relativas a la posición que ocupan o un determinado índice:

Filtro Propósito

:first

Selecciona sólo la primera instancia del conjunto devuelto por el selector

:last

Selecciona sólo la última instancia del conjunto devuelto por el selector

:even

Selecciona sólo los elementos pares del conjunto devuelto por el selector

:odd

Selecciona sólo los elementos impares del conjunto devuelto por el selector

:eq(n)

Selecciona los elementos situados en el índice n

:gt(n)

Selecciona los elementos situados detrás del índice n

:lt(n)

Selecciona los elementos situados antes del índice n

:header

Selecciona todos los elementos cabeceras (h1, h2, h3 ,…​)

:animated

Selecciona todos los elementos que están actualmente animados de alguna manera

:not(selector)

Selecciona los elementos que no cumplen el selector

Y algunos ejemplos con estos filtros (http://jsbin.com/hufido/1/edit?html,css,js,output):

Obtener el primer párrafo
$("p:first");
Obtener el último elemento cuya clase sea a
$(".a:last");
Obtener los párrafos pares (el primer párrafo es el 0, con lo cual es par)
$("p:even");
Obtener todos los párrafos menos los dos primeros
$("p:gt(1)");
Obtener todos los párrafos menos el segundo
$("p:not(p:eq(1))");
Basados en los Atributos

Permiten refinar la selección con los elementos que cumplen condiciones relativas al contenido de los atributos:

Filtro Propósito

[atrib]

Incluye los elementos que tienen el atributo atrib

[atrib=valor]

Incluye los elementos que tienen el atributo atrib con el valor valor

[atrib!=valor]

Incluye los elementos que tienen el atributo atrib y no tiene el valor valor

[atrib^=valor]

Incluye los elementos que tienen el atributo atrib y su valor comienza por valor

[atrib$=valor]

Incluye los elementos que tienen el atributo atrib y su valor termina con valor

[atrib*=valor]

Incluye los elementos que tienen el atributo atrib y su valor contiene valor

[filtroAtrib1][filtroAtrib2]

Incluye los elementos que cumplen todos los filtros especificados, es decir, filtroAtrib1 y filtroAtrib2

Y algunos ejemplos con estos filtros (http://jsbin.com/ziraqe/1/edit?html,css,js,output):

Obtener los párrafos que tienen alguna clase
$("p[class]");
Obtener el párrafo cuyo id sea pa2
$("p[id=pa2]");
Obtener los párrafos cuyo id comience por pa
$("p[id^=pa]");
Obtener los párrafos cuyo id comience por pa y que su atributo lang contenga es
$("p[id^=pa][lang*=es]");
Basados en el Contenido

Permiten refinar la selección con los elementos que cumplen condiciones relativas a su contenido:

Filtro Propósito

:contains(texto)

Incluye los elementos que contienen la cadena texto

:empty

Incluye los elementos vacíos, es decir, sin contenido

:has(selector)

Incluye los elementos que contienen al menos uno que cumple el selector

:parents

Incluye los elementos que son padres, es decir, que contienen al menos otro elemento, incluido texto

Y algunos ejemplos con estos filtros (http://jsbin.com/runojo/1/edit?html,css,js,output):

Obtener los párrafos que contienen el número 3
$("p:contains(3)");
Obtener todos los elementos que contienen el número 3
$(":contains(3)");	(1)
$("p:contains(3),li:contains(3)"); (2)
1 Selecciona los que contienen un 3, y sus padres, ya que por ejemplo, el elemento body contiene un 3 dentro de un párrafo, con lo cual no sería una solución correcta
2 Restringimos la búsqueda sobre los elementos que conocemos. Para hacerlo correctamente deberíamos seleccionar todos aquellos que fueran hijos
Obtener los párrafos que son padres
$("p:parent");
Obtener una lista desordenada donde haya algún elemento que sea de la clase b
$("ul:has(li[class=b])");
Basados en la Visibilidad

Permiten refinar la selección con los elementos que cumplen condiciones relativas a la propiedad visibility:

Filtro Propósito

:visible

Incluye los elementos visibles

:hidden

Incluye los elementos ocultos

Y un ejemplo:

Obtener todos los párrafos que éstan ocultos
$("p:hidden");
Basados en los Hijos

Permiten refinar la selección examinando la relación que cada elemento tiene con su elemento padre:

Filtro Propósito

:nth-child(índice)

Incluye los hijos número índice, numerados de 1 a n.

:nth-child(even)

Incluye los hijos pares

:nth-child(odd)

Incluye los hijos impares

:nth-child(ecuación)

Incluye los hijos cuya posición cumple la ecuación, por ejemplo, 2n o 3n+1

:first-child

Incluye los elementos que son el primer hijo

:last-child

Incluye los elementos que son el último hijo

:only-child

Incluye los elementos que son hijos únicos, es decir, no tienen hermanos

Y algunos ejemplos con estos filtros:

Obtener el tercer elemento de lista de una lista desordenada
$("ul li:nth-child(3)");
Obtener el último elemento de lista de una lista desordenada
$("ul li:last-child");
Basados en los Elementos del Formulario

Aunque se consideran como selectores de formulario, se expresan como filtros y permiten refina los campos dependiendo del tipo de elemento:

Selector Propósito

:input

Encuentra todos los input, select, textarea y elementos button

:text

Encuentra todos los elementos de dicho tipo

:password

:radio

:checkbox

:submit

:reset

:image

:button

:file

Y para realizar filtrados adicionales sobre los elementos, podemos usar los siguientes filtros de manera conjunta a los anteriores:

Filtro Propósito

:enabled

Incluye los elementos que están habilitados

:disabled

Incluye los elementos que están deshabilitados

:checked

Incluye los elementos que están marcados (radio y checkbox)

:selected

Incluye los elementos que están seleccionados (select)

Y algunos ejemplos con estos filtros:

Obtener todos los campos de un formulario, incluyendo los botones
$("form :input");
Obtener los campos de texto que están habilitados
$("form :text:enabled");
Obtener los campos de tipo checkbox que están marcados
$("form :checkbox:checked");

6.4.3. El objeto jQuery

Tras seleccionar la información mediante selectores y filtros obtendremos un objeto jQuery, el cual envuelve a uno o más elementos HTML.

A partir de este objeto vamos a poder acceder al resultado, iterar sobre él o encadenar nuevas sentencias.

Las propiedades y métodos que soporta un objeto jQuery son:

Propiedad / Método Propósito Devuelve

context

Devuelve el conjunto de elementos utilizado como contexto

HTMLElement

each(función)

Invoca la función para cada uno de los elementos buscados

jQuery

find(selector)

Obtiene elementos descendientes que cumplen el selector

jQuery

get() / toArray()

Devuelve un array con todos los elementos DOM del conjunto de resultado. Se usa cuando necesitamos trabajar con los objetos DOM en vez de los objetos propios de jQuery

HTMLElement[]

get(índice)

Obtiene un único elemento DOM que ocupa la posición índice del conjunto de resultados

HTMLElement

index(HTMLElement)

Obtiene el indice del HTMLElement especificado

number

index(jQuery)

Obtiene el indice del primer elemento del objeto jQuery

number

index(selector)

Obtiene el indice del primer elemento del objeto jQuery dentro del conjunto de elementos encontrados por el selector. Realiza la operación inversa a las anteriores

number

length / size()

Número de elementos del conjunto de resultados

number

selector

Devuelve el selector empleado

string

Destacar que los métodos index() son los complementarios a get(), de modo que en get() se indica un índice y se obtiene un HTMLElement, y el index() a partir del elemento se obtiene el índice.

Vamos a practicar estos métodos con algunos ejemplos:

Obtener el selector empleado
var parrafosImpares = $("p:odd");
console.log(parrafosImpares.selector); // "p:odd"
Obtener la cantidad de párrafos del documento
var numParrafos = $("p").length;
Obtener el primer elemento de la lista
$("li").get()[0];	// object HTMLLIElement -> objeto DOM
$("li").get(0);	// object HTMLLIElement -> objeto DOM
$("li:first-child");	// object Object -> objeto jQuery
Obtener la posición del elemento cuyo id es contenido
$("body *").index(document.getElementById("contenido"));
$("body *").index($("#contenido"));
$("#contenido").index("body *");
Obtener los elementos de lista que tienen la clase b
$("ul").find("li.b");
$("ul li.b");
Realizar una acción sobre todos los párrafos
$("p").each(function() {
  // Por ejemplo, ponerles un borde
  $(this).css("border","3px solid red");
});
De HTMLElement a jQuery

Si tenemos un objeto DOM y queremos crear un objeto jQuery, podemos hacerlo pasándolo como argumento a la función $. De este modo podemos incluir código que no usa jQuery e integrarlo de un modo sencillo.

var contenido = document.getElementById("contenido");
var contenidoJQuery = $(contenido);

6.4.4. Modificando la selección

Una vez seleccionada la información mediante selectores y filtros, vamos a poder modificarla añadiendo nuevos elementos al conjunto de resultados o restringiendo los resultados a un subconjunto menor.

Encadenando sentencias

Una de las características más potentes de jQuery es su habilidad de encadenar múltiples sentencias de manera conjunta para realizar varias operaciones en una única línea de código.

Para ello, tras usar el selector, mediante el operador . podemos añadir métodos que se irán encadenando unos detrás de otros:

$(selector).funcion1().funcion2().funcion3();

Al encadenar las sentencias, los métodos se ejecutan sobre el elemento modificado. Si queremos que dentro del chaining vuelva a trabajar con la selección inicial, podemos usar end().

$(this).siblings('button').removeAttr('disabled').end().attr('disabled','disabled');

Todos los métodos que veremos a continuación devuelven un objeto jQuery, para permitir el encadenamiento de sentencias.

Expandiendo la selección

El método add permite ampliar la selección añadiendo elementos al conjunto de resultados:

Método Propósito

add(selector)

Añade todos los elementos que cumplen el selector

add(selector, contexto)

Añade todos los elementos que cumplen el selector dentro del contexto

add(HTMLElement)

Añade un elemento o un array de HTMLElements

add(HTMLElement[])

add(jQuery)

Añade los contenidos del objeto jQuery

Por ejemplo, podemos obtener un selector para posteriormente reutilizarlo sobre otra selección:

var imgFlickr = $("img[src*=flickr]");
$("img:even").add("img[src*=twitpic]").add(imgFlickr);
Reduciendo la selección

Si lo que queremos es reducir el conjunto de resultados para refinar la selección, podemos utilizar los siguientes métodos similar a los empleados en los filtros:

Método Propósito

eq(índice)

Elimina todos los elementos a excepción al que ocupa la posición índice . Si el indice es negativo, cuenta desde el final.

first()

Elimina todos los elementos excepto el primero

has(selector)

Elimina los elementos que no tengan un descendiente que cumpla el selector o el objeto jQuery o aquellos descendientes que no incluyan los objetos HTMLElement especificados

has(jQuery)

has(HTMLElement)

has(HTMLElement[])

last()

Elimina todos los elementos excepto el último

slice(inicio, fin)

Elimina los elementos fuera del rango indicado por los valores de inicio y fin

Por ejemplo, mediante los operadores sencillos podemos restringir el conjunto de resultados (http://jsbin.com/hufoci/1/edit?html,css,js,output):

$("li").first();
$("li").last();
$("li").eq(1); // segundo elemento
$("li").eq(-1); // último elemento
$("li").slice(0,2); // primeros dos elementos

Otros métodos que permiten restringir el resultado son filter() y not(), los cuales son complementarios:

Método Propósito

filter(selector)

Mantiene todos los elementos que cumplen el selector

filter(HTMLElement)

Elimina todos los elementos menos el HTMLElement

filter(jQuery)

Elimina todos los elementos que no están contenidos en el objeto jQuery

filter(función(índice))

La función se invoca por cada elemento; aquellos en los que la función devuelva false se eliminarán

not(selector)

Elimina los elementos que cumplen el selector

not(HTMLElement[])

Elimina los elementos del array

not(HTMLElement)

Elimina el elemento especificado

not(jQuery)

Elimina los elementos contenidos en el objeto jQuery

not(función(índice))

La función se invoca por cada elemento; aquellos en los que la función devuelva true se eliminarán

Obtener los párrafos que tiene clase o que no la tienen
$("p").filter("[class]")
$("p").not("[class]");
Obtener los párrafos cuyo id comience por pa o los que no comiencen por pa
var pa = $("[id^=pa]");
$("p").filter(pa);
$("p").not(pa);
Obtener los párrafos cuya clase es a o el párrafo que ocupa la tercera posición
$("p").filter(function(indice) {
  return this.getAttribute("class") == "a" || indice == 2;
});

6.5. Navegando por la selección

Una vez tenemos la selección de elementos con la que queremos trabajar podemos navegar (traversing) desde la selección empleando las relaciones DOM entre los elementos.

6.5.1. Navegación descendiente

Para navegar descendentemente podemos emplear los siguientes métodos:

Método Propósito

children()

Selecciona los hijos (descendientes inmediatos) de todos los elementos del conjunto de resultados

children(selector)

Selecciona todos los elementos que cumplen el selector y que son hijos del conjunto de resultados

contents()

Devuelve los hijos, incluyendo los contenidos de texto u nodos de comentarios de todos los elementos

El uso de las relaciones DOM suele emplearse con el método find() (ver El objeto jQuery) para buscar un selector entre los descendientes de la selección. Además, en ambos casos no se devuelven elementos duplicados.

Por ejemplo, podemos recorrer todos los hijos de un selector de este modo:

var numHijos = $("ul").children().each(
  function(indice, elem) {
    console.log(elem.tagName + " - " + elem.className);
  }
).length;

6.5.2. Navegación ascendente

Si en cambio estamos interesados en los elementos que están por encima de nuestro conjunto de resultados usaremos los siguientes métodos:

Método Propósito

parent()

Selecciona el padre de cada elemento del objeto jQuery, pudiéndose filtrar por el selector

parent(selector)

parents()

Selecciona los ascendentes de cada elemento del objeto jQuery, pudiéndose filtrar por el selector

parents(selector)

parentsUntil(selector1)

Selecciona los ascendentes de cada elemento del objeto jQuery hasta que se encuentre una ocurrencia para el selector1. Los resultados se pueden filtrar mediante selector2.

parentsUntil(selector1, selector2)

parentsUntil(HTMLElement)

Selecciona los ascendentes de cada elemento del objeto jQuery hasta que se encuentre uno de los elementos especificados. Los resultados se pueden filtrar mediante un selector.

parentsUntil(HTMLElement, selector)

parentsUntil(HTMLElement[])

parentsUntil(HTMLElement[], selector)

closest(selector)

Selecciona el ascendente más cercano de cada elemento del objeto jQuery y realiza la intersección con el selector / contexto.

closest(selector, contexto)

closest(jQuery)

Selecciona el ascendente más cercano de cada elemento del objeto jQuery y realiza la intersección con los elementos contenidos en el parámetro.

closest(HTMLElement)

offsetParent()

Encuentra el ascendente posicionado más cercano (tiene valor fixed, absolute o relative en la propiedad position). Útil para trabajar con animaciones

Algunos ejemplos con estos métodos:

Obtener el padre de todos los enlaces - Obtener aquellos padres de enlaces que tienen el atributo lang
$("a").parent();
$("a").parent("[lang]");
Obtener los ascendentes (padre, abuelo, etc..) de los elementos cuya clase sea a
$(".a").parents();
Obtener los ascendentes hasta llegar a la etiqueta form de los elementos cuya clase sea a y que contenga el valor es en el atributo lang
$(".a").parentsUntil('form','[lang*=es]');
Obtiene los ascendentes más cercanos cuya clase sea a, de la imagen cuya fuente referencie a flickr
$("img[src*=flickr]").closest(".a");

6.5.3. Navegación horizontal

Finalmente, si lo queremos es navegar entre los elementos que están en el mismo nivel, podemos utilizar los siguientes métodos:

Método Propósito

siblings()

Selecciona todos los hermanos (anteriores y posteriores) para cada uno de los elementos del objeto jQuery.

next()

Selecciona el hermano inmediatamente posterior para cada elemento del objeto jQuery.

nextAll()

Selecciona todos los hermanos posteriores para cada elemento del objeto jQuery, pudiéndose filtrar por el selector

nextUntil(selector)

Selecciona los hermanos anteriores para cada elemento hasta (sin incluir) un elemento que cumpla el selector o un elemento del objeto jQuery o del array HTMLElement.

nextUntil(jQuery)

nextUntil(HTMLElement[])

prev()

Selecciona el hermano inmediatamente anterior para cada elemento del objeto jQuery.

prevAll()

Selecciona todos los hermanos anteriores para cada elemento del objeto jQuery.

prevUntil(selector)

Selecciona los hermanos anteriores para cada elemento hasta (sin incluir) un elemento que cumpla el selector o un elemento del objeto jQuery o del array HTMLElement.

prevUntil(jQuery)

prevUntil(HTMLElement[])

Todos estos métodos aceptan un segundo parámetro opcional con un selector para filtrar resultados.

6.6. Manipulando contenido

Una vez seleccionados los elementos con los que queremos trabajar mediante el uso de selectores y filtros, llega el momento de manipular su contenido.

En ocasiones crearemos contenido de manera dinámica, de modo que jQuery nos ofrece métodos para crear, copiar, borrar y mover contenido, además de permitir envolver contenido dentro de otro.

Además, jQuery ofrece soporte Cross-Browser para trabajar con CSS, incluyendo información sobre posicionamiento y tamaño.

6.6.1. Creando contenido

Para crear nuevo contenido, no tenemos más que pasarle una cadena con el código HTML a la función $()

var nuevoEncabezado = $("<h1>Encabezado Nivel 1</h1>");

También podemos hacerlo mediante los siguientes métodos:

Método Propósito

html()

Devuelve el contenido HTML del primer elemento del conjunto de resultados

html(nuevoContenido)

Asigna el nuevoContenido HTML a todos los elementos del conjunto de resultados

text()

Devuelve el contenido de todos los elemento del conjunto de resultados

text(nuevoTexto)

Asigna el nuevoTexto a todos los elementos del conjunto de resultados

La principal diferencia entre html(contenido) y text(contenido) es que si pasamos código HTML al método text(), éste escapará los símbolos de < y >.

Vamos a practicar estos métodos con algunos ejemplos:

console.log($("#listado").html());	// <li class="a">elemento 0.......elemento 3</li>
console.log($("#listado").text());	// elemento 0 elemento 1 ... elemento 3
console.log($("li").html());	// elemento 0
console.log($("li").text());	// elemento 0elemento 1elemento 2elemento 3
$("#listado").html("<li>Nuevo elemento mediante jQuery</li>");
console.log($("#listado").html());	// <li>Nuevo elemento mediante jQuery</li>
$("p:first").html("Este es el primer párrafo con <br /> en medio");
$("p:last").text("Este es el último párrafo con <br /> en medio");

Como podemos observar, al asignar nuevo contenido estamos eliminando el contenido previo y sustituyéndolo por el nuevo, pero manteniendo los atributos de la etiqueta sobre la que se añade el contenido.

Resultado de crear contenido
Figure 3. Resultado de crear contenido

6.6.2. Trabajando con valores

Una vez hemos seleccionado elementos que forman parte de un formulario, podremos obtener o modificar sus valores mediante el método val():

Método Propósito

val()

Devuelve el valor del primer elemento del conjunto de resultados

val(valor)

Asigna el valor a todos los elementos del conjunto de resultados

val(función)

Asigna valores a todos los elementos del conjunto de resultados mediante la función

Por ejemplo:

Mostrar el valor de todos los elementos de entrada:
$("input").each(function(indice, elem) {
  console.log("Nombre: " + elem.name + " Valor: " + $(elem).val());
});
Asignar el valor mediante una función
$("input").val(function(indice, valorActual) {
  return (indice + 1) * 100;
});

6.6.3. Trabajando con atributos

Para inspeccionar o modificar el valor de los atributos de los elementos usaremos el método attr.

Método Propósito

attr(nombre)

Accede a la propiedad del primero elemento del conjunto de resultados. Éste método facilita obtener el valor de propiedades. Si el elemento no tiene un atributo con dicho nombre, devuelve undefined

attr(objetoPropiedades)

Asigna una serie de atributos en todos los elementos del conjunto de resultado mediante la sintaxis de notación de objeto, lo que permite asignar un gran número de propiedades de una sola vez $("img").attr({ src:"/imagenes/logo.gif", title:"JavaUA", alt: "Logo JavaUA"});

attr(clave, valor)

Asigna valor a la propiedad clave a todos los elementos del conjunto de resultados

attr(clave, función)

Asigna una única propiedad a un valor calculado para todos los elementos del conjunto de resultados. En vez de pasar un valor mediante una cadena, la función devolverá el valor del atributo.

removeAttr(nombre)

Elimina el atributo nombre de todos los elementos del conjunto de resultados

Vamos a estudiar algunos ejemplos, suponiendo que tenemos un enlace con una imagen del siguiente modo:

<a href="imagenes/logo.jpg"><img src="imagenes/logo.jpg" alt="logo" /></a>
Abrir el enlace en una nueva ventana
$("a").attr("target","_blank");
Quitar el cuadrado que encubre la imagen al tratarse de un enlace
$("a").removeAttr("href");
Cambiar la ruta de la imagen y su texto alternativo
$("img").attr({src:"imagenes/batman.jpg", alt:"Batman"});

6.6.4. Insertando contenido

jQuery ofrece varios métodos para insertar nuevo contenido en el documento, tanto antes como después del contenido de los elementos de la página.

Método Propósito

append(contenido)

Anexa contenido (al final) dentro de cada elemento del conjunto de resultados

appendTo(selector)

Traslada todos los elementos del conjunto de resultados detrás de los encontrados por el selector

prepend(contenido)

Añade contenido (al inicio) dentro de cada elemento del conjunto de resultados

prependTo(selector)

Traslada todos los elementos del conjunto de resultados delante de los encontrados por el selector

El contenido de los métodos puede ser contenido HTML, un objeto jQuery, un array de HTMLElement o una función que devuelva el contenido.

Supongamos que partimos del siguiente fragmento:

Plantilla Ejemplos Modificar Contenido - http://jsbin.com/yobegu/2/edit?html,js,output
<p>Párrafo 1</p>
<p>Párrafo 2</p>
Añadir un texto al final de todos los párrafos
$("p").append(" con nuevo contenido anexado");
// <p>Párrafo 1 con nuevo contenido anexado</p>
// <p>Párrafo 2 con nuevo contenido anexado</p>
Añadir un texto al inicio de todos los párrafos
$("p").prepend("Nuevo contenido en el ");
// <p>Nuevo contenido en el Párrafo 1</p>
// <p>Nuevo contenido en el Párrafo 2</p>
Trasladar el último párrafo delante del primero
$("p:last").prependTo("p:first");
// <p>Párrafo 2</p>
// <p>Párrafo 1</p>
Autoevaluación

Suponiendo que tenemos una capa que contiene una capa con artículos ¿Qué realiza el siguiente código ? [1]

$('#articulo').find('span.co').each(function() {
  var $this = $(this);
  $('<blockquote></blockquote>', {
    class: 'co',
    text: $this.text()
  }).prependTo( $this.closest('p') );
});

Si queremos que el contenido se situe antes o después de ciertos elementos (y no dentro de los seleccionados), hemos de emplear los siguiente métodos:

Método Propósito

after(contenido)

Inserta el contenido detrás de cada elemento del conjunto de resultados

before(contenido)

Inserta el contenido antes de cada elemento del conjunto de resultados

insertAfter(selector)

Inserta todos los elementos del conjunto de resultados detrás de los encontrados por el selector

insertBefore(selector)

Inserta todos los elementos del conjunto de resultados delante de los encontrados por el selector

Añadir un nuevo párrafo detrás de cada uno de los existentes
$("p").after("<p>Párrafo separado</p>");
$("<p>Párrafo separado</p>").insertAfter("p");
// <p>Párrafo 1</p>
// <p>Párrafo separado</p>
// <p>Párrafo 2</p>
// <p>Párrafo separado</p>

Por ejemplo, si queremos mover un elemento una posición hacia abajo, dentro de after podemos crear una función anónima que devuelva el contenido a mover (además nos sirve para hacer un filtro):

$("p:eq(1)").after(function() {
  return $(this).prev();
});

Finalmente, si queremos duplicar el contenido usaremos el método clone():

Método Propósito

clone()

Clona los elementos del conjunto de resultados y selecciona los clonados

clone(bool)

Clona los elementos del conjunto de resultados y todos sus manejadores de eventos y selecciona los clonados (pasándole true)

Por ejemplo, podemos clonar un elemento existente para modificarlo a continuación:

var vip2 = $("#vip").clone();
vip2.attr("id","vip2");

6.6.5. Modificando el contenido

jQuery permite envolver contenido en la página, sustituir contenido, copiar y eliminarlo, mediante los siguientes métodos.

Si nos centramos en los métodos para envolver contenido, de manera que les añadiremos un padre, tenemos:

Método Propósito

wrap(html)

Envuelve cada elemento del conjunto de resultados con el contenido html especificado

wrap(elemento)

Envuelve cada elemento del conjunto de resultados con el elemento especificado

wrapAll(html)

Envuelve todos los elementos del conjunto de resultados con el contenido html especificado

wrapAll(elemento)

Envuelve todos los elementos del conjunto de resultados con un único elemento

wrapInner(html)

Envuelve los contenidos de los hijos internos de cada elemento del conjunto de resultados (incluyendo los nodos de texto) con una estructura html

wrapInner(elemento)

Envuelve los contenidos de los hijos internos de cada elemento del conjunto de resultados (incluyendo los nodos de texto) con un elemento DOM

Una caso particular de wrapAll() es el cual en el que la selección no comparte un padre común. En este caso, el nuevo elemento se inserta com padre del primer elemento seleccionado. A continuación, jQuery mueve el resto de elementos para que sean hermanos del primero.

Vamos a practicar estos métodos con algunos ejemplos (http://jsbin.com/gavipi/1/edit?html,js,output):

Envuelve cada párrafo con una capa de color rojo (tantas capas como párrafos)
$("p").wrap("<div style='color:red' />");
// <div style='color:red'><p>Párrafo 1</p></div>
// <div style='color:red'><p>Párrafo 2</p></div>
Envuelve todos los párrafos con una capa de color rojo (una capa que envuelve a todos los párrafos)
$("p").wrapAll("<div style='color:red' />");
// <div style='color:red'><p>Párrafo 1</p><p>Párrafo 2</p>...</div>
Envuelve el contenido de todos los párrafos con una capa de color rojo
$("p").wrapInner("<div style='color:red' />");
// <p><div style='color:red'>Párrafo 1</div></p>
// <p><div style='color:red'>Párrafo 2</div></p>

Si lo que queremos es sustituir o eliminar contenido, tenemos los siguientes métodos:

Método Propósito

replaceWith(contenido)

Sustituye todos los elementos del conjunto de resultados con el contenido especificado, ya sea HTML o un elemento DOM

replaceAll(selector)

Sustituye los elementos encontrados por el selector con los del conjunto de resultados, es decir, lo contrario a replaceWith.

empty()

Elimina todos los nodos hijos del conjunto de resultados

remove()

Elimina todos los elementos del conjunto de resultados del DOM

detach()

Igual que remove() pero devuelve los elementos eliminados. Esto permite volver a insertarlos en otra parte del documento

detach(selector)

unwrap()

Elimina el padre de cada uno de los elementos del conjunto de resultados, de modo que los elementos se convierten en hijos de sus abuelos.

Vamos a practicar estos métodos con algunos ejemplos:

Sustituye los párrafos (y su contenido) por capas
$("p").replaceWith("<div>Capa Nueva</div>");
$("<div>Capa Nueva</div>").replaceAll("p");
Vacía la lista desordenada (la etiqueta ul sigue en el documento)
$("ul").empty();
Elimina la lista desordenada (la etiqueta ul desaparece)
$("ul").remove();
var listaBorrada = $("ul").detach();

6.6.6. Trabajando con CSS

Los métodos jQuery facilitan el acceso y modificación de las propiedades CSS con código Cross-Browser, además de facilitar la información de posicionamiento y tamaño de los elementos de la página.

El método css() permite obtener y asignar estilos CSS a un conjunto de elementos modificando el valor del atributo style mediante valores a propiedades CSS:

Método Propósito

css(nombre)

Devuelve el valor de la propiedad CSS nombre del primer elemento del conjunto de resultados

css(propiedades)

Asigna las propiedades de cada elemento del conjunto de resultados mediante la sintaxis de objeto: var cssObj = {'background-color':'blue';'font-weight':'bold'}; $(this).set(cssObj);

css(propiedad, valor)

Asigna un valor a una única propiedad CSS. Si se pasa un número, se convertirá automáticamente a un valor de píxel, a excepción de: z-index, font-weight, opacity, zoom y line-height.

Algunos ejemplos del uso del método css():

Cambia el tamaño de los párrafos
var tamAnt = $("p").css("font-size");
$("p").css("font-size","1.5em");
$("p").css("font-size","+=5"); // 5 píxeles más
Cambiar tamaño y color de los párrafos
var valoresCSS = {
  "font-size": "1.5em",
  "color": "blue"
};
$("p").css("font-size", valoresCSS);

Además, jQuery también ofrece métodos para trabajar con las clases CSS y poder añadir, borrar, detectar o intercambiar sus clases:

Método Propósito

addClass(clase1 [clase2])

Añade la clase especificada a cada elemento del conjunto de resultados

hasClass(clase)

Devueve true si está presenta la clase en al menos uno de los elementos del conjunto de resultados

removeClass(clase [clase2])

Elimina la clase de todos los elementos del conjunto de resultados

toggleClass(clase [clase2])

Si no está presente, añade la clase. Si está presente, la elimina.

toggleClass(clase, cambio)

Si cambio es true añade la clase, pero la elimina en el caso de que cambio sea false.

Y algunos ejemplos de la manipulación de clases CSS (http://jsbin.com/tujuni/3/edit?html,css,js,console,output):

$("p").addClass("a");
$("p:even").removeClass("b").addClass("a");

console.log("En algún elemento:" + $("p").hasClass("a"));
$("p").each(function(indice, elem) {
  console.log("Elemento: " + $(elem).text() + " -> " + $(elem).hasClass("a"));
});

$("p").toggleClass("a");

Respecto al posicionamiento CSS, jQuery ofrece métodos que soportan Cross-Browser para averiguar la posición de los elementos:

Método Propósito

offset()

Obtiene el desplazamiento actual (en píxeles) del primer elemento del conjunto de resultados, respecto al documento

offsetParent()

Devuelve una colección jQuery con el padre posicionado del primer elemento del conjunto de resultados

position()

Obtiene la posiciones superior e izquierda de un elemento relativo al desplazamiento de su padre

scrollTop()

Obtiene el desplazamiento del scroll superior del primer elemento del conjunto de resultados

scrollTop(valor)

Asigna el valor del desplazamiento del scroll superior a todos los elementos del conjunto de resultados

scrollLeft()

Obtiene el desplazamiento del scroll superior del primer elemento del conjunto de resultados

scrollLeft(valor)

Asigna el valor del desplazamiento del scroll izquierdo a todos los elementos del conjunto de resultados

var pos = $("img").position();
console.log("Arriba:" + pos.top + " Izq:" + pos.left);

Y finalmente para obtener información Cross-Browser y manipular propiedades específicas relativas al tamaño de los elementos:

Método Propósito

height()

Obtiene la altura calculada en píxeles del primer elemento del conjunto de resultados

height(valor)

Asigna la altura CSS para cada elemento del conjunto de resultados

width()

Obtiene la anchura calculada en píxeles del primer elemento del conjunto de resultados

width(valor)

Asigna la anchura CSS para cada elemento del conjunto de resultados

innerHeight()

Obtiene la altura interna (excluye el borde e incluye el padding) para el primer elemento del conjunto de resultados

innerWidth()

Obtiene la anchura interna (excluye el borde e incluye el padding) para el primer elemento del conjunto de resultados

outerHeight(margen)

Obtiene la altura externa (incluye el borde y el padding) para el primer elemento del conjunto de resultados. Si el margen es true, también se incluyen los valores del margen.

outerWidth(margen)

Obtiene la anchura externa (incluye el borde y el padding) para el primer elemento del conjunto de resultados. Si el margen es true, también se incluyen los valores del margen.

Por ejemplo, supongamos que tenemos el siguiente fragmento CSS:

div#capa {
  width: 250px;
  height: 180px;
  margin: 10px;
  padding: 20px;
  background: blue;
  border: 2px solid black;
  cursor: pointer;
}
p, span {
  font-size: 16pt;
}

Si en el documento HTML tenemos capas con ids para imprimir el valor de las propiedades mediante este código:

$("#height").html($("#capa").height());
$("#width").html($("#capa").width());
$("#innerH").html($("#capa").innerHeight());
$("#innerW").html($("#capa").innerWidth());
$("#outerH").html($("#capa").outerHeight());
$("#outerW").html($("#capa").outerWidth());
$("#offset").html($("#capa").offset().top + " - " + $("#capa").offset().left);
$("#position").html($("#capa").position().top + " - " + $("#capa").position().left);

Tendríamos un resultado similar a este (http://jsbin.com/futozu/2/edit?html,css,js,output):

Posición y Tamaño con jQuery
Figure 4. Posición y Tamaño con jQuery

6.6.7. Asociando datos con elementos

jQuery ofrece el método data() para trabajar con los atributos de datos personalizados que ofrece HTML5, que empiezan por data-, es decir, data-nombre, data-tipo, etc…​

Método Propósito

data(clave)

Obtiene el atributo data-clave del primer elemento del conjunto de resultados

data(clave, valor)

Almacena el valor en el atributo data-clave en cada elemento del conjunto de resultados

removeData()

Elimina todos los atributos de datos de cada elemento del conjunto de resultados

removeData(clave)

Elimina el atributo data-clave de cada elemento del conjunto de resultados

Es mejor acceder a los elementos por su atributo data que por el texto que contienen sus atributos, ya que éste último puede cambiar debido a la internacionalización (18n) de la aplicación.

Vamos a practicar estos métodos con algunos ejemplos:

Asigna el valor a un atributo
$("#listado").data("tipo","tutorial");
$("#listado").data("codigo", 123); (1)
1 Lo que sería similar a <ul id="#listado" data-tipo="tutorial" data-codigo="123">. Cuidado que estos atributos no se renderizan como atributos del DOM, por lo cual no los podemos inspeccionar mediante las DevTools
Elimina el valor a un atributo
$("#listado").removeData("tipo");
Los atributos de datos almacenados mediante jQuery no se trasladan a atributos del DOM, por lo que no podremos obtenerlos mediante getAttribute().

6.7. Eventos

Igual que estudiamos en la sesión de JavaScript vamos a ver como jQuery simplifica, y mucho, el tratamiento de los eventos.

Sus principales ventajas son:

  • permite asociar un manejador a un grupo de elementos obtenido mediante selectores y filtros.

  • permite conectarse a y desconectarse de los eventos de manera Cross-Browser y dar soporte a IE8 y anteriores.

  • ofrece un objeto de evento que expone la mayoría de las propiedades comunes de manera Cross-Browser.

  • ofrece funciones que encapsulan las funcionalidades de eventos más comunes y funciones auxiliares con código Cross-Browser.

Para empezar veremos el evento que más vamos a utilizar.

6.7.1. Evento document.ready

Del mismo modo que con JavaScript básico, si ponemos un selector jQuery en el encabezado que referencia a elementos DOM que no se han cargado, no va a funcionar. En cambio, si movemos el selector a una posición previa a cerrar el body irá todo correctamente. Para asegurarnos, de manera similar a window.onload, mediante jQuery usaremos el siguiente fragmento:

$(document).ready(function() {
  // código jQuery
}

La ventaja de usar jQuery es que permite usar varias funciones sobre el mismo evento, ya que mediante window.onload sólo se ejecutará la última función que le hayamos asignado.

$(document).ready(function() {
  // código alfa
}

$(document).ready(function() {
  // código beta
}

Además, mientras que con window.onload el evento espera a que haya cargado toda la página, incluidas las imágenes, con jQuery se lanzará cuando haya cargado el DOM, sin tener que esperar a descargar todas las imágenes. Esto se conoce como el evento document.ready.

Si queremos poner el mismo código pero de manera simplificada, aunque menos legible, podemos hacerlo del siguiente modo:

$(function() {
  // código jQuery
});

Es decir, cada vez que le pasemos una función a jQuery() o $(), le diremos al navegador que espere hasta que el DOM haya cargado completamente para ejecutar el código.

6.7.2. Adjuntando manejadores de eventos

Una vez hemos seleccionado el conjunto de elementos al cual queremos asociarle un manejador, usaremos la función on() para indicar que evento capturar y el código del manejador, y off() para eliminar un manejador de eventos.

En versiones anteriores de jQuery se usaban los métodos bind(), unbind(), live(), die(), los cuales han sido marcados como métodos deprecated y no veremos en este módulo.

Antes de jQuery 1.7, en vez de on(), se usaba bind() con jQuery 1.0, live() con la versión 1.3 y delegate() a partir de la versión 1.4.2

Así pues, mediante on(eventos [,selector] [,datos] ,manejador(objetoEvento)) capturaremos los eventos. Más información en http://api.jquery.com/on . En su versión más simple la usaremos del siguiente modo:

$(selector).on("nombreDelEvento", function() {
  // código del manejador
})

Por ejemplo, si queremos capturar cuando se hace click sobre un párrafo:

$("p").on("click", function() {
  console.log("Click sobre un párrafo");
})

Si queremos capturar más de un evento, hemos de separar el nombre de los eventos con espacio:

$("p").on("click mouseenter", function() {
  console.log("Click o mouseenter sobre un párrafo");
})

Cuando aplicamos el evento on, en vez de seleccionar todos los elementos de un tipo que hay en DOM, es mejor elegir el padre del que estamos interesado y luego filtrar dentro del método. Por ejemplo, en vez de:

$("dt").on("mouseenter", function() {

es más eficiente hacer

$("dl").on("mouseenter","dt",function() {
Ejemplo eventos

Vamos a realizar un ejemplo para ver a jQuery en acción. Vamos a cambiar los estilos de una caja cuando el ratón pase por encima del mismo. Si hacemos click sobre la caja, eliminaremos el evento.

Así pues, primero dibujemos la caja:

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <style type="text/css">
    .normal {
      width:300px; height:200px; background-color:yellow; font-size:18pt;
    }
    .resaltado {
      background-color:red;
    }
  </style>
</head>
<body>
<h1>Eventos con jQuery</h1>
<div id="destinoEvento" class="normal">Pasar el ratón para ver el efecto. Click para quitar/añadir el manejador.</div>
</body>
</html>

A continuación, el código JavaScript para capturar y manejar los eventos:

$(document).ready(function() {
  var dest = $("#destinoEvento");	(1)
  dest.on("mouseover mouseleave", function(evt) {	(2)
    dest.toggleClass("resaltado");
  });
  dest.on("click", function(evt) {
    dest.off("mouseover mouseleave");	(3)
    $("#destinoEvento").text("Manejadores eliminados");
  });
});
1 Guardamos el destino del enlace en una variable para evitar repetir la búsqueda
2 Capturamos los eventos de mouseover y mouseleave sobre la caja y le cambiamos la clase del estilo dentro del manejador
3 Dentro de la captura del evento click, eliminamos los manejadores de los eventos mouseover y mouseleave e informamos al usuario del cambio. Si el destino tuviese más manejadores sobre estos eventos, también se eliminarían.
.on() sólo puede crear manejadores de eventos sobre elementos que ya existen en el momento de asignación del listener. Los elementos similares creados tras asignar los manejadores no capturarán los eventos configurados con anterioridad.

6.7.3. Métodos auxiliares

jQuery ofrece un conjunto de métodos auxiliares para simplificar su uso con los eventos más utilizados. Si los ordenamos por su categoría tenemos:

  • Ratón: click(), dblclick(), focusout(), hover(), mousedown(), mouseenter(), mouseleave(), mousemove(), mouseout(), mouseover(), mouseup().

  • Teclado: focusout(), keydown(), keypress(), keyup().

  • Formulario: blur(), change(), focus(), focusin(), select(), submit().

  • Navegador: error(), resize(), scroll().

Es decir, las dos acciones siguientes son semejantes:

$("p").click(function() {
  // Manejador
});

$("p").on("click", function() {
  // Manejador
});

Un caso particular que conviene estudiar es el método hover(), el cual acepta dos manejadores, los cuales se ejecutarán cuando el ratón entre y salga del elemento respectivamente:

$(function() {
  $("#destino").hover(resaltar, resaltar);

  $("#destino").click(fnClick1);
  $("#destino").dblclick(fnClick2);
});

function resaltar(evt) {
  $("#destino").toggleClass("resaltado");
}
function fnClick1() {
  $("#destino").html("Click!");
}
function fnClick2() {
  $("#destino").html("Doble Click!");
}
Recordad que el método clone() clona el contenido seleccionado, pero sin copiar sus eventos. Para ello, hay que pasarle como parámetro true, para que clones los eventos del selector

También podemos usar los siguientes métodos para realizar tareas específicas:

Método Propósito

one(tipo, datos, manejador)

Permite capturar el evento tipo una sola vez para cada elemento del conjunto de resultado

trigger(evento, datos)

Lanza un evento para cada elemento del conjunto de resultados, lo que también provoca que se ejecute la acción predeterminada por el navegador. Por ejemplo, si le pasamos un evento click, el navegador actuará como si se hubiese clickado dicho elemento.

triggerHandler(evento, datos)

Dispara todos los manejadores asociados al evento sin ejecutar la acción predeterminada o burbujero del navegador. Sólo funciona para el primer elemento del conjunto de resultados del selector

Por ejemplo, supongamos que tenemos varios cuadrados y queremos que cambien su color una sola vez:

<!DOCTYPE html>
<html lang="es">
<head>
  <title>Usando el objeto jQuery Event</title>
  <meta charset="UTF-8">
  <style type="text/css">
    div {
      width: 60px; height: 60px; margin: 10px; float: left;
      background: blue; border: 2px solid black; cursor: pointer;
    }
    p {
      font-size: 18pt;
    }
  </style>
</head>
<body>
  <p>Click en cualquier cuadrado para cambiar su color</p>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</body>
</html>

Y el código para cambiar el color una sola vez al hacer click:

$(function() {
  $("div").one("click", function(evt) {
    $(this).css({
      background: "red", cursor: "auto"
    });
    console.log("Evento " + evt.type + " en div " + $(this).attr("id"));
  });
});

Si hacemos click más de una ocasión sobre un mismo cuadrado podemos comprobar como no sale por consola.

6.7.4. Objeto jQuery Event

Para poder trabajar con manejadores de eventos Cross-Browser, es importante que exista un único objeto que permite acceder a las propiedades más importantes:

Propiedad / Método Propósito

type

Tipo del evento, por ejemplo, click

target

Elemento que lanzó el evento

data

Datos pasados al manejador

pageX, pageY

Coordenadas relativas al documento del ratón en el momento de lanzarse el evento

result

Valor devuelto por el último manejador

timestamp

Tiempo en el que se lanzó el evento

preventDefault()

Previene el comportamiento por defecto del navegador

idDefaultPrevented()

Averigua si se ha detenido el comportamiento por defecto

stopPropagation()

Detiene el burbujeo del evento hacia los elementos superiores

isPropagationStopped()

Averigua si se ha detenido el burbujeo del evento

Este objeto se obtiene como parámetro de la función manejadora del evento. Para mostrar estas propiedades en acción, vamos a basarnos en el siguiente código donde podemos ver tres capas:

<!DOCTYPE html>
<html lang="es">
<head>
	<title>Usando el objeto jQuery Event</title>
	<meta charset="UTF-8">
	<style type="text/css">
	.normal {
		width:300px; height:200px; background-color: silver;
		font-size:18pt; margin:5pt 5pt 5pt 5pt;
	}
	</style>
</head>
<body>
<h1>Usando el objeto jQuery Event</h1>
<div id="div1" class="normal">Click en esta capa (div1) para ver la información del evento</div>
<div id="div2" class="normal">Click en esta capa (div2) para ver la información del evento</div>
</body>
</html>

Y el código para mostrar la información:

$(function() {
  $("div").click(function(evt) {
      $(this).html("pageX: " + evt.pageX + ", pageY: " + evt.pageY + ", tipo: " + evt.type + ", target: " + evt.target);
  });
});

Al pulsar encima de las capas, se mostrará la información de coordenadas relativas, tipo y target del evento:

Ejemplo de uso del objeto jQuery Event
Figure 5. Ejemplo de uso del objeto jQuery Event

6.7.5. Eventos personalizados

Sabemos que una vez seleccionado un elemento, mediante el método on podemos capturar el evento y controlar el código del manejador mediante una función anónima.

Ya hemos visto que si queremos lanzar un evento de manera programativa, sin que lo haga el usuario, podemos usar el método trigger(evento,datos):

$('body').trigger('click');	// lanza el evento _click_ sobre el _body_

De este modo, podemos crear eventos personalizados para posteriormente capturarlos:

$('body').on('batClick', function() {
  console.log("Capturado el evento personalizado batClick");
});
$('body').trigger('batClick');

Veamos un ejemplo práctico. Supongamos que accedemos a un servicio REST que devuelve JSON como el de OMDB visto en la sesión anterior:

var datos;

$.getJSON('http://www.omdbapi.com/?s=batman&callback=?', function(resultado) {
  datos = resultado;
});

console.log(datos);	// undefined

Tal como vimos, se trata de una llamada asíncrona de modo que cuando mostramos los datos recibidos no obtenemos nada porque realmente la llamada AJAX todavía no ha finalizado.

Para poder controlar cuando se han cargado los datos podemos lanzar un evento personalizado de modo que al capturarlo estaremos seguro que la petición ha finalizado.

Ejemplo modelo publicador/subscriptor - http://jsbin.com/pisihe/edit?html,js,console,output

$.getJSON('http://www.omdbapi.com/?s=batman&callback=?', function(datos) {
  $(document).trigger('obd/datos', datos);	// publicador
});

$(document).on('obd/datos', function(evt, datos) {
  console.log('Subscriptor 1 recibe los datos');		// subscriptor 1
});

$(document).on('obd/datos', function(evt, datos) {
  console.log('Subscriptor 2 recibe los datos');		// subscriptor 2
});

A este mecanismo de comunicación se le conoce como Publish/Subscribe (y que implementa el patrón Observer), donde podemos tener varios publicadores que envían información mediante el trigger del evento personalizado y múltiples subscriptores con los capturadores del evento.

Esto mismo lo podemos resolver mediante las promesas que veremos en la siguiente unidad.

6.8. this

Ya sabemos que al cargar una página, si referenciamos a this desde la raíz apunta al objeto window. En cambio, si lo hacemos desde dentro una función, siempre apuntará a su padre. Es decir, si la función se llama desde el click de un enlace, this contendrá el enlace.

Supongamos que tenemos un botón que lanza un manejador:

<button id="clickeame">Click aquí</button>

Al mostrar con consola el valor de this obtendremos el elemento button:

$("#clickeame").click(function(evt) {
  console.log(this); // HTMLButtonElement
  console.log(this.type); // "submit"
})

Una acción que se utiliza mucho es convertir el elemento HTML a un objeto jQuery:

var $this = $(this);

6.8.1. Modificando this

Si queremos especificar qué va a ser this al pasarle el control a una función, podemos usar un proxy mediante:

$.proxy(nombreDeLaFuncion, objetoQueSeraThis);

Supongamos que rescribimos el ejemplo anterior mediante un módulo, del siguiente modo:

var manejador =  {
  type: "Mi manejador de eventos",
  manejadorClick: function(evt) {
    console.log(this.type);
  }
};

$(function() {
  $("#clickeame").click(manejador.manejadorClick);
});

Si volvemos al ejemplo anterior, y en vez de obtener mediante this el botón queremos que tome el del manejadores haremos:

$(function() {
  $("#clickeame").click($.proxy(manejador.manejadorClick, manejador));
});

6.9. Ejercicios

6.9.1. (0.2 ptos) Ejercicio 61. Recuperando contenido

A partir del siguiente documento con enlaces, añadir el código jQuery necesario para que al lado de cada enlace muestre mediante una imagen el formato del archivo (pdf, html) o el tipo de enlace (sólo para email).

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Ejercicio 61. Enlaces</title>
</head>
<body>
Enlaces
<ul>
  <li><a href="algunsitio.html">Enlace 1</a></li>
  <li><a name="#ancla1">Ancla de Enlace</a></li>
  <li><a href="algunsitio.html">Enlace 2</a></li>
  <li><a href="algunsitio.pdf">Enlace 3</a></li>
  <li><a href="algunsitio.html">Enlace 4</a></li>
  <li><a href="algunsitio.pdf">Enlace 5</a></li>
  <li><a href="algunsitio.pdf">Enlace 6</a></li>
  <li><a href="mailto:batman@heroes.com">Enlace email</a></li>
</ul>
</body>
</html>

Tras ejecutar la página, el resultado debe ser similar a la siguiente imagen:

Enlaces con imágenes
Figure 6. Enlaces con imágenes

Todo el código estará incluido en el archivo ej61.js

6.9.2. (0.7 ptos) Ejercicio 62. Apuntes 1.0

A partir del documento HTML de los apuntes, haciendo uso de jQuery realizar las siguientes tareas:

  • cambia los datos del autor y email con tus datos personales

  • sustituye la tabla de contenidos con una lista desordenada que sólo contenga los títulos de primer nivel, con enlaces al lugar correspondiente

  • añade un enlace al final de cada capítulo para volver al inicio del documento.

  • añade dos botones (A+, a-) tras el título del documento que modifiquen el tamaño del texto para hacerlo más grande o más pequeño.

La apariencia será similar a la siguiente:

Apuntes retocados
Figure 7. Apuntes retocados

Todo el código estará incluido en el archivo ej62.js

6.9.3. (0.3 ptos) Ejercicio 63. Tabla con resaltado

Supongamos que tenemos una tabla con información en filas similar a la siguiente:

<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="UTF-8">
	<title>Ejercicio 63. Tablas</title>
	<style type="text/css">
	th,td {
		font-family: Verdana, Arial, Helvetica;	font-size: 18px; color: #000000;
	}
	tr {
		border: 1px solid gray;
	}
	td {
		width:200px; padding:3px;
	}
	th {
		background-color:#D2E0E8; color:#003366
	}
	table {
		border: 1pt solid gray;
	}
	.clickable {
		cursor:pointer;
	}
</style>
</head>
<body>
<h1>Usando jQuery para resaltar filas de una tabla</h1>
<table id="laLista">
	<thead>
		<tr><th>Producto</th><th>Precio</th></tr>
	</thead>
	<tbody>
		<tr><td>Leche</td><td>1.99</td></tr>
		<tr><td>Huevos</td><td>2.29</td></tr>
		<tr><td>Mantequilla</td><td>3.49</td></tr>
		<tr><td>Pan</td><td>0.99</td></tr>
		<tr><td>Macarrones</td><td>1.19</td></tr>
		<tr><td>Miel</td><td>4.39</td></tr>
		<tr><td>Galletas</td><td>2.99</td></tr>
	</tbody>
</table>
</body>
</html>

Las tareas a realizar son:

  • Añadir a cada fila de manera alterna los siguientes estilos:

.par {
	background-color:#0f0;
}
.impar {
	background-color:#afa;
}
  • Resalta la fila sobre la que está el ratón con el siguiente estilo:

.resaltado {
	background-color: #ffcc00;
	font-weight:bold;
}
  • Al clickar sobre una fila, mostrará una alerta con el producto y su precio

Tabla con resaltado
Figure 8. Tabla con resaltado

Todo el código estará incluido en el archivo ej63.js


1. Crea una cita con el contenido que había en la capa span.co