Académique Documents
Professionnel Documents
Culture Documents
TRADUCTOR:
Beatriz Parra Pérez
DISEÑO DE CUBIERTA:
Cecilia Poza Melero
.'
~
:".-
Ionathan Chaffer
Karl Swedberg
.s
~ ,"-
..:~~
h\Nh\Yh\
.-MULTIMEDIA_
l _~,
_._c. _ • ~
\,.
/
Todos los nombres propios de programas, sistemas operativos, equipos Quiero dar las gracias a Jenny por su incansable entusiasmo y apoyo,
hardware, etc. que aparecen en este libro son marcas registradas de a Karl por su motivación por continuar escribiendo cuando el espíritu era débil,
sus respectivas compañías u organizaciones. ya la comunidad Ars Technica por su constante inspiración hacia la excelencia técnica.
Jonathan Chaffer
Reservados todos los derechos. El contenido de Quiero dar las gracias a mi mujer Sara, por su constante amor y apoyo.
esta obra está protegido por la Ley, que establece Gracias también a mis dos encantadores hijos, Benjamin y Lucia. Jonathan Chaffer
penas de prisión y/o multas, además de las cuenta con mi más profundo respeto por su experiencia en programación y mi gratitud por su
correspondientes indemnizaciones por daños y
perjuicios, para quienes reprodujeren, plagiaren, ;:;"';,
" - deseo de escribir este libro conmigo.
Muchas gracias a [ohn Resig por crear la librería JavaScript más grande del mundo y por
distribuyeren o comunicaren públicamente, en ~,':.
fomentar una sorprendente comunidad a su alrededor.
todo o en parte, una obra literaria, artística o
científica, o su transformación, interpretación Gracias también a las personas de Packt Publishing, los revisores técnicos de este libro, y a
o ejecución artística fijada en cualquier tipo todos aquellos que han proporcionado ayuda e inspiración en el camino.
de soporte o comunicada a través de cualquier Karl Swedberg
medio, sin la preceptiva autorización.
Edición española:
© EDICIONES ANAYA MULTIMEDIA (GRUPO ANAYA, S.A.), 2010
Juan Ignacio Luca de Tena, 15. 28027 Madrid
Depósito legal: M-47.674-2009
ISBN: 978-84-415-2665-5
Printed in Spain
Impreso en: Gráficas Hermanos Gómez, S. L. L.
•• Agradecimientos Aprende jQuery 1.3 ••
Sobre los autores En estos momentos, Akash proporciona redacción técnica independiente y desarrollo
Web por medio de su sitio Web, http://bitmeta . org.
]onathan Chaffer es el Director Tecnológico de Structure Interactive, una agencia
interactiva con sede en Grand Rapids, Michigan. Allí supervisa los proyectos de de- Dave Methvin cuenta con más de'<25años de experiencia en desarrollo de software
sarrollo Web utilizando una amplia variedad de tecnologías, y colabora en tareas de tanto en entornos Windows como Unix, Su carrera se ha centrado en software incorporado
programación del día a día, también. en los campos de la robótica, telecomunicaciones, y medicina. Posteriormente, pasó a
En la comunidad de código abierto, [onathan ha estado muy activo en el proyecto CMS proyectos de software basados en PC utilizando C/C++ y tecnologías Web.
Drupal, que ha adoptado jQuery como el marco de trabajo JavaScript de su elección. Es Dave cuenta también con más de 20 años de experiencia en periodismo informático.
el creador del Content Construction Kit, un módulo popular para gestionar contenido Fue Editor Ejecutivo en pe Tech Journal y Windows Magazine, tratando temas de PC e
estructurado en sitios Drupal. Es responsable de reparaciones importantes del sistema / Internet; sus columnas prácticas sobre JavaScript ofrecían algunas de las primeras solu-
de menú de Drupal y desarrollador de referencia de API. cienes de cortar y pegar a problemas comunes de páginas Web. También fue co-autor
Jonathan vive en Grand Rapids con su mujer, Jennifer. del libro Networking Windows NT (john Wiley & Sons, 1997).
Actualmente, Dave es Director Tecnológico en PC Pitstop, un sitio Web que ayuda
Karl Swedberg es desarrollador Web en Fusionary Media en Grand Rapids, Michigan, • a los usuarios a solucionar y optimizar el rendimiento de sus ordenadores. También es
donde pasa la mayor parte de su tiempo implementando diseño con el foco en "están- un colaborador activo en la comunidad jQuery.
dares Web", HTML semántico, CSS bien formado, y JavaScript sencillo, Miembro del
Equipo de proyecto jQuery y colaborador activo de la lista de distribución jQuery, Kart .. _ Mike Alsup ha participado en el proyecto jQuery casi desde su creación y ha contri-
es conferenciante habitual y ha impartido formación corporativa en Europa y América buido con muchos plug-ins populares en la comunidad. Es un participante activo en el
del Norte. jQuery Google Group donde frecuentemente proporciona soporte a los nuevos usuarios
Antes de su relación actual con el desarrollo Web, Karl trabajó como editor de estiJQ,' ..- jQuery.
profesor de inglés en un instituto y propietario de una cafetería. Su fascinación con la" Mike vive en el norte del estado de Nueva York con su mujer, Diane, y sus tres hijos
tecnología empezó a principios de los años 90 cuando trabajó en Microsoft en Redmond, adolescentes. Es desarrollador de software en Click Commerce, Inc. donde se centra en
Washington, y ha continuado sin cesar desde entonces. [ava, Swing, y desarrollo de aplicaciones Web.
Karl preferiría pasar el tiempo con su mujer, Sara, y sus dos hijos, Benjamin y Sus plug-ins jQuery se pueden encontrar en http : / / j query . mal sup . com/.
Lucia.
Prólogo 18
i:':.,,~
Introducción 22
Qué trata este libro 24
Qué necesita para este libro 25
Para quién es este libro 25
Convenciones 25
Código fuente 26
1. Empezar a trabajar 28
Qué hace jQuery 29
Por qué jQuery funciona bien 31
Historia del proyecto jQuery 32
Nuestra primera página Web con jQuery 33
Descargar jQuery 33
Configurar el documento HTML 33
Añadir jQuery 36
Encontrar el texto del poema 36
Aplicar la nueva clase 37
Ejecutar el código 37
El producto terminado 38
Resumen 39
2. Selectores 40
UUM 41
$0 : 42
•• Índice de contenidos Índice de contenidos ••
Llevar a cabo una petición POST 154 El código terminado , , , , , , , ;.. 216
Serializar un formulario ,' , , , , , , 156 Resumen , : , ,.., ,., , , , , 219
Estar pendiente de la petición 158
8. Formularios con funciones ~~ 220
AJAX Yeventos ,.., , ,..' , , , , ' " , 160
Limitaciones de seguridad , , , , 161 Mejorar un formulario básico .., ,.., , ,..".., , , , 221
Utilizar JSONP para datos remotos 162 Estilo de formulario mejorado de forma progresiva 222 '
Opciones adicionales , , " , , " , , ,, , 163 La leyenda , , ,.., ,..,..".." , ,, ' , , 224
El método AJAX de bajo nivel... 164 Mensajes de campo obligatorio 225
Modificar opciones predeterminadas , , ,."., , 164 Campos mostrados condicionalmente ., , , , , 228
Cargar partes de una página HTML.. 165 Validación de formulario , , , , , ,.., " , ,..,., , " 231
Resumen , , , , , 167 Campos obligatorios .." , , , ' ' ' , 231
Formatos obligatorios ." , , ,..".., 234
7. Manipulación de tabla 17O Una última comprobación , , ,.., ,.., , , , ,.. 236
Manipulación de casilla de verificación 238
Ordenar y paginar 172
El código terminado , , , , ,.., 241
Ordenación del lado del servidor 172
Formularios compactos , , 243
Impedir que la página se refresque 173
Texto como marcador de posición para campos 243
Ordenación JavaScript : 173 .
Autocompletar AJAX ,..,..,., , ,..,.., , , , 246
Etiquetas de agrupación de filas 175
En el servidor , , , , ,.., , " 247
Ordenación alfabética básica 175
En el navegador , ' , '..,.., , ,.., 247
El poder de los plug-ins 1z,'
Completar el campo de búsqueda 249
Problemas de rendimiento 180.,
Navegación por medio de teclado 249
Manipular las teclas de ordenar 182
Gestionar las teclas del cursor.. , , ,.., , , , , , "".251
Ordenar otros tipos de datos · 183
Insertar sugerencias en el campo 252
Resaltar columna 186
Eliminar la lista de sugerencias 253
Alternar la dirección de la ordenación 186
Autocompletar frente a live search 253
Paginación del lado del servidor 188
El código terminado , 254
Ordenar y paginar van juntos 189
Trabajar con datos de formulario numéricos 256
Paginación JavaScript 190
Estructura de tabla de carro de la compra 256
Mostrar el paginador 191
Rechazar entrada no numérica , " , , " 259
Habilitar los botones del paginador 192
Cálculos numéricos , , , , ,.., , , 260
Marcar la página actual.. : ~' , ', , 193
Analizar y aplicar formato a moneda 261
Paginación con ordenación , , ,.194
Tratar con decimales ., , , ,.., , , , , , 262
El código terminado, ", , ,.., , 195
Otros cálculos , , ,.., , " '.',., , , , 264
Modificar la apariencia de la tabla 197
Redondear valores , , ".." , ,..,.,." ,., , , , , 265
Resaltar filas , , , 197
Toques finales." , ,.., ,.., , ,..,., , , 265
Alternar color de filas ,.., ,.., 198
Eliminar elementos , , , ", 266
Alternar colores de fila avanzado 200
Editar información de envío , ,.., ,.., , ' , , ' , , 270
Resaltar filas basado en interacción del usuario 202
El código terminado , ".., , , , , , 273
Descripciones emergentes , ,..,, , , ,.204
Resumen ,.,.., " , '..,.., ,., ,..,.., ,.. 275
Contraer y expandir secciones , , , ··., 209
Filtrado , ,.., , , , " , , , , ,..,.., ,.., 211 9. Rotativos 276
Opciones de filtro., ,."., , , , , , , 212
Invertir los filtros ,.., , , ,.., , , , , , , 213 Titular rotativo , , , ' , , , " 277
Interactuar con otro código 214 Configurar la página , ,.., , 278
•• Índice de contenidos fndice de contenidos ••
~~~
Referencia (X)HTML. , 376
~;~
~~~~~~:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::~:::::::::::::::::::::: Charles
Fiddler :
387
387
Aptana 387
Página principal de W3c.. 376
Referencia CSS 376, ' Apéndice C. ]avaScript Closures 388
Página principal CSS de W3C. 376 •
Funciones internas , 389
La chuleta CSS de Mezzoblue 377
El gran escape 390
Position is everything 377
Ámbito de aplicación de variables 392
Blogs de utilidad 377
Interacciones entre closures 394
El blog jQuery 377
Closures en jQuery 395
Learning jQuery 377
Argumentos para $(document).readyO 395
Ajaxian 377
Manejadores de evento 396
[ohn Resig 378
Peligros de pérdidas de memoria 397
JavaScript ant 378
Bucles de referencia accidentales 398
Roberts talk 378
El problema de pérdidas de memoria de Internet Explorer 399
Estándares Web con imaginación : :: 378
La buena noticia 400
Snook 378
Resumen 400
Recurso JavaScript de Matt Snider 378
I cant 378 Apéndice D. Referencia rápida 402
DOM scripting 379
As days pass by 379 Expresiones de selector 403
A list apart 379 Métodos transversales DOM 405
Marcos de trabajo de desarrollo Web utilizando jQuery 379 Métodos de evento 407
Métodos de efecto 409
Apéndice B. Herramientas de desarrollo 382 Métodos de manipulación DOM 410
Métodos AJAX 413
Herramientas para Firefox 383 Métodos variados 414
Firebug 383
.Barra de herramientas de desarrollador Web 384 Índice alfabético 415
,
(
'..
.
'
.-.~
Me siento honrado de saber que Karl Swedberg y [onathan Chaffer han emprendido
la tarea de escribir este libro. Como el primer libro sobre jQuery, establece el estándar
Prólogo
que otros libros jQuery, y realmente, otros libros JavaScript en general, han intentado
igualar. Constantemente ha sido uno de los libros JavaScript más vendidos desde su
aparición, debido a su calidad y atención al detalle.
Estoy especialmente contento de que Karl y Jonathan hayan escrito el libro ya que
los conocía muy bien y sabía que serían perfectos para el trabajo. Al ser parte del equi-
po jQuery, he tenido la oportunidad de llegar a conocer a Karl bastante bien en los úl-
timos dos años, y especialmente dentro del contexto de su esfuerzo por escribir este
libro. Viendo el resultado final, queda claro que sus conocimientos como desarrollador
y profesor de inglés estaban perfectamente diseñados para esta tarea.
También he tenido la oportunidad de conocer a ambos en persona, una ocurrencia
extraña en el mundo de los proyectos de código abierto distribuidos, y continúan siendo
miembros íntegros de la comunidad jQuery.
La librería jQuery se utiliza por muchas personas diferentes en la comunidad jQuery.
La comunidad está llena de diseñadores, desarrolladores, personas que tienen expe-
riencia programando y otros que no la tienen. Incluso dentro del equipo jQuery, tene-
mos personas de todos los perfiles que proporcionan su conocimiento en la dirección
del proyecto. Hay una cosa que es común a todos los usuarios de jQuery, sin embargo:
somos una comunidad de desarrolladores y diseñadores que quieren que el desarrollo
JavaScript sea sencillo.
Es casi un cliché, en este punto, decir que un proyecto de código abierto está orientado
a la comunidad, o que un proyecto quiere centrarse en ayudar a los nuevos usuarios a
empezar. Pero no sólo es un gesto vacío para jQuery; es el combustible para el proyecto.
E!II Prólogo
john Resig
Creador de jQuery
, /
.",
.::~
Todo empezó como un mero trabajo desinteresado, por amor al arte, allá por el año
• ¿# 2005, por [ohn Resig, un niño prodigio en JavaScript que ahora trabaja para Mozilla.
lntrodu cion
Inspirado por los pioneros en este terreno como Dean Edwards y Simon Willison, Resig
reunió una serie de funciones para que por medio de programación fuera sencillo en-
contrar elementos en una página Web y asignarles comportamientos. Cuando hizo pú-
blico su proyecto por primera vez en enero de 2006, había añadido modificación DOM
y animaciones básicas. Le asignó el nombre jQuery para enfatizar el papel decisivo de
encontrar o "consultar" partes de una página Web y actuar sobre ellas con JavaScript.
Pasados pocos años desde entonces, jQuery ha crecido en su conjunto de características,
mejorado en su rendimiento, y adquirido una amplia aprobación por algunos de los si-
tios más populares en Internet. Aunque Resig sigue siendo el desarrollador principal del
proyecto, jQuery ha prosperado, al estilo de código abierto, hasta el punto donde ahora
cuenta con un equipo de excelentes desarrolladores JavaScript, así como una comunidad
dinámica de miles de desarrolladores. La biblioteca JavaScript jQuery puede mejorar sus
sitios Web con independencia de su formación previa. Proporciona una amplia varie-
dad de características, una sintaxis fácil de aprender, y compatibilidad multiplataforma
robusta en un solo archivo compacto. Además, se han desarrollado cientos de plug-ins
para ampliar la funcionalidad de jQuery, convirtiéndolo en una herramienta esencial
para casi cualquier ocasión de programación del lado del cliente.
Este libro proporciona una introducción a los conceptos jQuery, permitiéndole aña-
dir interacciones y animaciones a sus páginas, incluso si los intentos previos de escribir
JavaScript le han dejado frustrado. Este libro le ayudará a superar los escollos asociados
con AJAX,eventos, efectos y características avanzadas del lenguaje JavaScript, y le pro-
porciona una breve referencia a la librería jQuery para regresar una y otra vez.
•• Introducción Aprende jQuery 1.3 lEa
En el apéndice B descubrirá un número de programas de terceros de utilidad y utilida-
Qué trata este libro des para editat y depurar código jQuery dentro de su entorno de desarrollo personal.
En el apéndice C adquirirá un conocimiento profundo de los closures, qué son y cómo
En el capítulo 1 conocerá qué es la librería JavaScript jQuery. El capítulo empieza con los puede utilizar en su propio beneficio.
una descripción de jQuery y lo que puede hacer por usted. Le muestra cómo descargar En el apéndice D tendrá una visión de conjunto de toda la librería jQuery, incluido
y establecer la librería, así corno escribir su primer script. cada uno de sus métodos y expresiones de selector. Su formato es perfecto para esos
En el capítulo 2 aprenderá cómo utilizar las expresiones selector de jQuery y los mé- momentos cuando sabe qué quiere hacer, pero no está seguro del nombre de método o
todos transversales DOM para encontrar elementos en la página, allí donde se encuen- selector correcto.
tren. Utilizará jQuery para aplicar estilo a un conjunto diverso de elementos de página,
algunas veces en una forma que CSS puro no puede.
En el capítulo 3 utilizará el mecanismo de gestión de evento de jQuery para activar
comportamientos cuando ocurren eventos de navegador. Verá cómo jQuery facilita Qué necesita para este libro
anexar eventos a elementos discretamente, incluso antes de que la página termine de
cargarse. Y, se le presentarán más temas avanzados, tales corno burbujeo de eventos, Para poder escribir y ejecutar el código que se demuestra en este libro, necesitará lo
delegación y espacio de nombres. siguiente:
En el capítulo 4 se le presentarán las técnicas de animación de jQuery y verá cómo
ocultar, mostrar y mover elementos de página con efectos que son-tanto de utilidad • Un editor de texto básico.
corno agradables a la vista. • Un navegador Web moderno corno Mozilla Firefox, Apple Safari, y Microsoft
En el capítulo 5, aprenderá cómo cambiar su página. Este capítulo le enseñará cómo Internet Explorer.
alternar la estructura de un documento HTML, así corno su contenido, en el momento.
En el capítulo 6 descubrirá las muchas formas en las que jQuery facilita acceder-a . • El archivo fuente jQuery, versión 1.3.1 o posterior, que se puede descargar de
funcionalidad del lado del servidor sin recurrir a refrescar página. http://jquery.com/.
En los siguientes tres capítulos (7, 8, Y9) trabajará en varios ejemplos del mundo real,
Además, para ejecutar los ejemplos AJAX del capítulo 6, necesitará un servidor com-
reuniendo lo que ha aprendido en varios capítulos y creando soluciones jQuery robus-
patible con PHP.
tas a problemas comunes.
En el capítulo 7 ordenará, clasificará y aplicará estilo a información para crear dise-
ños de datos bonitos y funcionales.
En el capítulo 8 dominará los puntos de la validación del lado del cliente, diseñar un Para quién es este libro
diseño de formulario adaptable, e implementar características de formulario interactivas
cliente-servidor corno autocompletar. Este libro es para diseñadores Web que desean crear elementos interactivos para
En el capítulo 9 mejorará la belleza y utilidad de elementos, de página al mostrarlos sus diseños, y para desarrolladores que desean crear la mejor interfaz de usuario para
en bloques más manejables. Hará que la información se muestre a la vista y fuera de ella sus aplicaciones Web. Conocimiento básico de programación JavaScript es necesario.
tanto por su cuenta corno bajo el control del usuario. Necesitará saber los fundamentos básicos de HTML y CSS, y debería estar cómodo con
Los capítulos 10 y 11 le llevan más allá de los métodos jQuery fundamentales para la sintaxis de JavaScript. No se asume ningún conocimiento de jQuery, ni experiencia
explorar extensiones de terceros a la biblioteca, y le muestran varias formas en las que con ninguna librería JavaScript.
puede ampliar la biblioteca usted mismo.
En el capítulo 10 examinará el plug-in Form y la colección oficial de plug-ins de inter-
faz de usuario conocida corno jQuery UI. También aprenderá dónde encontrar muchos
otros plug-ins jQuery populares y verá qué pueden hacer por usted. Convenciones
En el capítulo 11 aprenderá cómo aprovechar las impresionantes posibilidades de
uso de ampliación de jQuery para desarrollar sus propios plug-ins desde el principio. Para ayudarle a sacar el mayor partido al texto y saber dónde se encuentra en cada
Creará sus propias funciones de utilidad, añadirá métodos de objeto jQuery, escribirá momento, a lo largo del libro utilizamos distintas convenciones:
expresiones personalizadas de selector y mucho más.
En el apéndice A encontrará una gran cantidad de sitios Web informativos sobre • Las combinaciones de teclas se muestran en negrita, corno por ejemplo Control-A.
temas relacionados con jQuery, JavaScript, y el desarrollo Web en general. Los botones de las distintas aplicaciones también se muestran en negrita.
N
ElJI Introducción
$(/a[href$=.pdfl/) .addClass(/pdflink/);
$ (fa [hrefA=http] [href*=henry] /)
.addClass(/henrylink/),
}) ;
Código fuente
Para desarrollar los ejemplos, puede optar por introducir manualmente el código o
utilizar los archivos de código fuente que acompañan al libro. También puede descargar
el código fuente utilizado en el sitio Web de Anaya (http://www .AnayaMultimedia .
es, sección Soporte técnico, opción Complementos).
,
1[
"t;
r
r
...•
Hoy en día World Wide Web es un entorno dinámico, y sus usuarios establecen un
nivel alto tanto para el estilo como para la función de los sitios. Para crear sitios intere-
santes e interactivos, los desarrolladores están recurriendo a librerías JavaScript como
1 jQuery para automatizar tareas comunes y simplificar las complicadas. Una razón por
la que la librería jQuery es una elección popular es su capacidad de ayudar en una am-
plia variedad de tareas.
Puede parecer difícil saber por dónde empezar porque jQuery lleva a cabo muchas
m ez r funciones diferentes. Sin embargo, existe una coherencia y simetría en el diseño de la li-
brería; la mayoría de sus conceptos se toman de la estructura de HTML y CSS (Cascading
Style Sheets u Hojas de estilo en cascada). El diseño de la librería lleva a un comienzo
rápido para diseñadores con poca experiencia de programación ya que muchos desarro-
lladores Web tienen más experiencia con estas tecnologías que con JavaScript. De hecho,
a trabajar
en este capítulo escribimos un programa jQuery en sólo tres líneas de código. Por otro
lado, esta consistencia conceptual también ayudará a los programadores experimenta-
dos, como veremos en los capítulos siguientes, más avanzados.
Por lo tanto, veamos lo que jQuery puede hacer por nosotros.
La elegancia de la librería viene en parte por diseño, y en parte debido al proceso • jQuery 1.2.6 (mayo 2008): La funcionalidad del popular plug-in Dimensions de
evolutivo impulsado por la comunidad vibrante que ha surgido alrededor del proyecto. Brandon- Aaron se incluyó en la librería principal.
Los usuarios de jQuery se reúnen para tratar no sólo el desarrollo de plug-ins, sino tam-
bién las mejores a la librería principal. El Apéndice A detalla muchos de los recursos de
• jQuery}.3 (enero 2009): Una importante revisión del motor selector (Sizzle) pro-
comunidad disponibles para los desarrolladores jQuery. A pesar de los esfuerzos nece- porcionó un gran impulso al reñríimiento de la librería. La delegación de evento
pasó a soportarse formalmente.
sarios para crear un sistema así de robusto y flexible, el producto final es gratuito para
todos. Este proyecto de código abierto tiene doble licencia bajo la Licencia Pública de
GNU (apropiada para incluir en muchos otros proyectos de código abierto) y la Licencia
MIT (para facilitar el uso de jQuery dentro de software propietario).
Las notas para versiones jQuery más antiguas se pueden encontrar en el sitio Web del
proyecto en http://docs . j query. corn/His.tory _of_jQuery.
Historia del proyecto jQuerv
Este libro trata la funcionalidad y sintaxis de jQuery 1.3.x, la versión más actualizada
en el momento de escribir estas líneas. La premisa detrás de la biblioteca, proporcionar Nuestra primera página Web con jQuery
una forma sencilla de encontrar elementos en una página Web y manipularlos, no ha
cambiado en el transcurso de su desarrollo, pero sí lo han hecho ciertos detalles de sin- Ahora que hemos tratado el conjunto de características que tenemos disponibles con
taxis y características. Esta breve visión de conjunto de la historia del proyecto describe jQuery, podemos examinar cómo utilizar la biblioteca.
los cambios más significativos de una versión a otra.
• Fase de desarrollo público: [ohn Resig mencionó por primera vez una mejora en Descargar jQuery
la biblioteca "Behaviour" del prototipo en agosto de 2005. Este nuevo marco de
trabajo se lanzó formalmente como jQuery el 14 de enero, 2006. El sitio Web jQuery oficial (http://j query. corn/) es siempre el recurso más ac ..
tualizado para código y noticias relacionadas con la librería. Para empezar, necesitamos
• jQuery 1.0 (agosto 2006): Ésta, la primera versión estable de la biblioteca, ya dis-
una copia de jQuery, que se puede descargar desde la página principal del sitio. Varias
ponía de soporte robusto para selectores CSS, manejadores de evento e interacción
versiones de jQuery pueden estar disponibles en cualquier momento dado; lo más
AJAX.
apropiado para nosotros como desarrolladores de sitios será la versión no comprimida
• jQuery 1.1 (enero 2007): Esta versión simplificaba la API considerablemente. más actualizada de la librería. Ésta se puede reemplazar con una versión comprimida
Muchos métodos rara vez utilizados se combinaron, reduciendo el número de en entornos de producción. No necesita instalación. Para utilizar jQuery, simplemente
métodos a aprender y documentar. necesitamos situarlo en nuestro sitio en una ubicación pública. Puesto que JavaScript es
un lenguaje interpretado, no hay fase de compilación o creación por la que preocuparse.
• jQuery 1.1.3 (julio 2007): Esta versión menor contenía importantes mejoras de
Siempre que necesitemos una página para tener jQuery disponible, simplemente hare-
velocidad para el motor selector de jQuery. A partir de esta versión, el rendimiento
mos referencia a la ubicación del archivo desde el documento HTML.
de jQuery se compararía favorablemente con las librerías JavaScript semejantes
como Prototype, Mootools, y Dojo.
• jQuery 1.2 (septiembre 2007): La sintaxis XPath para seleccionar elementos se Configurar el documento HTMl
eliminó en esta versión, ya que pasaba a ser redundante con la sintaxis CSS. La
personalización de efectos pasó a ser mucho más flexible en esta versión, y el Existen tres partes en la mayoría de ejemplos de uso jQuery: el propio documento
desarrollo de plug-ins pasó a ser más sencillo con la incorporación de eventos de HTML, archivos CSS para aplicar estilo, y archivos JavaScript para actuar sobre él. Para
espacio de nombre. nuestro primer ejemplo, utilizaremos una página con un extracto de libro que tiene apli-
cadas un número de clases a partes de él.
• jQuery UI (septiembre 2007): Esta nueva suite de plug-in se anunció para sustituir
<!DOCTYPE html PUELlC "-jjW3CjjDTD XHTML 1.0 TransitionaljjEN"
el plug-in popular, aunque antiguo, Interface. Se incluyó una rica colección de
"http,jjwww.w3.orgjTRjxhtmlljDTDjxhtmll-transitional.dtd">
widgets prefabricados, así como un conjunto de herramientas para crear elementos
sofisticados como interfaces de arrastrar y soltar. <html xmlns="http,jjwww.w3.orgj1999jxhtml"
~
lmI 1. Empezar a trabajar Aprende jQuery 1.3 ••
xml:lang:=lIenll lang=lIen">
chead>
<meta http-equiv="Content-Typell
content:="textjhtml; charset=utf-8 '/> 1
El diseño real de los archivos en el servidor no importa. Las referencias de un archivo a
ctitle>Through the Looking-Glassc/title>
otro se tienen que ajustar para coincidir con la organización que elegimos. En la mayoría
de ejemplos de este libro, utilizaremos rutas de acceso relativas para hacer referencia
<link rel="stylesheet" href="alice.css" a archivos ( .. / images/foo. png) en lugar de rutas de acceso absolutas CI images/
type=lItext/cssll media=l!screenll /> f oo. png). Esto permitirá que el código se ejecute localmente sin la necesidad de un
servidor Web.
cscript src=lIjquery.jsll type=lItext/javascriptH></script>
cscript sre= alice. j s
11 11 type= text/j avascript ></ s c r pt s
I! 11 í
</head>
Inmediatamente a continuación del preámbulo HTML normal, se carga la hoja de
ebody> estilo. Para este ejemplo, utilizaremos una espartana.
<hl>Through the Looking-Glass</hl>
cdiv class="author">by Lewis Carrollc/div> body {
font: 62.5% Arial, Verdana, sans-serif¡
cdiv class="chapter id="chapter-l">
11 )
ch2 class=/(chapter-title">l. Looking-Glass House</h2> hl
<p>There was a book lying near Alice on the table, font-size: 2.5em¡
and while she sat watching the White King (for she margin-bottom: O¡
was still a little anxious about him, and had the )
ink a11 ready te threw over him, in case he fainted h2
again), she turned over the leaves, to find some font-size: 1.3em¡
part that she could read, <span c1ass="spoken"> margin-bottom: .Semi
"&mdash¡for it1s all in some language I don't know,lI )
</span> she said to herself.</p> h3
<p>It was like this.</p> font-size: l.lem;
cdiv class="poem'1> margin-bottom: O;
ch3 class=lpoem-title">YKCOWREBBAJc/h3> )
cdiv class="poem-stanza"> .poem (
cdiv>sevot yhtils eht dna ,gillirb sawT'c/div> margin: O 2em;
cdiv>;ebaw eht ni elbrnig dna eryg diDc/div> )
cdiv>,sevogorob eht erew ysmim l1Ac/div> .highlight {
cdiv>.ebargtuo shtar ernom eht dnAc/div> font-style, italic;
c/div> border, lpx solid #888;
c/div> padding, O.5em;
<p>She puzzled over this for sorne time, but at last margin: O. Sem O,'
a bright thought struck her. cspan class=l1spoken"> background-color: #ffc;
"Why, it's a Looking-glass book, of course! And if
1 hold it up to a glass, the words will all go the
right way again."</span>c/p> Después de hacer referencia a la hoja de estilo, se incluyen los archivos JavaScript. Es
cp>This was the poem that Alice read.</p> importante que la etiqueta de script para la librería jQuery se sitúe antes de la etiqueta
cdiv class="poem">
ch3 class="poem-title >JAEBERWOCKYc/h3>
tl
para nuestros scripts personalizados; de lo contrario, el marco de trabajo jQuery no se
cdiv class=l'poem-stanza11> encontrará disponible cuando nuestro código intente hacerle referencia.
cdiv>'Twas brillig, and the slithy tovesc/div>
cdiv>Did gyre and gimble in the wabe¡c/div>
cdiv>All mimsy were the borogoves,c/div>
cdiv>And the mame raths outgrabe.c/div>
</div>
En el resto de este libro, solamente se imprimirán las partes relevantes de los archivos
</div> HTMLy CSS. Los archivos completos se encuentran disponibles en la página Web de
c/div> Anaya Multimedia (http://www.AnayaMultimedia.es. sección Soporte técnico,
</body>
opción Complementos).
</html>
•• 1. Empezar a trabajar
, AprendejQuery 1.3 ••
Ahora tenemos una página que se parece a la figura 1.1: del libro. Pasaremos por las diferentes formas de localizar partes de un documento en
el capítulo siguiente.
La función, $ () es en realidad una función factory para el objeto jQuery, que es el
Through the Looking-Glass componente básico con el que trabaj~emos a partir de ahora. El objeto jQuery encap-
by Lewi. Carroll
sula cero o más elementos DOM, y nos permite interactuar con ellos de formas muy di-
1. Looking.Glass House ferentes. En este caso, deseamos modificar la apariencia de estas partes de la página, y
There ..•••.
as a book Iyill(¡ ne.at AliOR on 11'1. tabla, anel whlle in. ,at w1Itcl11ng the White King (for $he wa~ stil! a Httle anxioltl about him, anel hild the ink all ready lo
throw ovar htm. in case he 'a¡nte<! aoaln), she tumed OVIK Ihe teeves. lo find ~me part thal she ccutd resd. "-f~ rts 811 in sorne lal'l9ullge I clon't\I:.now,'" she said lo realizaremos esto al cambiar las clases aplicadas al poema.
heuelf.
ltwldlike-lhls.
Ejecutar el código
Este ejemplo es para demostrar el uso sencillo de jQuery. En situaciones del mundo
Juntos, $ () y . addClass () son suficientes para conseguir nuestro objetivo de cam-
real, este tipo de estilo se puede llevar a cabo sólo con CSS. biar la apariencia del texto del poema. Sin embargo, si esta línea de código se inserta
sola en el encabezado del documento, no tendrá efecto. El código JavaScript se ejecuta
generalmente tan pronto como se encuentra en el navegador, y en el momento en que se
Añadir jQuery procesa el encabezado, todavía no hay HTML presente al que aplicar estilo. Necesitamos
retrasar la ejecución del código hasta después de que el DOM está disponible para nues-
Nuestro código personalizado irá en el segundo archivo JavaScript, actualmen-
tro uso. El mecanismo tradicional para controlar cuándo se ejecuta código JavaScript es
te vacío, que hemos incluido desde HTML utilizando <script S'rc="alice.js" llamar al código desde los manejadores de evento. Muchos manejadores se encuentran
type=" text/j avascript" ></ s c r i.pt.>. Para este ejemplo, solamente necesitamos
disponibles para eventos iniciados por el usuario, como c1icsdel ratón o pulsar teclas.
tres líneas de código: Si no tenemos jQuery disponible para nuestro uso, tendremos que basarnos en el mane-
$ (document) .ready(function() { jador onload, que se activa después de que la página (junto con todas sus imágenes) se
$ (' .poem-stanza') .addClass ('highlight'); ha mostrado. Para activar nuestro código desde el evento onload, situaremos el código
}) ; dentro de una función:
function highlightPoemStanzas() {
Encontrar el texto del poema $(' .poem-stanza' I .addClass('highlight');
Esto hace que nuestro código se ejecute después de que la página esté completamen- Through the Looking-Glass
te cargada. by lewi, CanDil
Existen desventajas a este enfoque. Hemos alterado el propio HTML para efectuar 1. Looking-Glass H.ouse
este cambio de comportamiento. Este estrecho acoplamiento de estructura y función sa- Therewas a boot Iying rlCaf Auceen the tabla, 800 while she sal W8tchl~ theWhite King (fOfshe W8S51illa little anxiousabout hlm, 3M had the Ink all feady lo
IhJOWovar hlm. in case:he f.lnlQd ag.ain), ,he turrMld 0Vt!f tne leaves. lo fin<!som. p8Ji thal she aHJld ,ead, ~-fOf ir, all In tome language I don', know,"S'ne said lo
tura el código, posiblemente requiriendo que se repitan las mismas llamadas de función 1MfM:1t.
durante muchas páginas diferentes, o en el caso de otros eventos como clics del ratón, It WIU lb ¡hls.
durante cada instancia de un elemento en una página. Añadir nuevos comportamientos YKCOWREBBAJ
requeriría entonces alteraciones en múltiples lugares, aumentando la oportunidad para aelo'Oi yhtHa eht dna ,gillirb _wr
;ebaw ehl ni eJbei'J dna et)'9 diD
error y complicando flujos de trabajo paralelos para diseñado res y programadores. ,H~ehtw.wj4llJti/Jl' l/A
.abIugtuo mhu MKlIm ffht dnA
Para evitar este peligro, jQuery nos permite programar llamadas de función a activarse
una vez que se ha cargado el DOM, sin esperar a las imágenes, con la construcción $ (do- She puzzled 0W!f this for some time, but al last. bflgtrt lhoughl strud: h••.. "Why, Ir. a L<x*ing-glau boOl. of cecrse!And If I hold 1I up lo a glaa. the words wlll all go
th4! tight.way 8gain,"
cument) . ready () . Con nuestra función definida como antes, podemos escribir: Thls was the poem lhal Allc. reed,
JAB8fRWOCKY
$Idocument) .readylhighlightPoemStanzas);
7wa. txil/ig, snd /he .Uthy lo","
Did g)n! and gmbl. in lita w.~
Esta técnica no requiere ninguna modificación HTML. En su lugar, el comportamien- AlI.IIIlmsy WBrIi! the borogo\OWII,
Artd the lIIfome ralha outgmbe.
to se anexa completamente desde el archivo JavaScript. Aprenderemos más adelante
cómo responder a otros tipos de eventos, separando sus efectos desde la estructura
Figura 1.2. Resultado final.
HTML también.
Esta encarnación es todavía un despilfarro porque la función highlightPoemS-
tanzas () se define solamente para utilizarse inmediatamente, y exactamente una vez. Las estrofas del poema ahora están en cursiva y encerradas en cajas, como se ha espe-
Esto significa que hemos utilizado un identificador en el espacio de nombres global de cificado por la hoja de estilo alice. c s s, debido a la inserción de la clase highlight
funciones que tenemos que recordar no utilizar de nuevo. JavaScript, como otros lengua- por el código JavaScript.
jes de programación, tiene una forma de evitar esta ineficiencia denominada funciones
anónimas (algunas veces también denominada funciones lambda). Al utilizar funciones
anónimas, podemos escribir el código como se presentó originalmente: Resumen
$Idocument) .readylfunction() {
$1' .poem-stanza') .addClassl'highlight'); Ahora ya tenemos una idea de por qué un desarrollador elegiría utilizar un marco
}) ; de trabajo JavaScript en lugar de escribir todo el código desde el principio, incluso para
las tareas más básicas. También hemos visto algunas formas en las que jQuery sobresa-
Al utilizar la palabra clave funct ion sin un nombre de función, definimos una fun-
le como marco de trabajo, y por qué podríamos elegirlo sobre otras opciones. También
ción exactamente donde se necesita, y no antes. Esto elimina saturación y nos lleva a tres
sabemos en general qué tareas hace más sencillas jQuery.
líneas de JavaScript. Este estilo es extremadamente adecuado en código jQuery, ya que
En este capítulo, hemos aprendido cómo poner jQuery disponible para el código
muchos métodos toman una función como un argumento y dichas funciones raramente
JavaScript en nuestra página Web, utilizar la función $ () para localizar una parte de la
son reutilizables. página que tiene una clase dada, llamar a . addClass () para aplicar estilo adicional a
Cuando se utiliza esta sintaxis para definir una función anónima dentro del cuerpo
esta parte de la página, e invocar $ (documen t ) . ready () para hacer que este código
de otra función, se puede crear un cierre. Éste es un concepto avanzado y potente, pero
se ejecute al cargarse la página.
se debería entender cuando se haga amplio uso de definiciones anidadas de función ya
El ejemplo sencillo que hemos utilizado demuestra cómo funciona jQuery, pero no
que puede tener consecuencias inesperadas y ramificaciones en el uso de memoria. Este
es de mucha utilidad en situaciones del mundo real. En el siguiente capítulo, ampliare-
tema se trata en el Apéndice C. mos este código al explorar el sofisticado lenguaje selector de jQuery, encontrando usos
prácticos para esta técnica.
El producto terminado
Ahora que nuestro JavaScript está en su lugar, la página se parece a la que se mues-
tra en la figura 1.2:
,,
""
.'
La librería jQuery aprovecha el potencial de los selectores CSS (Cascading Style Sheets
u Hojas de estilo en cascada) para permitimos acceder rápida y fácilmente a elemen-
2
tos o grupos de elementos en el DQM (Document Object Modelo Modelo de Objetos de
Documento). En este capítulo, exploraremos algunos de estos selectores, así como los
selectores personalizados de jQuery. También examinaremos los métodos transversa-
les DOM de jQuery que proporcionan incluso mayor flexibilidad para obtener lo que
queremos.
electores, El DOM
Uno de los aspectos más potentes de jQuery es su capacidad para que la selección de
elementos en el DOM sea sencilla. El DOM es una estructura en árbol de tipos. HTML,
como otros lenguajes de marcación, utiliza este modelo para describir las relaciones de
cosas en una página. Cuando hacemos referencia a estas relaciones, utilizamos la misma
terminología que utilizamos cuando hacemos referencia a relaciones familiares, padres,
hijos, etc.
Un sencillo ejemplo puede ayudamos a entender cómo la metáfora del árbol familiar
se aplica a un documento:
e ht.ml »
<:head>
<title>the title</title>
</head>
IIPJI 2. Selectores ,, Aprende jQuery 1.3 11m
<body> Tabla 2.1. Ejemplo de selectores.
<div>
ep>This is a paragraph.</p>
ep>This is another paragraph.e/p>
ep>This is yet another paragraph.e/p>
e/div> Nombre etiqueta p $(/p/) Selecciona todos los párrafos en el docu-
e/body> mento.
e/htrnl>
ID #un-id $(/#un-idl) Selecciona el único elemento en el documento
Aquí, <h t ml > es el ancestro de todos los otros elementos; en otras palabras, todos que tiene un ID de un-id.
los otros elementos son descendientes de c ht.m Lc-. Los elementos -che ad» y -ebody» Clase .una-clase $(/.una-clase/) Selecciona todos los elementos en el docu-
no son sólo descendientes, sino hijos de -cht.rnl.», también. De igual forma, además de mento que tienen una clase de una-clase.
ser el ancestro de ehe ad» y «body», «ht ml » es también su padre. Los elementos <p>
son hijos (y descendientes) de <di V>, descendientes de «body» y <html », y hermanos
Como se ha mencionado en el capítulo anterior, cuando anexamos métodos a la fun-
entre sí. Para información sobre cómo visualizar la estructura de árbol familiar del DOM
ción factory $ ( ) , se pasa en bucle automáticamente e implícitamente por los elementos
utilizando software de terceros, consulte el Apéndice B.
situados en el objeto jQuery. Por lo tanto, podemos evitar normalmente la iteración ex-
Un punto importante a destacar antes de empezar es que el conjunto de elementos plícita, como un bucle for, que a menudo es necesario en programación DOM.
resultante desde selectores y métodos siempre se encuentra situado en un objeto jQuery.
Ahora que hemos tratado los aspectos básicos, estamos listos para empezar a explo-
Es muy sencillo trabajar con objetos jQuery cuando queremos realmente hacer algo con rar algunos usos de selectores más potentes.
las cosas que encontramos en una página. Podemos fácilmente vincular eventos a estos
objetos y añadir les efectos, así como encadenar múltiples modificaciones o efectos juntos.
No obstante, los objetos jQuery son diferentes de los elementos DOM normales o listas de
nodo, y como tales no necesariamente proporcionan los mismos métodos y propiedades Selecto res CSS
para algunas tares. En la última parte del capítulo, por lo tanto, examinaremos formas
de acceder a los elementos DOM que se encuentran en un objeto jQuery. La librería CSS soporta casi todos los selectores incluidos en las especificaciones CSS
1 a 3, como se detalla en el sitio Web del World Wide Web Consortium: http://www .
w3. org/Style/CSS/#specs. Este soporte permite a los desarrolladores mejorar sus
sitios Web sin preocuparse por qué navegador (particularmente Internet Explorer 6)
La función factory $0 podría no entender selectores avanzados, siempre y cuando los navegadores tengan
habilitado JavaScript.
Con independencia del tipo de selector que queremos utilizar en jQuery, siempre
empezamos con el signo de dólar y paréntesis: $ () . Cualquier cosa que podemos utili-
zar en una hoja de estilo también se puede situar entre comillas y dentro de paréntesis,
permitiéndonos aplicar métodos jQuery al conjunto coincidente de elementos. Desarrolladores jQuery responsables deberían siempre aplicar los conceptos de mejora
progresiva y degradación elegante a su código, asegurando que una página se mostrará
igual de precisa con JavaScript desactivado como lo hace con JavaScript activado.
Continuaremos explorando estos conceptos a lo largo del libro.
<li>Comedies
<ul>
Tres bloques principales de estos selectores son nombre etiqueta, ID, y clase. Se pue-
<li><a href="/asyoulikeit/n>As You Like It</a></li>
den utilizar de forma independiente o en combinación con otros selectores. La tabla 2.1 eli>All/s Well That Ends Well</li>
muestra un ejemplo del aspecto que tiene cada uno de estos tres selectores. eli>A Midsurnrner Night/s Drearne/li>
11m 2.Selectores
Aprende jQuery 1.3 ••
</ul>
</li> Como se ha tratado en el capítulo anterior, empezamos el código jQuery con el
</ul>
envoltorio $ (document) . ready () r que se ejecuta tan pronto como se ha cargado el
Observe que la primera -cu L» tiene un ID de selected-plays, pero ninguna de DOM.
las etiquetas < 1 i > tiene una clase asociada. Sin ningún estilo aplicado, la lista se parece La segunda línea utiliza el combinador hijo (» para añadir la clase horizontal
a la figura 2.1: a todos los elementos de nivel superior únicamente. En efecto, el selector dentro de la
función $ () dice, encontrar todo elemento de lista (li) que es un hijo (» del elemento con un
• Comedies ID de selected-plays (#selected-plays).
o As You Like It
o All's Well That Ends Well
Con la clase ahora aplicada, nuestra lista anidada se parece a la figura 2.2:
o A Midsummer Night's Dream
o 1We1fth Night
• Tragedies Comedíes Tragedles Histories
o Hamlet
o As You Uke lt o Hamlet o Henry N (emall)
o Macbeth o AIt's Well That Ends Well o Macbeth • Part ¡
o Romeo and Juliet o A Midsummer NIght's Dream o Romeo and Juliet • Part n
• Histories o lWelfth Night o HenrvV
o Henry IV (email) o Richard II
• Pan!
·PanlJ
o HenryV Figura 2.2. Aplicar la clase horizontal a la lista.
o Richard lJ
Aplicar estilo a los otros elementos, los que no se encuentran en el nivel superior, se
Figura 2.1. Lista sin estilo. puede realizar de varias formas.
Puesto que ya hemos aplicado la clase horizontal a los elementos de nivel supe-
La lista anidada aparece como esperaríamos que lo hiciera, un conjunto de elementos rior, una forma de seleccionar todos los elementos por debajo del nivel es utilizar una
con boliches organizados verticalmente y sangrados de acuerdo a su nivel. pseudo-clase de negación para identificar todos los elementos de lista que no tienen una
clase horizontal.
Aplicar estilo a niveles de elementos de lista Observe la incorporación de la tercera línea de código:
(document) .ready(function() {
Supongamos que deseamos que los elementos de nivel superior, y solamente los ele- $(/#selected-plays > li/) .addClass(/horizontal/);
mentos de nivel superior, se organicen horizontalmente. Podemos empezar por definir $(/#selected-plays li:not(.horizontal)/) .addClass(/sub-level/);
}) ;
una clase horizontal en la hoja de estilo:
I!'
Esta vez estamos seleccionando todo elemento de lista (1 i) que: Digamos que queremos tener diferentes estilos para diferentes tipos de vínc;ulos.
Primero definimos los estilos en nuestra hoja de estilo:
1. Es un descendiente del elemento con un ID de selected-plays (#selected-
a
plays). color, #OOc; '"
2. No tiene una clase hori zontal (: not ( . hori zontal) ).
a.mailto (
Cuando añadimos la clase sub-level a estos elementos, reciben el fondo sombrea- background, url(images/mail.png) no-repeat right top;
padding-right, 18px;
do definido en la hoja de estilo. Ahora la lista anidada se parece a lo que se muestra en }
la figura 2.3: a .pdflink (
background, url(images/pdf.png) no-repeat right top;
padding-right, 18px;
Comedias Histories }
o o a.henrylink {
o o
background-color, #ff~;
o o
o padding, 2px;
o
o border, lpx solid #000;
<td>Macbeth</td>
<td>Tr~gedy</td>
<td>1606</td>
</tr>
o
o
<tr> ...•
o <td>Romeo and Juliet</td>
o
<td>Tragedy</td>
Figura 2.4. Lista mejorada con clases. <td>1595</td>
</tr>
<tr>
<td>Henry IV, Part I</td>
Selectores personalizados <td>History</td>
<td>1596</td>
</tr>
Para la amplia variedad de selectores CSS,jQuery añade sus propios selectores per- <.tr>
sonalizados. La mayoría de los selectores personalizados nos permiten elegir ciertos <td>Henry V</td>
<td>History</td>
elementos de una línea, por así decirlo. La sintaxis es la misma que la sintaxis de pseu-
<td>1599</td>
do-clase CSS, donde el selector empieza con dos puntos (:). Por ejemplo, para seleccio- </tr>
nar el segundo elemento de un conjunto coincidente de selectores di v con una clase de </table>
horizontal, escribimos esto:
Ahora podemos añadir un estilo a la hoja de estilo para todas las filas de tabla, y uti-
$(1 div.horizontal,eq(l)1) lizar una clase alt para las filas pares:
Observe que: eq ( 1) selecciona el segundo elemento en el conjunto porque la nume- tr
ración de tabla JavaScript es de base cero, lo que significa que empieza con O. Por el con- background-color, #fff;
trario, CSSes de base uno, parla que un selector CSScomo $ (/di v: nth-child (1) /) }
.alt (
seleccionaría todos los selectores di v que son el primer hijo de su padre (en este caso, background-color, #ccc;
sin embargo, utilizaríamos probablemente $ (/di v: first -childj) en su lugar).
Por último, escribimos nuestro código jQuery, anexando la clase a las filas de tabla
Aplicar estilo a filas alternas pares (etiquetas e t r s ):
$ (document) .ready(function() (
Dos selectores personalizados de mucha utilidad en la librería jQuery son : odd y
$(/tr,odd/) .addClass(/alt/);
: even. Echemos un vistazo a cómo podemos utilizar uno de ellos para las bandas bási- }) ;
Observe que podemos ver resultados no deseados si existe más de una tabla en una Selector.es de formulario
página. Por ejemplo, puesto que la última fila en esta tabla tiene un fondo blanco, la pri-
mera fila en la siguiente tabla tendrá el fondo gris "alterno". Una forma de evitar este Cuando.se trabaja con formularios, los selectores personalizados de jQuery pueden
tipo de problema es utilizar el selector :nth - child () en su lugar. Este selector puede facilitar seleccionar simplemente 10'8 elementos que necesitamos. La tabla 2.2 describe
tomar un número, odd, o even como su argumento. Sin embargo, : nth-child () es un conjunto de estos selectores.
el único selector jQuery que es en base uno. Para conseguir las mismas bandas de filas
que hemos realizado antes, y para que sea consistente entre múltiples tablas en un do- Tabla 2.2. Selectores personalizados.
cumento, el código se parecería a esto:
$ (document) .ready(function() (
$(/tr,nth-child(even)/) .addClass(/alt/); : text, :checkbox, Incorporaelementos con un atributotype igual al nombre del
)) ;
: radio, : image, :submit, selector (excluidoslos dos puntos).Por ejemplo, :text selec-
Para un último toque de selector personalizado, supongamos por alguna razón que :reset, :password, :,file ciona <input t.ype t.ext ».
e v v
queremos resaltar cualquier celda de tabla que haga referencia a una de las obras que : input Elementos input, textarea, select, y but ton.
contienen Henry. :button Elementos button y elementos input con un atributotype
Todo lo que tenemos que hacer, después de añadir una clase a la hoja de estilo para igual a button.
que aparezca el texto en negrita y cursiva ( .highlight {font-'(i'·eight :bold;
font-style: i talics; }), es añadir una línea a nuestro código jQuery, utilizando el :enabled Elementos de formularioque están habilitados.
selector : contains () . :disabled Elementos de formularioque están deshabilitados.
$ (document) .ready(function() ( :checked Botonesde opcióny casillasde verificaciónque están seleccio-
$ (/tr,nth-child(even)/) .addClass(/alt/); nadas.
$ (/td:contains(Henry)/) .addClass(/highlight/);
}) ;
:selected Elementos de opción que están seleccionados.
Por lo tanto, ahora podemos ver nuestra hermosa tabla a rayas con las obras de Henry
Como con otros selectores, los selectores de formulario se pueden combinar para
destacadas de forma notable (véase la figura 2.6): mayor especificidad. Podemos, por ejemplo, seleccionar todos los botones de opción
seleccionados (pero no las casillas de verificación) con $ (/ : radio: checked/) o
seleccionar todas las entradas de contraseña y entradas de texto deshabilitadas con
$ (/ : pas sword, : text : di sabled/ ) .Incluso con selectores personalizados, utilizamos
los mismos principios básicos de CSS para crear la lista de elementos coincidentes.
La segunda línea filtra el conjunto de elementos <a> por dos criterios: Como podríamos esperar, los métodos next () y . nextAll () tienen equivalentes:
.prev () y . prevAll () .Además, . siblings () seleccionatodos los otros elementos
1. Deben tener un atributo href con un nombre de dominio (this. hostname). al mismo nivel DOM, con independencia de si vienen antes o detrás del elemento
Utilizamos esta prueba para excluir vínculos mai 1to y otros de su tipo. seleccionadopreviamente.
2. El nombre de dominio con el que vinculan (de nuevo, this. hostname) no
debe coincidir (! =) con el nombre de dominio de la página actual (location. Para incluir la celda original (la que contiene Henry) junto con las celdas que siguen,
hostname). podemos añadir el método. andSelf () :
Más precisamente, el método . f i 1ter () itera por el conjunto coincidente de ele- $ (document) .ready(function() {
$ (/td:contains(Henry)/) .nextAll() .andSelf() .addClass(/highlight/);
mentos, comprobando el valor de retorno de la función. Si la función devuelve f alse, el }) ;
elemento se elimina del conjunto coincidente. Si devuelve true, el elemento se man-
tiene. Para estar seguro, existen una multitud de combinaciones de selectores y métodos
Ahora, echemos un vistazo a nuestra tabla a rayas de nuevo para ver qué más es po- transversales por lo que podemos seleccionar el mismo conjunto de elementos. Aquí,
sible con los métodos transversales. por ejemplo, tiene otra forma de seleccionar cada celda en cada fila donde al menos una
de las celdas contiene Henry:
.'
3 aprovechar esta posibilidad de uso de modo que podamos, en los momentos apropiados,
utilizar las técnicas jQuery que hemos aprendido hasta el momento y otros trucos que
aprenderemos más adelante. Aunque podemos hacer esto con JavaScript, jQuery mejora
y amplía los mecanismos básicos de gestión de eventos para asignar les una sintaxis más
elegante mientras que al mismo tiempo los hacemos más potentes.
ca cuando el DOM está complemente listo para su uso. Esto también significa que todos O bien, podríamos asignar lo desde el propio código JavaScript:
los elementos son accesibles por nuestros scripts, pero no significa que se ha descargado
window.onload doStuff¡
todo archivo asociado. Tan pronto como se ha descargado y analizado el HTML en un =
árbol DOM, el código puede ejecutarse. Ambos enfoques harán que la función se ejecute cuando se carga la página. La ventaja
de la segunda es que el comportamiento está más separado del código.
Para asegurarse de que la página tiene aplicado estilo antes de que se ejecute el código
JavaScript, es una buena práctica situar etiquetas <link z e L» " stylesheet" > antes Observe que cuando asignamos una función como un manejador, utilizamos el nombre
de las etiquetas « scr-í.pt » dentro del elemento «head» del documento. de la función pero omitimos los paréntesis finales. Con los paréntesis, la función se
invoca inmediatamente; sin ellos, el nombre simplemente identifica la función, y se
Considere, por ejemplo, una página que presenta una galería de imágenes; dicha puede utilizar para invocarla más tarde.
página puede tener muchas imágenes grandes, que podemos ocultar, mostrar. mover y
manipular con jQuery. Si configuramos nuestra interfaz utilizando el evento onload, los
Con una función, esta estrategia funciona bastante bien. Sin embargo, suponga que
usuarios tendrán que esperar hasta que cada una de las imágenes se ha descargado por
tenemos una segunda función:
completo antes de que puedan utilizar la página. Incluso peor, si los comportamientos
todavía no se han anexado a elementos que tienen comportamientos predeterminados function doOtherStuff() (
11 Realizar otra tarea.
(como los vínculos), las interacciones de usuario podrían producir resultados no desea-
dos. Sin embargo, cuando utilizamos $ (document) . ready () para la configuración,
la interfaz se prepara mucho antes con el comportamiento correcto. Luego podríamos tratar de asignar esta función para que se ejecute cuando se carga
la página:
window.onload = doOtherStuff;
Sin embargo, esta asignación supera a la primera. El atributo. onload puede alma-
Utilizar $ (documen t) . ready () es casi siempre preferible a utilizar un manejador cenar solamente una referencia de función de cada vez, por lo que no podemos añadir
onload, pero necesitamos recordar que puesto que es posible que no se hayan car- al comportamiento existente. El mecanismo $ (document) . ready () gestiona esta si-
gado los archivos que se soportan, atributos como altura y anchura de imagen no se tuación. Toda llamada al método añade la nueva función a una cola interna de compor-
encuentran necesariamente disponibles en este momento. Si se necesitan, podemos tamientos; cuando se carga la página, se ejecutarán todas las funciones. Las funciones
también elegir implementar un manejador onload (o más probable, utilizarjQuery para se ejecutarán en el orden en que se han registrado.
establecer un manejador para el evento Load); los dos mecanismos pueden coexistir
pacíficamente.
Para ser justos, jQuery no tiene el monopolio de la solución a esta cuestión. Podemos
Múltiples scripts en una página escribir una función JavaScript que forma una nueva función que invoca el manejador
onload existente, luego llama a un manejador pasado. Este enfoque, utilizado por
El mecanismo tradicional para registrar manejadores de eventos por medio de ejemplo por el addLoadEvent () de Siman Willison, evita conflictos entre maneja-
JavaScript (en lugar de añadir atributos de manejador en el HTML), es asignar una fun- dores rivales como hace $ (document) . ready ( ) , pero carece de algunos de los otros
ción al atributo correspondiente del elemento DOM. Por ejemplo, suponga que hubié- beneficios que hemos tratado. Los métodos específicos de navegador como documen t .
ramos definido la función: addEventListener () ydocument. attachEvent () ofrecenfuncionalidadsimiJar,
function doStuff() { pero jQuery nos permite conseguir esta tarea sin preocupamos por inconsistencias de
11 Realizar una tarea ... navegador.
ElII 3. Eventos
Aprende jQuery 1.3 lIlIII
incluida (Prototype). Ahora, en nuestro script personalizado, podemos utilizar ambas
Métodos abreviados para código librerías, pero siempre que necesitamos utilizar un método jQuery, necesitamos utilizar
j Query en lugar de $ como identificador.
La construcción $ (document) . ready () en realidad llama al método. ready () en
El método' '.ready () guarda un tr,¡¡coen la manga para ayudamos en esta situación.
un objeto jQuery que hemos construido desde el elemento DOM documen t. La función
La función de retrollamada que le pasamos puede tomar un solo parámetro: el propio
factory $ () nos proporciona un método abreviado ya que se trata de una tarea común.
objeto jQuery. Esto nos permite renombrarlo de forma efectiva sin miedo a conflictos:
Cuando se invoca sin argumentos, la función se comporta como si se pasara documento
Esto significa que en lugar de: jQuery(document) .ready (function ($) {
1/ Aquí, podemos utilizar $ normal
$ (document) .ready(function() }) ;
// Nuestro código aquí ... f
}) ; o bien, utilizar la sintaxis más corta que hemos aprendido anteriormente:
Además, la función factory puede tomar otra función como argumento. Cuando ha- Eventos sencillos
cemos esto, jQuery lleva a cabo una llamada implícita a . ready () , por lo que para el
mismo resultado podemos escribir: Existen muchos otros momentos aparte de cargar la página en los que podríamos
querer llevar a cabo una tarea. Igual que JavaScript nos permite interceptar el evento
$(function() {
// Nuestro código aquí. de carga de página con -c bo'd'y onload= > o window. onload, proporciona enlaces
11 11
j); similares para eventos iniciados por el usuario como clics del ratón (onc 1i ck), campos
de formulario que se modifican (onchange), y ventanas que cambian de tamaño (on-
Aunque estas otras sintaxis son más breves, los autores recomiendan la versión más resize). Cuando se asignan directamente a elementos en el DOM, estos enlaces tienen
larga para que quede claro lo que está haciendo el código. desventajas similares a las que hemos detallado para onload. Por lo tanto, jQuery ofrece
una forma mejorada de gestionar estos eventos también.
Coexistir con otras librerías
Un sencillo conmutador de estilo
En algunos casos, puede resultar de utilidad utilizar más de una librería JavaScript
en la misma página. Puesto que muchas librerías hacen uso del identificador $ (pues- Para ilustrar algunas técnicas de gestión de eventos, suponga que deseamos que una
to que es corto y adecuado), necesitamos una forma de impedir colisiones entre estos página se muestre en varios estilos diferentes basándose en la entrada de datos de usua-
nombres. rio. Permitiremos que el usuario haga clicen los botones para alternar entre vista normal,
Afortunadamente, jQuery proporciona un método denominado . noConf 1i e t () una vista en la que el texto está limitado a una columna estrecha, y una vista con una
para devolver control del identificador $ a otras librerías. El uso típico de . no Con- impresión mayor para el área de contenido. En un ejemplo del mundo real, un buen ciu-
flict () sigue el siguiente patrón: dadano Web empleará el principio de mejora progresiva aquí. El conmutador de estilo
debería estar oculto cuando JavaScript no está disponible o, mejor aún, debería seguir
<script src:::"prototype.js" type=l1textjjavascripttr></script>
<script src=Ujquery.jsll type""lItext/javascriptll></script> funcionando por medio de vínculos a versiones alternativas de la página. Para los obje-
<script type=tttext/javascriptll> tivos de este tutorial, asumiremos que todos los usuarios tienen activado JavaScript.
jQuery .noConflict (); El código HTML para el conmutador de estilo es de la siguiente forma:
</script>
<script src="rnyscript.jsll type="text/javascript"></script> <div id="switcher">
<h3>Style Switcher</h3>
En primer lugar, se incluye la otra librería (Prototype en este ejemplo). Luego, <div class="button selectedll id=1!switcher-defaultll>
se incluye jQuery, asumiendo $ para su propio uso. A continuación, una llamada a Default
</div>
. noConf 1i e t () libera s, de modo que el control de él regresa a la primera librería
•• 3,Eventos
Aprende jQuery 1,3 lID
<:div class="buttonn id=lIswitcher-narrowlI>
Narrow Colunm el método. bind () . Este método nos permite especificar cualquier evento JavaScr,ipt, y
</div> anexarle un comportamiento. En este caso, el evento se denomina click, y el compor-
cdiv class=l'buttonrr id=,rswitcher-large"> tamiento es una función que consta de nuestra línea anterior:
Large Print '\...
</div> $ (docurnent).ready(function() {
</div> $ (/#switcher-large/) .bind(/click/, function() {
$ {/body/l .addClass (/large/l ;
Combinados con el resto del código HTML de la página y algo de CSS básico, obte- }) ;
}) ;
nemos una página que se parece a la figura 3.1.
Ahora cuando se pulsa el botón, se ejecuta nuestro código y el texto se agranda como
se muestra en la figura 3.2.
Style¡ Switcher
I Dofault
~~
II !I! 'Narrow Column
1.1
largo Prtnt
10:"
,.:,' ~1~~~;,,¿~~i:.~
~~:: 'J >;; , '
A Chrlstmas Carol
Mlndll donl mean lo say mar 1I<now, 01 my own knowledge, whal tIlere Is panlcularty dead about a door-nall. I mlghl haye besn e.o.
Inclinad, myselt, to regard a comn-neü as me deadest plece of lronmongery In the ttade. But the wfsdom 01 our ancestors Is In the
slmile; and my unhaUowed hands shall not distorb 11.cr the Country's done foro You witl therefore permlt me lo rapeat, emphatlcally,
December, 1843.
Figura 3_1. Combinar el conmutador de estilo con código,
Stave 1: Marley's Ghost
Para empezar, pondremos operativo el botón Large Print. Necesitamos un poco de Figura 3.2. Vincular un evento.
CSS para implementar nuestra vista alternativa de la página:
body.large .chapter ( Esto es lo que se necesita para vincular un evento. Las ventajas que hemos tratado
font-size: 1.Semi con el método. ready () se aplican aquí, también. Múltiples llamadas a . bind () co-
existen sin problema, añadiendo comportamiento adicional al mismo evento según sea
Nuestro objetivo, entonces, es aplicar la clase large a la etiqueta -cbody». Esto per- necesario. Esta no es necesariamente la forma más elegante o eficiente de realizar esta
mitirá que la hoja de estilo vuelva a aplicar formato a la página de forma apropiada. tarea. A medida que avanzamos por este capítulo, ampliaremos y mejoraremos este có-
Utilizando lo que hemos aprendido en el capítulo anterior, ya conocemos la sentencia digo en algo de lo que estemos orgullosos.
necesaria para conseguir esto:
Habilitar los otros botones
$ Ubody/) .addClass (/large/);
Ahora tenemos un botón Large Print que funciona según lo esperado, pero nece-
Sin embargo, queremos que esto ocurra cuando se hace clic en el botón, no cuando sitamos aplicar gestión similar a los otros botones (Default y Narrow Column) para
se carga la página como hemos visto hasta el momento. Para hacer esto, presentaremos que realicen sus tareas. Esto es sencillo; utilizamos. bind () para añadir un manejador
"
•• 3. Eventos Aprende jQuery 1.3 ••
el i ck a cada uno de ellos, eliminando y añadiendo clases según sea necesario. El nuevo Hacer clic en Default elimina los nombres de clase de la etiqueta <body>, devolvien-
código se muestra así: do la página a -su forma inicial. .
$ (document) .ready(function() {
$ (/#switcher-default/) .bind(/click/, function() { Contexto de manejador de-evento
$ (/body/) .removeClass(/narrow/);
$ (/body/) .removeClass(/large/); Nuestro conmutador se está comportando correctamente, pero no estamos proporcio-
}) ;
nando al usuario ninguna información sobre qué botón está actualmente activo. Nuestro
$ (/#switcher-narrow/) .bind(/click/, function() {
$ (/body/) .addClass(/narrow/); enfoque para gestionar esto será aplicar la clase selected al botón cuando se hace clic,
$ (/body/) .removeClass(/large/); y eliminar esta clase de los otros botones. La clase selected simplemente pone el texto
j); del botón en negrita:
$ (/#switcher-large/) .bind(/click/, function()
$ (/body/) .removeClass(/narrow/); .selepted {
$ (/body/) .addClass(/large/); font-weight: bold;
}) ;
}) ;
Situar esta línea en cada uno de los tres manejadores añadirá la clase cuando se hace
A Christmas Carol
clic en un botón. Para eliminar la clase de los otros botones, podemos aprovechamos de
la característica de iteración implícita de jQuery, y escribir:
In Prosa, Being a Ghost Story of Chl'istmas
by Charles Dlekens $(/#switcher .button/) .removeClass(/selected/);
Pmface
Esta línea elimina la clase de cada botón dentro del conmutador de estilo. Por lo tanto,
I HAVE endeavoured In thls GhosUy IIWe book, lo ralse Ihe Ghost 01 an
Idea, WhICh shall Mal put rny readers out of humour wlth themselves, wlth al situados en el orden correcto, tenemos el código como:
eaeh other, wlth the season, or wlth me. May it haunt thelr houses
pleasantly, and no ane wlsh lo ley It. $ (document) .ready(function() {
Thelr faithful Friend and Servant, $ (/#switcher-default/) .bind(/click/. function() {
$ (/body/) .removeClass(/narrow/);
C. D.
$ (/body/) .removeClass(/large/);
December, 1843.
$(/#switcher .button/) .removeClass(/selected/);
5tave 1: Marley', Ghost $(this) .addClass(/selected/);
}) ;
MARLEY was oeac: to begln wlth. Therels no doubl whatever about that
The reglster of hls bunal was slgned by Ille clergyman. Ille elell<. Ille $ (/#switcher-narrow/) .bind(/click/, function() {
undartaxer, and the chlef mourner. Scrooge slgned it: and Scrooge's name $ (/body/) .addClass(/narrow/);
was good upon 'Changa, tor anythlng he chose lo put hls hand to. Old
Mariey W8S as dead as B door-nau.
$ (/body/) .removeClass(/large/);
$(/#switcher .button/) .removeClass(/selected/);
Figura 3.3. Vincularcomportamientoa los otros botones. $(this) .addClass(/selected/);
ElII 3. Eventos
Aprende jQuery 1.3 ••
}) ;
más eficientes. Podemos tener en cuenta la rutina destacada en un manejador aparte,
como se muestra en el siguiente código, porque es el mismo para los tres botones:
Observe que el orden de las operaciones ha cambiado un poco para acomodar la eli-
$ (document) .ready(function() { minación general de nuestra clase; necesitamos ejecutar. removeClass () primero de
$ (/#switcher-default/) .bind(/click/, function() { modo que no deshaga el . addClass () que realizamos al mismo tiempo.
$ (/body/) .removeClass(/narrow/) .removeClass(/large/);
}) ;
$ (/#switcher-narrow/) .bind(/click/, function() {
$ (/body/) .addClass(/narrow/) .removeClass(/large/);
}) ;
$ (/#switcher-large/) .bind(/click/, function() { Solamente podemos eliminar de forma segura todas las clases porque estamos a cargo
$ (/body/) .removeClass(/narrow/) .addClass(/large/); del HTML en este caso. Cuando escribimos código para reutilizar (como para un plug-in),
}) ;
necesitamos respetar cualquier clase que pudiera estar presente y dejarla intacta.
$(/#switcher .button/) .bind(/click/, function() (
•• 3. Eventos
Aprende jQuery 1.3 ••
Ahora estamos ejecutando parte del mismo código en cada uno de los manejadores $ (document) .ready(function 11 {
de los botones. Esto se puede separar fácilmente en nuestro manejador general click $(/#switcqer .button/) .click(function()
$ (/body/) .removeClass();
de botón: if (th~s.id == /switcher-narrow/) (
$I!body /) .addClass I/narrow.':) ;
$ (document) .ready(function() { } .
$(/#switcher .button/) .bind(/click/. function() (
else if (this.id == /switcher-large/)
$ (/body/) .removeClass();
$ (/body/) .addClass(/large/) ;
$(/#switcher .button/) .removeClass(/selected/);
$ (this) .addClass (/selected/);
}) ;
$(/#switcher .button/) .removeClass(/selected/);
$ (/#switcher-narrow/) .bind(/click/. function() (
$(this) .addClass(/selected/);
$ (/body/) .addClass(/narrow/);
}) ;
}I; }) ;
$ (/#switcher-large/l .bind (/click/. function () (
$ (/body/) .addClass(/large/);
Los métodos de evento abreviados como éste existen para todos los eventos DOM
}) ;
estándar: .
}) ;
• blur
Observe que necesitamos mover el manejador general por encima del específico
ahora .. removeClass () necesita ocurrir antes de . addClass (), y.podemos contar • change
con esto porque jQuery siempre activa manejadores de evento en el orden en que se re- • click
gistraron.
Por último, podemos libramos de los manejadores específicos por completo al hacer • dblclick
uso una vez nuevamente del contexto de evento. Puesto que la palabra clave de con- • error
texto this nos proporciona un elemento DOM en lugar de un objeto jQuery, podemos
utilizar las propiedades DOM nativas para determinar el ID del elemento sobre el que
• focus
se hico clic. Podemos así vincular el mismo manejador a todos los botones, y dentro del • keydown
manejador llevar a cabo diferentes acciones para cada botón: • keypress
$ (document) .ready(function()
$(/#switcher
{
.button/) .bind(/click/. function()
• keyup
$ (/body/) .removeClass(); • load
if (this.id == /switcher-narrow/)
$ (/body/) .addClass(/narrow/); • mousedown
}
else if (this.id == /switcher-large/) • mousemove
}
$ (/body/) .addClass(/large/);
• mouseout
$(/#switcher .button/) .removeClass(/selected/); • mouseover
$(this) .addClassl/selected/);
}) ; • mouseup
». • resize
• scroll
Eventos abreviados
• select
Vincular unmanejador para un evento (como un sencillo evento c l í.ck) es una tarea • submit
tan sencilla que jQuery proporciona una forma más concisa de logrario; los métodos de
evento abreviados funcionan de la misma forma que sus equivalentes. bind () con • unload
algunas menos teclas del teclado. Por ejemplo, nuestro conmutador de estilo se podría Todo método abreviado vincula un manejador al evento con el nombre correspon-
escribir utilizando. click () en lugar de . bind () de la siguiente forma: diente.
lIl.!II 3. Eventos
,
!
Aprende jQuery 1.3 ••
Eventos compuestos I Después del primer clie, los botones se ocultan todos, como se muestra en la figura
siguiente.
La mayoría de los métodos de gestión de eventos de jQuery corresponden directa-
mente a eventos JavaScript nativos. Sin embargo, unos cuantos son manejadores perso-
'-.
nalizados añadidos por conveniencia y optimización entre navegadores. Uno de estos, el
método. ready ( ), ya lo hemos tratado en detalle. Los métodos. toggle () y . hover ()
Figura 3.5. Ocultartodos los botones.
son dos manejadores de evento personalizados; ambos se conocen como manejadores
de evento compuestos porque interceptan combinaciones de acciones de usuario, y res-
Un segundo clic los hace de nuevo visibles, como se ve en la figura 3.6.
ponden a ellas utilizando más de una función.
El método de evento. toggle () toma dos o más argumentos, cada uno de los cuáles
Destacar elementos sobre los que se hace die
es una función. El primer clie en el elemento hace que se ejecute la primera función, el Al ilustrar la posibilidad del evento el i ck de operar sobre elementos de página sobre
segundo clic activa la segunda función, y así sucesivamente. Una vez que se ha invocado los que normalmente no se hace clic, hemos diseñado una interfaz que nos proporciona
cada función, el ciclo empieza de nuevo desde la primera función. Con. toggle (), po- algunas pistas de que los botones, en realidad sólo los elementos <di V>, son en reali-
demos implementar nuestro conmutador de estilo que se pliega bastante fácilmente: dad partes vivas de la página, en espera de la interacción del usuario. Para remediar
$ (document) .ready(function() { esto, podemos asignar a los botones un estado rollover, dejando claro que interactúan
$ (!#switcher h3!) .toggle (function () { de alguna forma con el ratón:
$(!#switcher .button!) .addClass(!hidden!);
l. function () { #switcher .hover {
$(!#switcher .button!) .removeClass(!hidden!); cursar: pointer¡
}) ; background-color, #afa;
}) ;
fiII 3, Eventos
!
Aprende jQuery 1.3 m-.
cdiv class=f'foo">
La especificación CSS incluye una pseudo-clase denominada: hover, que permite cspan class,.=l1bar >
ll
que una hoja de estilo afecte la apariencia de un elemento cuando el cursor del ratón del <a href=lIhttp://www.example.comjll>
usuario pasa por encima de él. En Internet Explorer 6, esta posibilidad de uso está res- The Ruick brown fox jumps ayer the lazy dog,
tringida a los elementos de vínculo, por lo que no podemos utilizarla para otros elemen- <la>
</span> '"
tos en código multinavegador. En su lugar, jQuery nos permite utilizar JavaScript para
<r»
cambiar el estilo de un elemento, y por lo tanto, llevar a cabo cualquier acción arbitraria, How razorback-jumping trags can level six piqued gymnasts!
tanto cuando el cursor del ratón entra en el elemento y cuando deja el elemento, <!p>
El método . hover () toma dos argumentos de función, como en nuestro ejemplo <!div>
. toggle () antes. En este caso, la primera función se ejecutará cuando el cursor del
Luego visualizamos el código como un conjunto de elementos anidados como se
ratón entra en el elemento seleccionado, y la segunda se activa cuando el cursor se aleja,
muestra en la figura 3.8.
Podemos modificar las clases aplicadas a los botones en estos momentos para conseguir
un efecto rollover:
$ (document) ,ready(function() { <div>
$(!#switcher ,button!) ,hover(function()
$(this) .addClass(!hover!);
}, function () (
$(this) ,removeClass(!hover!);
}I ;
.'
}) ;
El uso de . hover () también significa que evitamos dolores de cabeza causados por
<div>
propagación de evento en JavaScript. Para entender esto, necesitamos echar un vistazo
a cómo JavaScript decide qué elemento gestiona un evento dado.
El viaje de un evento
Cuando ocurre un evento en una página, toda una jerarquía de elementos DOM tiene
Figura 3.9. Captura de eventos.
la oportunidad de gestionar el evento. Considere un modelo de página como éste:
Aprende jQuery 1.3 ••
•• 3. Eventos
manejador de evento. Esta secuencia de burbujeo es probable que no se desee; para los
botones en nuestro conmutador de estilo de ejemplo, podría significar que el que apa-
rezca resaltado se desactive prematuramente.
Técnicamente, en implementaciones de navegador de captura de evento, elementos
El método . hover () es consciente de estos problemas de burbujeo, y cuando utili-
específicos se registran para escuchar eventos que ocurren entre sus descendientes. La
zamos ese método para anexar eventos, podemos ignorar los problemas causados por
aproximación proporcionada aquí está lo bastante cerca de nuestras necesidades.
el elemento erróneo con un evento mouseover o mouseout. Esto hace que. hover ()
sea una alternativa muy atractiva para vincular los eventos individuales de ratón.
La estrategia contraria se denomina burbujeo de eventos (event bubbling). El evento se
envía al elemento más específico, y después de que este elemento tiene una oportunidad
de reaccionar, el evento burbujea a los elementos más generales. En nuestro ejemplo,
se pasa primero el evento a <a>, y luego a -c apa n» y <di v » en ese orden, como ilustra Si solamente se tiene que realizar una acción cuando el ratón entra o abandona un
la figura 3.10. elemento, pero no ambos, podemos vincular los eventos mouseenter y mouseleave
de jQuery, que también eluden los problemas de burbujeo. Sin embargo, estos eventos
se emparejan tan a menudo, que. hover () es por lo general la opción correcta.
<div>
r-~--="""'" El escenario mouseout que se acaba de describir ilustra la necesidad de limitar el
ámbito de aplicación de un evento. Aunque. hover () gestiona este caso específico, en-
contraremos otras situaciones en las que necesitamos limitar un evento espacialmente
(impidiendo que el evento se envíe a ciertos elementos) o temporalmente (impidiendo
que el evento se envíe en ciertos momentos).
Puesto que se encuentra arriba de la jerarquía, ningún otro elemento recibe el evento. manejador se activa y oculta los botones.
Por otro lado, cuando el cursor sale del elemento <a>, se le envía un evento mouseout. Para solucionar este problema, necesitamos acceder al objeto event. Ésta es una
Este evento luego burbujeará hasta el <spari» y luego al <di v», activando el mismo construcción JavaScript que se pasa al manejador de evento de cada elemento cuando
r
m. 3. Eventos
.: Aprende jQuery 1.3 ••
se invoca. Proporciona información sobre el evento, como dónde se encontraba el cur- $ (document) .ready(function(} {
$(/#switcher .button/) .click(function(event)
sor del ratón en el momento del evento. También proporciona algunos métodos que se $ (/body/) .removeClass();
pueden utilizar para afectar el progreso del evento por el DOM. if (thás.id == /switcher-narrow/)
Para utilizar el objeto event en nuestros manejadores, solamente necesitamos añadir $ (íbody/) . addClass (/narro~i) ;
un parámetro a la función: }
el se if (this.id == /switcher-large/)
$ (document) .ready(function() $ (/body/) .addClass(/large/);
$ (/#switcher/) .click(function(event} {
$(/#switcher .button/) .toggleClass(/hidden/};
}} ; $(/#switcher .button/) .removeClass(/selected/};
)} ; $(this) .addClass(/selected/);
event.stopPropagation{)¡
II ; .
Destinos de los eventos }) ;
Detener la propagación de evento Invocar. preventDefaul t () es a menudo de utilidad después de haber realizado
algunas pruebas sobre el entorno del evento. Por ejemplo, durante el envío de un
El objeto event proporciona el método. stopPropagation (), que puede detener formulario, podríamos desear comprobar que los campos obligatorios están rellenos,
el proceso de burbujeo completamente para el evento. Como . target, este método es e impedir la acción predeterminada sólo si no lo están.
una característica JavaScript sencilla, pero no se puede utilizar de forma segura entre
todos los navegadores. Sin embargo, siempre y cuando registramos todos nuestros ma- La propagación de evento y acciones predeterminadas son mecanismos independien-
nejadores de evento utilizando jQuery, podemos utilizarlo con impunidad. tes; se puede detener cualquiera mientras ocurre la otra. Si deseamos detener ambas, po-
Eliminaremos la comprobación event. target == this que hemos añadido, y en demos devolver fal se desde nuestro manejador de evento, que es un método abreviado
su lugar añadiremos algo de código en los manejadores el i ck de nuestros botones: para invocar. stopPropagat ion () y . preventDefaul t () sobre el evento.
f
Aprende jQuery 1.3 ••
ElII 3. Eventos
lizar la propiedad event. target para comprobar qué elemento estaba bajo el cursor $ (document) .ready(function() (
$ (/#switcher/) .click(function(event) (
del ratón cuando ocurrió el clic.
if ($ (event.target) .is(/.button/)) (
$ (/body/) .removeClass();
$ (document) .ready (function () (
if (event.target.id == /switcher-narrow/)
$ (/#switcher/) .click(function(event) (
$ (/body/) .addClass(/narrow/);
if ($ (event.target) .is(/.button/)) {
$ (/body/) .removeClass() ;
}
if (event.target.id == /switcher-narrow/) else if (event.target.id == /switcher-large/)
$ (/body/) .addClass(/large/);
$ (/body/) .addClass(/narrow/);
}
}
$(/#switcher .button/) .removeClass(/selected/);
el se if (event.target.id == /switcher-large/)
$(event.target) .addClass(/selected/);
$ (/body/) .addClass(/large/);
}
} }) ;
$(/#switcher .button/) .removeClass(/selected/);
}) ;
$ (event.target) .addClass(/selected/);
event.stopPropagation() ;
Este ejemplo es demasiado complicado para su tamaño, pero como el número de
}
}) ;
elementos con manejadores de evento aumenta, la delegación de evento es la técnica
}) ;
correcta a utilizar.
.mil 3. Eventos Aprende jQuery 1.3 ••
}) ;
Existen momentos en los que habremos terminado con un manejador de evento que $.I/#swi tcher-narrow, ·#switcher-large/l. click Ifunction 1)
$I/#switcher/) .unbindl/click.collapse/);
hemos registrado previamente. Quizá el estado de la página ha cambiado tanto que la
}J;
acción ya no tiene sentido. Normalmente es posible gestionar esta situación con senten- }J;
cias condicionales dentro de nuestros manejadores de evento, pero puede ser más ele-
gante quitar la vinculación del manejador por completo. El sufijo. eollapse es invisible para el sistema de gestión de evento; los eventos
Suponga que queremos que nuestro conmutador de estilo que se puede contraer per- el iek se gestionan por esta función, igual que si escribiéramos. bind (1 eliek/) . Sin
manezca expandido siempre que la página no utiliza el estilo normal. Aunque el botón embargo, la incorporación del espacio de nombres significa que podemos desvincular sólo
Narrow Column O Large Print está seleccionado, hacer clíc en el fondo del conmutador este manejador, sin afectar al otro manejador eliek que escribimos para los botones.
de estilo no debería hacer nada. Podemos realizar esto al invocar el método. unbind ( )
para eliminar el manejador para contraer cuando se hace clic en uno de los botones no
predeterminados del conmutador de estilo.
$Idocument) .readylfunctionl) ( Existen otras formas de hacer que nuestra llamada . unbind () sea más específica,
$I/#switcher/) .clicklfunctionlevent) (
if 1!$levent.target) .isl/.button/)) (
como veremos en un momento. Sin embargo, el espacio de nombres de evento es una
$I/#switcher .button/) .toggleClassl/hidden/); herramienta de utilidad en nuestro arsenal. Es especialmente de utilidad en la creación
} de plug-ins, como veremos más adelante en el libro.
}) ;
$ (/#switcher/) .click(toggleStyleSwitcher);
Observe que aquí estamos utilizando una nueva sintaxis para definir una función. En
$ (/#switcher-narrow, #switcher-large/) .click(function()
lugar de definir la función al empezar con la palabra clave function, asignamos una $ (/#switcher/) .unbind(/click/, toggleStyleSwitcher);
función anónima a una variable local. Se trata de una elección de estilo para hacer que }) ;
nuestros manejadores de evento y otras definiciones de función se parezcan más entre $ (/#switcher-default/) .click(funebion() (
$ (/#switcher/) .click(toggleStyleSwitcher);
sí; las dos sintaxis son funcionalmente equivalentes. }) ;
También, recuerde que. bind () toma una referencia de función como su segundo }) ;
argumento. Es importante recordar, cuando se utiliza una función con nombre aquí, omi-
tir los paréntesis después del nombre de función; los paréntesis harán que se invoque Un método abreviado se encuentra también disponible para la situación en la que que-
la función en lugar de que se le haga referencia. Ahora que la función tiene un nombre, remos desvincular un manejador de evento inmediatamente después de la primera vez
podemos vincularla nuevamente más adelante sin repetir la definición de función: , que se activa. Este método abreviado, denominado. one ( ) r se utiliza de esta forma:
};
Figura 3.11. Simular la interaccióndel usuario.
•• 3. Eventos I
r Aprende jQuery 1.3 ••
El método. trigger () proporciona el mismo conjunto de métodos abreviados que $ (/#switcher-default/) .click();
.bind () . Cuando se utilizan estos métodos abreviados sin argumentos, el comporta- break;
miento es activar la acción en lugar de vincularla: case /N/:
.~(/#switcher-narrow/) .click();
$ (document) .ready(function() break; ~'"'tt
Como otro ejemplo, podemos añadir métodos abreviados de teclado a nuestro con-
Pulsar estas tres teclas ahora simula clics del ratón sobre los botones, siempre y cuando
mutador de estilo. Cuando el usuario escribe la primera letra de uno de los estilos, ha-
ese evento de tecla no se interrumpa por características como la de "buscar texto cuando
cemos que la página se comporte como si se hiciera clic sobre el botón correspondiente. empiezo a escribir" de Firefox.
Para implementar esta característica, necesitaremos explorar eventos de teclado, que se
Corno alternativa a utilizar . trigger () para simular este clic, exploremos cómo
comportan algo diferente de los eventos de ratón. Existen dos tipos de eventos de tecla-
separar código en una función de modo que más de un manejador pueda invocarlo, en
do: aquellos que reaccionan al teclado directamente (keyup y keydown) y aquellos que
este caso, el iek y keyup. Aunque no necesario en este caso, esta técnica puede ser de
reaccionan a entrada de texto (keypress). Un solo evento de entrada de ,carácter podría utilidad al eliminar redundancia de código.
corresponder a varias teclas, por ejemplo cuando la tecla Mayús en combinación con
$ (document) .ready(function() {
la tecla X crea la letra mayúscula X. Aunque los detalles específicos de implementación
/1 Permitir efecto de pasar por encima sobre los botones del conmutador de estilo
difieren de un navega dar a otro (no es de sorprender), una regla general es la siguiente: $(/#switcher .button/) .hover(function()
si desea saber qué tecla ha pulsado el usuario, debería observar el evento keyup o ke- $(this) .addClass(/hover/);
}, function () (
ydown; si desea saber qué carácter terminó en pantalla como resultado, debería observar
$(this) .removeClass(/hover/);
el evento keypress. Para esta característica, simplemente queremos saber cuando el }) ;
usuario pulsa la tecla D, N, o L, por lo que utilizaremos keyup. // Permitir que el conmutador de estilo se amplie o colapse
A continuación, necesitamos determinar qué elemento debería estar pendiente del var toggleStyleSwitcher = function(event) {
evento. Esto es un poco menos obvio que con los eventos de ratón, donde tenemos un if (! $ (event. target) .is (/ .button/)) {
$(/#switcher .button/) .toggleclass(/hidden/);
cursar de ratón obvio que nos habla del destino del evento. En su lugar, el destino de un
evento de teclado es el elemento que actualmente tiene el foco del teclado. El elemento };
con foco se puede cambiar de varias formas, incluidos clics del ratón y pulsaciones de la $ (/#switcher/) .click(toggleStyleSwitcher);
tecla Tab. No todos los elementos pueden tener el foco, tampoco; solamente los elemen- // Simular un clic de modo que empezamos en un estado colapsado
$ (/#switcher/) .click();
tos que tienen comportamientos predeterminados dirigidos por teclado como campos
de formulario, vínculos y elementos con una propiedad. t ab tndex-son candidatos. // La función setBodyClass() cambia el estilo de página.
En este caso, realmente no nos importa qué elemento tiene el foco; queremos que // El estado del conmutador de estilo también se actualiza.
nuestro conmutador funcione siempre que el usuario pulsa una de las teclas. El burbujeo var setBodyClass = function(className) (
$ (/body/) .removeClass() ;
de eventos será de nuevo de utilidad, ya que podemos vincular nuestro evento keyup $ (/body/) .addClass(clasSName);
al elemento doeument y tener la seguridad que con el tiempo cualquier evento de tecla $(/#switcher .button/) .removeClass(/selected/);
burbujeará hasta nosotros. $(/#switcher-/ + className) .addClass(/selected/);
Por último, necesitaremos saber qué tecla se pulsó cuando nuestro manejador keyup if (className == /default/) (
$ (/#switcher/) .click(toggleStyleSwitcher);
se activa. Podemos inspeccionar el objeto event para esto. La propiedad . keyCode del }
evento contiene un identifica dar para la tecla que se ha pulsado, y para teclas alfabéticas, else (
este identificador es el valor ASCII de la letra mayúscula. Por lo tanto podemos habilitar $ (/#switcher/) .unbind(/click/, toggleStyleSwitcher);
el valor y activar el clic de botón apropiado: $(/#switcher .button/) .removeClass(/hidden/);
$ (document) .ready(function() ( };
$ (document) .keyup(function(event) {
switch (String.fromCharCode(event.keyCode)) // Invocar setBodyClass() cuando se hace clic en un botón
case /D/: $ (/#switcher/) .click(function(event) {
,
Aprende jQuery 1.3 ••
•• 3. Eventos
Resumen
Las posibilidades de uso que hemos tratado en este capítulo nos permiten:
• Permitir que múltiples librerías JavaScript coexisten en una sola página utilizando
. noConflict ().
• Utilizar manejadores de evento de ratón para reaccionar al clic de un usuario
sobre un elemento de página con. bind () o . click () .
• Observar contexto de evento para llevar a cabo diferentes acciones dependiendo
del elemento de página que se hace clic, incluso cuando el manejador está vincu-
lado a varios elementos.
• Alternativamente ampliar y contraer un elemento de página al utilizar. t o-
ggle ().
• Resaltar elementos de página bajo el cursor del ratón al utilizar . hover ( ) .
• Influir en la propagación de evento para determinar qué elementos obtienen
respuesta a un evento al utilizar . s topPropaga t ion () y . preventDe-
fault(}.
,
, I
"'" . ·tá' !
.~ ~,~?•.f~(~'
·QP\ /'W \
.: :""'>',,,-,
\-~! 1"\
@r.-...., '"
/ : .'~\-(!)".,.:
·F"··
ti~,
''''1F
""
. -, ~, . --", ,~ '\
~...,'
\~, ~it·,/
~~-r.
{, '" ()
o~'~ ®
,_"
.'
Si las acciones dicen más que las palabras, entonces en el mundo JavaScript, los efectos
hacen que las acciones digan aún más. Con jQuery, podemos añadir fácilmente impac-
4
to a nuestras acciones por medio de un conjunto de sencillos efectos visuales, e incluso
diseñar nuestras propias animaciones más sofisticadas.
Los efectos jQuery añaden ciertamente atractivo, como es evidente cuando vemos
elementos que aparecen gradualmente en lugar de aparecer todos a la vez. Sin embargo,
también pueden proporcionar importantes mejoras de usabilidad que ayudan a orientar
fectos al usuario cuando existe algún cambio en una página (especialmente común en aplica-
ciones AJAX), En este capítulo, exploraremos una serie de estos efectos y los combina-
remos de formas interesantes,
cadena, como. css ( backgroundColor I ') .Las propiedades de múltiples palabras se En esta versión del conmutador de estilo, estamos utilizando elementos <butt;on:>.
pueden interpretar por jQuery cuando tienen guión, ya que se encuentran en notación Hacer clic en los botones Bigger y Smaller aumentará o disminuirá el tamaño de texto
CSS (background- color), o poniendo en mayúscula la primera letra de cada palabra de <di v cla~s;" speech" », mientras que hacer clic en el botón Default restablecerá
excepto la primera, como en notación DOM (backgroundColor). Para establecer pro- <di v c Las s e " speecnv s a su tamaño de texto original.
piedades de estilo, el método. css () se presenta de dos formas, una que toma una sola Si todo lo que quisiéramos fuera cambiar el tamaño de fuente una sola vez a un valor
propiedad de estilo y su valor y otra que toma un mapa de pares propiedad-valor: predeterminado, podríamos seguir utilizando el método. addClass () . Pero supon-
gamos que ahora queremos que el texto continúe aumentando o decreciendo incremen-
.css('propertY','value') talmente cada vez que se hace clic en el botón respectivo. Aunque podría ser posible
.css({propertyl: 'valuel', 'property-2': 'value2'}) definir una clase aparte para cada clic y pasar por ellos, un enfoque más sencillo sería
Desarrolladores JavaScript experimentados reconocerán estos mapas jQuery como calcular el nuevo tamaño de texto cada vez al obtener el tamaño actual y aumentarlo en
un factor establecido (por ejemplo, 40 por ciento).
objetos literales en JavaScript.
Nuestro código empezaría con los manejadores de evento $ (document) . ready ()
y $ ( #swi t.che r= Lar-qe ') . click () :
I
$ (document) .ready(function() (
$('#switcher-large') .click(function()
Losvaloresnuméricos no toman comillasmientras que los valoresde cadenasílo hacen. }) ;
Sinembargo, cuando se utiliza la notación de mapa, las comillasno son necesarias para }) ;
Utilizamos el método. css () de igual forma que hemos estado utilizando. add- valor devuelto incluirá un px final, necesitaremos retirar esa parte para llevar a cabo
I I
Class () ; es decir al encadenarlo a un selector y vincularlo a un evento. Para demostrar cálculos con el valor. Igualmente, cuando pretendemos utilizar un objeto jQuery más
esto, regresaremos al ejemplo del conmutador de estilo del capítulo anterior, pero con de una vez, generalmente es una buena idea guardar en caché el selector al almacenar
diferente HTML: el objeto jQuery resultante en una variable también.
cdiv id=11switcher'l> $ (document) .ready(function() (
cdiv class="lahel'I>Text Sizec/div> var $speech = $('div.speech')¡
<buttan id= switcher-default
11 >Defaultc/button>
11 $('#switcher-large') .click(function()
<buttan id="switcher-large >Biggerc/button>
ll
var num = parseFloat($speech.css('fontSize'), 10);
<buttan id=llswitcher-small">Smallerc/button> }) ;
</div> }) ;
cdiv class=l'speech">
<p>Fourscore and seven years ago our fathers brought forth La primera línea dentro de $ (document) . ready () ahora almacena una variable
on this continent a new nation, conceived in liberty,
para <di v c Las s« "speech":>. Observe el uso de un $ en el nombre de la variable,
and dedicated to the proposition that all men are created
$speech. Puesto que $ es un carácter legal en variables JavaScript, podemos utilizarlo
equal.</p>
</div> como un recordatorio de que la variable está almacenando un objeto jQuery.
Dentro del manejador . cl ick ( ) , utilizamos par ses'Loat; () para obtener solamente
Al vincular a una hoja de estilo con algunas reglas de estilo básicas, la página inicial- el número de la propiedad del tamaño de fuente. La función parseFloat () mira una
mente se parece a la figura 4.1. cadena de izquierda a derecha hasta que encuentra un carácter no numérico. La cadena
de dígitos se convierte en un número en coma flotante (decimal). Por ejemplo, converti-
Abraham Lincoln's Gettysburg Address ría la cadena '12 en el número 12. Además, quita los caracteres finales no numéricos
I
de la cadena, de modo que 12px pasa a ser 12 también. Si la cadena empieza con un
I I
Text Slze
carácter no numérico, parseFloat () devuelve NaN,que significa No un Número (Not
I EDefault'~@lrgger ~ €smaller}
I a Number). El segundo argumento para parseFloa t () nos permite aseguramos de que
Fourscore and seven years ago our fathers brought forth on thls contlnent a new natlon, conceíved In Ilberty, and dedlcated el número se interpreta como base 10 en lugar de octal u alguna otra representación.
to the proposltlon that all men are created equal.
Todo lo que queda por hacer, si estamos aumentando en un 40 por ciento, es multi-
Figura 4.1. Página con reglas de estilo básicas aplicadas. plicar num por 1 . 4 Yluego establecer el tamaño de fuente al concatenar num y px I I :
IIEII 4. Efectos Aprende jQuery 1.3 ••
$ (document) .ready(function() { Recuerde del capítulo anterior que podemos acceder a la propiedad id del elemento
var $speech = $(!div.speech');
$('#switcher-large') .click(function() {
DOM referenciado por this, que aparece aquí dentro de las sentencias if y else if.
var num = parseFloat($speech.css('fontSizer) I 10 ); Aquí, es más eficiente utilizar thi s que crear un objeto jQuery sólo para comprobar el
num *= 1.4; valor de una propiedad. ,
$speech.css('fontSize', num + 'px l);
Es también interesante tener una furma de devolver el tamaño de fuente a su valor
j);
inicial. Para permitir que el usuario realice esto, podemos simplemente almacenar el
j);
tamaño de fuente en una variable inmediatamente cuando el DOM está listo. Luego
podemos utilizar este valor siempre que se hace die en el botón Default. Para gestionar
este clic, podríamos añadir otra sentencia else if. Sin embargo, quizá una sentencia
y swi tch sería más apropiado.
Laecuaciónnum* = 1 .4 esun métodoabreviadopara num=num* 1.4. Podemosutilizar
(documerit ) . ready (funct ion () {
el mismo tipo de método abreviado para las otras operaciones matemáticas básicas, var $speech = $('div_speech');
también: suma, num+= 1.4; resta, num-'= 1.4; división,num/= 1.4; y módulo (resto var defaultSize = $sp~ech.css(lfontSizel);
de la división),num%=1.4. $('#switcher button') .click(function() {
var num = parseFloat( $speech.css('fontSize'), la );
switch (this.id) {
case 'switcher-large':
Ahora cuando un usuario hace die en el botón Bigger, el texto se hace más grande.
num *= 1.4;
Otro clic, y el texto se hace todavía más grande, como se muestra en la fi~ura 4.2. break;
case 'switcher-small':
num 1= 1.4;
Abraham lincoln'sGettysburg Address
break;
Text Slze
default:
num = parseFloat(defaultSize, 10);
@oefaiHt, E'Big~e~,~,smalfeª,
}
$speech.css('fontSize', num + IpXI);
})
Fourscore and seven years ago our fathers brought forth on j);
;
Para hacer que el botón Smaller reduzca el tamaño de la fuente, dividiremos en lugar
de multiplicar: num / = 1. 4. Mejor aún, combinaremos los dos en un solo manejador
. click () en todos los elementos «but.t.on» dentro de <di v id=" switcher" >. Luego, Métodos básicos ocultar y mostrar
después de averiguar el valor numérico, podemos multiplicar o dividir dependiendo del
ID del botón sobre el que se hizo die. Aquí tiene el aspecto que tiene el código ahora: Los métodos. hide () y . show () básicos, sin ningún parámetro, se pueden conside-
$ (document) .ready(function() {
rar como métodos abreviados para. css ( 'display' r 'string' ), donde' string'
var $speech = $('div.speech'); es el valor de visualización apropiado.
$ ('#switcher button') .click(function() El efecto, como cabría esperar, es que el conjunto de elementos coincidente se ocul-
var num = parseFloat{ $speech.css('fontSize l) I 10 ) i tará o mostrará inmediatamente, sin animación.
if (this.id == 'switcher-large') {
num *= 1.4¡
El método . hide () establece el atributo de estilo en línea del conjunto de elemen-
else if (this.id == 'switcher-small') tos coincidente en di splay: none. Lo bueno de esto es que recuerda el valor de la pro-
num 1= 1.4; piedad display, normalmente block o inline, antes de que se cambiara a none.
Por el contrario, el método . show () restaura el conjunto de elementos coincidentes
$speech.css(!fontSize'{ num + 'px) j
}) ;
a cualquier propiedad de visualización visible que tuvieran antes de que se aplicara
j); display:none.
lIi!II 4. Efectos
Aprende jQuery 1.3 ••
Para más información sobre la propiedad display y cómo se representan sus valores
visualmente en una página Web, visite el Mozilla Developer Center en h t t Ps : / /
I
Text Síze •
<p>Fourscore and seven years ago our fathers brought forth Foursc.ore and seven years ago our fathers brought forth on thls contlnent a new natlon, concelved in liberty, and dedlcated
to the proposltlon that al! men are created equal.
on this continent a new nation, conceived in liberty,
and dedicated to the proposition that all men are Now we are engaged tn a great dvH war, testlng whether that natíon, or any nation so conceived and so dedicated, can long
created equal. endure. We are met on a great battlefleld of that war. We have come to dedlcate a portlon of that fleld as a final
restlng-place for those who here gavethelr uves that the natíon mlght Uve. It Is altogether fitting and proper that we
</p>
should do ttus. But~ in a larger sense, we cannot dedlcate, we cannot consecrate, we cannot hallow, this ground.
<p>Now we are engaged in a great civil war, testing whether
that nation, or any nation so conceived and so dedicated,
Figura 4.4. Mostrar todos los párrafos.
can long endure. We are met on a great battlefield of
that war. We have come to dedicate a portion of that
field as a final resting-place for those who here gave Los métodos. hide () y . show () son rápidos y de utilidad, pero no son muy vis-
their lives that the nation might live. It is altogether tosos. Para añadir atractivo, podemos asignarles una velocidad.
fitting and proper that we should do this. But, in a
larger sense, we cannot dedicate, we cannot consecrate,
we cannot hallow, this ground.
</p>
<a href=II#" clasB="more">read more</a>
Efectos y velocidad
</div>
Cuando incluimos una velocidad (o más precisamente, una duración) con . show ()
Cuando el DOM está listo, el segundo párrafo se oculta: o . hide ( ) , se anima, es decir, ocurre durante un período específico de tiempo. El mé-
$ (document) .ready(function() todo. hide ( speed '), por ejemplo, reduce la altura, anchura y opacidad de un ele-
I
$ ('p: eq (1) ,) .hide () ; mento simultáneamente hasta que todos llegan a cero, en cuyo punto, se aplica la regla
}) ; CSS display: none. El método. show ( speed ') aumentará la altura del elemento
I
de arriba a bajo, anchura de izquierda a derecha, y opacidad de O a 1, hasta que sus con-
y el discurso se parece a lo que aparece en la figura 4.3. tenidos son visibles completamente.
E!II 4. Efectos
Aprende jQuery 1.3 EJI
Aplicar velocidad Esta vez cuando capturamos la apariencia del párrafo a la mitad, se puede parecer a
lo que se muestra en la figura 4.6:
Con cualquier efectojQuery, podemos utilizar una de las tres velocidades preestable-
cidas: 'slow', 'normal', y , fast '. Utilizar. show ( , slow' ) hace que el efecto mos-
trar se complete en .6segundos, . show ( 'normal' ) en.4 segundos, y . show ( , f as t ' )
en .2segundos. Para mayor precisión, podemos especificar un número de milisegundos,
por ejemplo . show ( 850) . A diferencia de los nombres de velocidad, los números no
!Text Slze
I (o::oe.raúlt1 (.'8fgger.1 ( Smaller-)
1
Fourscore and seven years ago our fathers brought forth on thls contlnent a new natlon, concelved In Ilberty, and
dedlcated to the proposltlon that al! men are created equal.
se sitúan entre comillas.
Now we are en9aged in a great ovil war, testing whether that nation, or any nation so coocetved and so dedlcated, can
Incluyamos una velocidad en nuestro ejemplo cuando mostramos el segundo párrafo long endure. We are met on a great battlefleld of that war. We have come to dedlcate a partlon of that field as a final
del Discurso de Gettysburg de Lincoln: restlng-place for those who here gave tnelr uves that the natlon mlght IIve. It is altogether fitting and proper that we
should do thls. But, in a larger sense, we cannot dedica te, we cannot consecrate, we cannor hallow, this ground.
$ (document) .ready(function() (
$ ('p,eq(l)') .hide (); Figura 4.6. Todo el párrafo aparece gradualmente.
$ ('a. more') .click (function () {
$ ( 'p: eq (1) , ) . show ( "sLow ') ; La diferencia aquí es que el efecto. fadeln () empieza al establecer las dimensiones
$ (this) .hide () ;
del párrafo de modo que los contenidos pueden simplemente desvanecerse. Para reducir
return false¡
)) ; gradualmente la opacidad podemos utilizar. fadeOut () .
)) ;
Cuando capturamos la apariencia del párrafo en aproximadamente la mitad del efec- Efectos compuestos
to, vemos algo como lo que se muestra en la figura 4.5:
Algunas veces tenemos una necesidad de alternar la visibilidad de elementos, en
lugar de mostrarlos de una vez como hicimos en el ejemplo anterior. Alternar se puede
Text Slze
tDeftwtrl} (81ggu-) ( Smaller9
1 netlon, concelved In llberty, and dedlcated
conseguir al comprobar primero la visibilidad de los elementos coincidentes y luego al
anexar el método apropiado. Utilizando los efectos de aparecer y desaparecer gradual-
Fourscore and seven years a90 our fathers brought
lo tne proposltton that all men are created equal.
forth on thls contlnent a new mente, podemos modificar el script de ejemplo para que se parezca a esto:
Now we are engsged in a great cívü war; testing whether that $ (document) .ready(function() (
nation, or any nation so concelved and so dedlcated, can long
var $firstPara = $('p,eq(l) ');
$firstPara.hide();
Figura 4.5. Incluir velocidad en el ejemplo. $ ( 'a. more' ) .click (function () {
if ($firstPara.is(' :hidden'))
$firstPara.fadeln('slow') i
su lugar: )) ;
$ (document) .ready(function() Como hemos hecho anteriormente en este capítulo, estamos guardando en caché
$ ('p: eq (1) ,) .hide () ; nuestro selector aquí. Observe también, que ya no ocultamos el vínculo sobre el que se
$('a.more') .click(function() (
ha hecho clic; en su lugar cambiamos su texto.
$ ('p:eq(l)') .fadeln(' a Low r j ¡
$ (this) .hide () ; Utilizar una sentencia if else es una forma perfectamente razonable de alternar la
return false¡ visibilidad de elementos. Pero con los efectos compuestos de jQuery podemos dejar los
)) ; condicionales fuera (aunque, en este ejemplo, seguimos necesitando uno para el texto
)) ;
de vínculo). jQuery proporciona un método. toggle (), que actúa como. show () Y
lImJI 4. Efectos Aprende jQuery 1.3 ••
. h i de () ,y como ellos, se puede utilizar con un argumento de velocidad o sin él. El otro La segunda forma toma dos argumentos, un mapa de propiedades y un mapa de
método compuesto es . slídeTogg1e (), que muestra u oculta elementos al incremen- opciones.
tar o disminuir gradualmente su altura. Aquí tiene el aspecto que tiene el script cuando .animate((properties}, (options})
utilizamos el método. slideTogg1e () : .,
",
De hecho, el segundo argumento engloba desde el segundo al cuarto argumento de la
$ (document) .ready(function() (
var $firstPara = $('p:eq(l) ');
primera forma en otro mapa, y añade dos opciones más a la mezcla. Cuando ajustamos
$firstPara.hide() ; los saltos de línea por legibilidad, la segunda forma se parece a esto:
$ ('a.more') .click(function() (
.animate ({
$firstPara.slideToggle('alow');
propertyl: Ivaluel' ,
var $1ink = $(this);
property2: 'value2'
if ( $link. text () == "read more" ) {
), (
$link.text{'read lessl);
duration: 'value',
el se {
easing: value
I I ,
$link.text('read more');
complete: function () 3
alert{'The animation is finished. ') i
return false¡
},
}) ;
queue: boolean,
}) ;
step: callback
}) ;
Esta vez $ (thís) se habría repetido, por lo que lo estamos almacenando en la varia-
ble $1 Lnk por rendimiento y legibilidad. También, la sentencia condicional comprueba Por ahora, utilizaremos la primera forma del método. anímate (), pero regresaremos
el texto del vínculo en lugar de la visibilidad del segundo párrafo, ya que solamente lo a la segunda forma más adelante en el capítulo cuando tratemos los efectos en cola.
estamos utilizando para cambiar el texto.
Alternar el aparecer y desaparecer paulatino
Crear animaciones personalizadas Cuando hemos tratado los efectos compuestos, ¿ha observado que no todos los mé-
todos tienen un método correspondiente para alternar? Correcto: aunque los métodos
Además de los métodos de efectos ya incorporados, jQuery proporciona un potente de desplazamiento incluyen. sl ídeTogg1e (), no existe. fadeTogg1e () correspon-
método. anímate () que nos permite crear nuestras propias animaciones personaliza- diente para continuar con . f ade 1n () y . f adeOu t ( ) . La buena noticia es que podemos
das con control detallado. El método. anímate () se presenta de dos formas. La pri- utilizar el método. anímate () para crear nuestra propia animación de alternar. Aquí,
mera toma cuatro argumentos: reemplazaremos la línea. slídeTogg1e () del ejemplo anterior con nuestra animación
personalizada:
1. Un mapa de propiedades de estilo y valores, similar al I).1apa. css () tratado $ (document) .ready(function() (
anteriormente en este capítulo. - " $ ('p: eq (1) ,) .hide () ;
$('a.more') .click(function() {
2. Una velocidad opcional, que puede ser una de las cadenas preestablecidas o un $ ('p: eq (1) ,) .animate ({opacity: 'toggle'}, 'slow');
número de milisegundos. var $link = $(this);
if ( $link. text () == "read more" ) (
3. Un tipo de mejora opcional. $link.text('read less');
else (
4. Una función de rellamada opcional, que se tratará más adelante en este capí- $link.text('read more');
tulo.
return false¡
Juntos/los cuatro argumentos se parecen a esto:
»;
}) ;
.animate((propertyl: 'valuel', property2: 'value2'},
speed, easing, function() (
alert('The animation is finished. '); Como ilustra el ejemplo, el método. anímate () proporciona valores abreviados con-
venientes para propiedades CSS, ' show', h i de " Y togg1e " para facilitar el camino
1 1
) ;
cuando los métodos abreviados no son del todo apropiados para la tarea determinada.
lIlllII 4. Efectos Aprende jQuery 1.3 lmII
}) ;
Cuando se trabaja con. animate (), es importante recordar las limitaciones que
impone CSS en los elementos que deseamos cambiar. Por ejemplo, ajustar la propiedad
Las propiedades adicionales nos permiten crear efectos mucho más complejos, tam- 1eft no tendrá efecto en los elementos coincidentes a menos que esos elementos tengan
bién. Podemos, por ejemplo, mover un elemento desde el lado izquierdo de la página al su posiciónCSSestablecida en relati ve o abso1ute. La posición CSSpredeterminada
II!PI 4. Efectos
,
Aprende jQuery 1.3 lIiD
para todos los elementos a nivel de bloque es static, que describe de forma precisa
embargo, esta vez, llevamos a cabo los tres efectos de forma secuencial, al situar cada
cómo permanecerán estos elementos si intentamos moverlos sin primero cambiar su
uno en su propio método. animate () y encadenar los tres juntos:
valor position.
$ (document) "teadY(function() { ,
$('div.label') .click(function() (
var paraWidth = $('div.speech p') .outerWidth();
var $switcher = $(this) .parent();
var switcherWidth = $switcher.outerWidth();
Para más información sobre posicionamiento absoluto o relativo, consulte el artículo $switcher
de Joe Gillespie,Absolutely Relative en: http://www.wpdfd.com/issues/78/ .animate({left: paraWidth - switcherWidth},
absolutely_relative/. "e Low " )
.animate({height: '+=20px'}, 'slow')
.anima te ({borderWidth: '5px'}, 'slow');
}.);
Una mirada rápida a nuestra hoja de estilo muestra que ahora hemos establecido j);
<di v id=" swi tcher" > con posición relativa:
#switcher { Recuerde que encadenar nos permite mantener los tres métodos. animate () en la
position: relative; misma línea, pero aquí los hemos sangrado y situado cada uno en su propia línea para
mayor legibilidad.
Con el CSS tenido en cuenta, el resultado de hacer clic en Text Size, cuando la ani-
. Podemos encolar cualquiera de los métodos de efecto jQuery, no sólo. animate (),
al encadenados. Podemos, por ejemplo, encolar efectos en <di v id=" swi tcher" > en
mación se ha completado, se parecerá a la figura 4.8. el siguiente orden:
( (~~
<,
~BJgg3 (-'Smaller¡)
..-/' 3. Pasar de nuevo a opacidad completa con. fadeTo () .
4. Ocultarlo con. slideup () .
Fourscore and seven years ago our fathers brought (orth on thls contlnent a new nañon, concelved In liberty, end dedlcated
to the proposltk>n that.1I men are creeted equa!. 5. Mostrarlo una vez más con. slideDown ().
read more
Todo lo que necesitamos hacer es encadenar los efectos en el mismo orden en nues-
Figura 4.8. Posicionarel cuadroa la derecha. tro código:
$ (document) .ready(function() (
$('div.label') .click(function() {
Efectos simultáneos frente a "en cola" var paraWidth = $('div.speech p') .outerWidth();
var $switcher = $(this) .parent();
var switcherWidth = $switcher.outerWidth();
El método. animate (), como acabamos de descubrir, es de mucha utilidad para $switcher
crear efectos simultáneos en un conjunto determinado de elementos. Sin embargo, exis- .fadeTo('fast',O.5)
ten momentos en los que deseamos encolar nuestros efectos, haciendo que ocurran uno .animate({
}) ;
Cuando se aplican múltiples efectos al mismo conjunto de elementos, encolados se
realiza al encadenar esos efectos. Para demostrado, moveremos de nuevo el cuadro Text
Size a la derecha, aumentaremos su altura y aumentaremos el ancho de su borde. Sin Pero, ¿qué sucedería si queremos mover el <di v » a la derecha al mismo tiempo que
se desvanece a media opacidad? Si las dos animaciones ocurrieran a la misma velocidad,
ll!!II 4. Efectos Aprende jQuery 1.3 lIiD
r
podríamos simplemente combinarlas en un solo método . animate () . Pero en este $ (document) .ready(function() {
$('div.label') .click(function() {
ejemplo, desvanecer utiliza la velocidad fast mientras que mover a la derecha uti-
I I
var paraWidth = $ ('div.speech p') .outerWidth() ;
liza la velocidad slow '. Aquí es donde la segunda forma del método. animate ()
I
var $switcher = $(this) .parent();
es de utilidad: var switcherWidth = $switcherl'"uterWidth () ;
$switcher
$ (document) .ready(function() { .fadeTo('fast',0.5)
$('div.label') .click(function() { .animate({
var paraWidth = $('div.speech p') .outerWidth() ; 'left': paraWidth - switcherWidth
var $switcher = $(this) .parent(); }, "e Low! )
var switcherWidth = $switcher.outerWidth(); .fadeTo('slow' ,1.0)
$switcher .slideUp (,s Low ')
.fadeTo('fast' ,0.5) .queue (function () {
.animate({ $switcher
'left': paraWidth - switcherwidth . esa ( 'backgroundColor 1, I #fOO I )
}) ; }) ;
}) ;
" Cuando se da una función de rellamada, como aquí, el método . queue () añade la
El segundo argumento, un mapa de opciones, proporciona la opción queue, que función a la cola de efectos para los elementos coincidentes. Dentro de la función, esta-
cuando se establece en fal se hace que la animación empiece simultáneamente con la blecemos el color de fondo en rojo y luego añadimos el método . dequeue ( ) . Incluir
anterior. este método . dequeue () permite que la cola de animación prosiga donde lo dejó y
Una última observación sobre encolar efectos en un solo conjunto de elementos es que complete la cadena con la siguiente línea . s 1i deDown ( S 1ow Si no hubiéramos I I ) •
encolar no se aplica automáticamente a otros métodos no de efectos como. css () . Por lo utilizado . dequeue ( ) , la animación se hubiera detenido.
tanto, supongamos que queremos cambiar el color de fondo de <di v id=" swi tcher" >
a rojo después de . slideUp () pero antes de slideDown (). Podríamos intentar ha-
cerlo así:
$ (document) .ready(function() { Másinformacióny ejemplospara. queue () y . dequeue () se encuentran disponibles
$('div.label') .click(function() { en http://docs.jquery . com/Effects.
var paraWidth = $('div.speech p') .outerWidth() ;
var $switcher = $(this) .parent();
var switcherWidth = $switcher.outerWidth();
Descubriremos otra forma de encolar los métodos de no efecto según examinamos
$switcher
los efectos con múltiples conjuntos de elementos.
.fadeTo('fast',0.5)
.animate({
'left': paraWidth - switcherWidth Trabajar con múltiples conjuntos de elementos
}, 'slow')
.fadeTo('slow',l.O)
A diferencia de con un solo conjunto de elementos, cuando aplicamos efectos a dife-
.slideUp('slow')
.css('backgroundColor','#fOO')
rentes conjuntos, estos ocurren casi al mismo tiempo. Para ver estos efectos simultáneos
.slideDown('slow') ; en acción, desplazaremos un párrafo hacia abajo mientras deslizamos otros hacia arri-
}) ; ba. En primer lugar, añadiremos la parte restante del Discurso de Gettysburg al HTML,
}) ; dividiéndolo en dos párrafos.
Sin embargo, aunque el código de cambio de fondo se sitúa en la posición correcta c::::div id=I1Switcherll:;:.
---------------- 1
lI!IiI 4. Efectos Aprende jQuery 1.3 lIiiI
cdiv class="speech"> .click(function() {
<p>Fourscore and seven years a90 our fathers brought forth $(this) .slideUp('slow')
on this continent a new nation, conceived in liberty, and .next () . slideDown (1 s Low "} ;
dedicated to the proposition that a11 men are created }) ¡
$( document) .ready(function() (
var $thirdPara = $ ('p:eq(2) '); Aquí de nuevo, el color de fondo de <di v id= swi tcher > cambia a rojo después
11 11
}) ; Con todas las variaciones a considerar cuando se aplican efectos, puede resultar di-
$ ('p: eq (3) ') .css ('backgroundColor', '#ccc'). hide () ; fícil recordar si los efectos ocurrirán simultáneamente o de forma secuencial. Un breve
}) ;
detalle podría ayudar:
Utilizar $thirdPara dentro de la rellamada . slideDown () se basa en las pro- 1. Efectos en un solo conjunto de elementos son:
piedades de cierres. Trataremos este importante tema, aunque difícil de dominar, en el
Apéndice C. • Simultáneos: Cuando se aplican como múltiples propiedades en un solo
Esta vez, una captura a mitad de camino durante los efectos revelará que tanto el método. animate () .
tercer como el cuarto párrafo son visibles, el cuarto ha terminado de deslizarse hacia • En cola: Cuando se aplican en una cadena de métodos, a menos que la opción
abajo y el tercero está a punto de empezar a desplazarse hacia arriba, como se ve en la queue se establezca en falseo
figura 4.10.
2. Efectos sobre múltiples conjuntos de elementos son:
e breve men, living Bnd dead, who struggled here have consecrated it, far above our poor power to add or detracl The
lrtd wlll IIttle note, nor long remcmber, what we say here, but It can never forget what they dld here. lt 15far us the
• Simultáneos: Por defecto.
'living, rather, to be dedlcated here to the unflnlshed wor1c whlch they who fought here have thU5 far so nobly advanced.
• En cola: Cuando se aplican en la rellamada de otro efecto o dentro de la
rellamada del método . queue ( ) .
/'
Como un mago que hace aparecer un ramo de flores de su chistera, jQuery puede
crear elementos, atributos y texto en una página Web, como por arte de magia. Pero,
5
espere, hay mucho más. Con jQuery, también podemos hacer desaparecer cualquiera
.
de estas cosas. Y,podemos tomar ese ramo de flores y transformarlo en una paloma con
<divclass="magic" id="flowerstodove">dove</div>.
'"
Manipul cron Manipular atributos
A lo largo de estos cuatro capítulos del libro, hemos estado utilizando los métodos
. addClass () y . removeClass () para demostrar cómo podemos cambiar la apariencia
de elementos en una página. Efectivamente, lo que estos dos métodos hacen es manipu-
Algunos atributos no se pueden manipular tan fácilmente sin la ayuda de jQuery. $ (document) .,éady(function() {
$ (!div.chapter a!) .attr({/rel!: lexternal/});
Además, jQuery nos permite modificar más de un atributo de cada vez, similar a la forma
}) ;
en que hemos trabajado con múltiples propiedades CSS utilizando el método . e s s () en
el capítulo anterior. Por ejemplo, podemos establecer fácilmente los atributos id, re 1, y Esta técnica funciona porque queremos que el nuevo atributo re1 tenga el mismo
tit1e para vínculos, todos a la vez. Empecemos con algo de HTML de ejemplo: valor para cada vínculo. Sin embargo, a menudo los atributos que añadimos o cambia-
<hl id=tlf-titlell>Flatland: A Romance of Many Dimensions</hl>
mos deben tener diferentes valores para cada elemento. Un ejemplo de esto es que para
<div id=tlf-authortl>by Edwin A. Abbott</div> cualquier documento dado, cada id debe ser único si queremos que nuestro código
<h2>Part 1, Section 3<!h2> JavaScript se comporte de forma predecible. Para establecer un id único para cada vín-
<h3 id=tlf-subtitlel!> culo; abandonamos la solución de una línea en favor del método. each () de jQuery.
Concerning the Inhabitants of Flatland
<!h3> $ (document) .ready(function() {
<div id=tlexcerpttl>an excerpt</div> $ (!div.chapter a!) .each(function(index)
$(this) .attr({
<div class="chapter"> !rel!: !external!,
lid!, !wikilink-! + index
<p class="squaretl>Our Professional Men and Gentlemen are }) ;
Squares (to which class 1 myself belong) and Five-Sided }) ;
Figures or <a }) ;
href=''http://en.wikipedia.org/wiki/Pentagonu>pentagons
<la>. El método. each (), que actúa como un iterador explícito, es en realidad una forma
<!p>
<p class=tlnobility hexagonl!>Next above these come the
más conveniente del bucle foro Se puede emplear cuando el código que queremos uti-
Nobility, of whom there are several degrees, beginning at lizar en cada elemento en el conjunto de elementos coincidentes del selector es dema-
Six-Sided Figures, or <a siado complejo para la sintaxis de iteración implícita. En nuestra situación, a la función
href=''http://en.wikipedia.org/wiki/Hexagontl>Hexagons</a>, anónima del método . each () se le pasa un índice que podemos anexar a cada id.
and from thence rising in the number of their sides till
they receive the honourable title of <a
°
Este argumento de índice actúa como un contador, empezando en para el primer vín-
href="http: !!en.wikipedia.org!wiki!Polygon" >Polygonal< la>, culo y aumentando en 1 con cada vínculo sucesivo. De esta forma, establecer el id en
or many-Sided. Finally when the number of the sides /wikilink- / + index proporciona al primer vínculo un id de wiki1ink- 0, al se-
becomes so numerous, and the sides themselves so small, gundo un id de wiki1ink-l, etc.
that the figure cannot be distinguished from a <a
href=''http://en.wikipedia.org/wiki/Circle''>circle</a>, he
is included in the Circular or Priestly arder¡ and this is
the highest class of all.
<!p> De hecho, podríamos haber optado por iteración implícita aquí, porque el método
<p><span class="pull-quote">It is a <span class="droptl>Law
. a t t r () puede tomar una función como su segundo argumento, similar a la forma en
of Nature<!span> with us that a male child shall have que el método . f i 1t er () puede hacerla con su único argumento como vimos en un
<strong>one more side</strong> than his father</span>, so capítulo anterior (véase http://docs.j query. com/Attributes/attr#keyfn
that each generation shall rise (as a rule) one step in
para detalles). Sin embargo, utilizar. each () parece más conveniente para nuestras
the scale of development and nobility. Thus the son of a
Square is a Pentagon¡ the son of a Pentagon, a Hexagon¡ necesidades.
and so on.
<!p> Utilizaremos el atributo ti t1e para invitar a personas a aprender más sobre el término
<!-- ... código continúa ..
vinculado en Wikipedia. En el ejemplo HTML, todos los vínculos apuntan a Wikipedia.
<!div> Sin embargo, es probablemente una buena idea hacer que la expresión de selector sea
un poco más específica, seleccionando solamente vínculos que contienen wikipedia
Ahora podemos pasar por cada uno de los vínculos dentro de <di v c.l ass« chapter
11 >
11
en el href, sólo en caso de que decidamos añadir un vínculo no Wikipedia al HTML en
Y aplicarles atributos uno a uno. Si solamente necesitáramos establecer un valor de un momento posterior:
II!I 5. Manipulación DOM Aprende jQuery 1.3 II'BI
$ (document) .ready(function() { $ (document) .ready(function() {
$ (!div.chapter a [href*=wikipediaJ!) .each(function(index) $(/<a href,,"#top">back to top<!a>!);
var $thisLink = $(this); $ (/<a id="top"><!a>!);
$thisLink.attr({ }) ;
!rel!: !external!,
r~-::J'
!id!, !wikilink-! + index,
En la figura 5.1 tiene cómo se muestra la página en este punto.
!title!, !learn more about ! + $thisLink.text() + ! at Wikipedia!
}) ;
}) ; Flatland: A Romance of Many Dimenslons
}) ; by Edwln A. Abbott
Par1 1, Section 3
Una cosa que merece la pena destacar aquí es que ahora estamos almacenando
Concernlng the Inhabltants of Flatland
$ (this) en una variable denominada $thisLink, simplemente porque acabamos an excerpt
utilizándola más de una vez. Con estos tres atributos establecidos, el HTML del primer Ou', Professlonal Men and Gentlemen are Squares (to whlch class I myself belong)
vínculo, por ejemplo, ahora se parece a esto: and Flve-Slded Figures o, Pentagons.
Next above these come the Noblllty, of whom there are several degrees, beglnnlng
ea href=''http://en.wikipedia.org/wiki/Pentagon" rel=flexternalr, at SixaSlded Figures, or ~ and from thence rlslng In the number ef thelr
id= wikilink-O
lI I1 title="learn more about Pentagons at sldes tili they recelve the honourable tltle of ~, or many-Slded. Finaily
Wikipediall>pentagons</a:> when the number of the sldes beoomes so numerous, and the sldes themselves so
small, that the figure cannot be dlstingulshed from a~, he Is ¡ncluded In the
Circular or Pnestly arder; and thls is the hlghest dass of all.
la función factory $0 revisada It Is a taw of Nature wlth us that a male chlld shall have one more slde than hls
father, so that each generatlon shall ríse (as a rule) ene step In the scale of
develapment and noblllty. Thus the son of a Square Is a Pentagon; the son of a
Pentagon, a Hexagon; and so on.
Desde el principio de este libro, hemos venido utilizando la función $ () para acceder
But this- ruJe applies not aiways to the Tradesman, and stlll less often to the
a elementos en un documento. En cierto sentido, esta función se encuentra en la base de Soldlers, and to the Werkmen; whe indeed can hardly be sald to deserve the name
la biblioteca jQuery, ya que se utiliza cada vez que anexamos un efecto, evento, o pro- of human Figures, slnce they have not all thelr sldes equel, Wlth them therefore
the Law of Nature does nat hold; and the son of an Isesceles (Le. a Trlangle with
piedad a un conjunto coincidente de elementos. two sfdes equal) remalns Isosceles stlll. Nevertheless, aU hope 15 not such out,
Aún más, la función $ () tiene todavía un truco más dentro de su paréntesis, una ca- even fi"om the lsosceles, that hls posterltv may ultlmately rise above hls degraded
condltion ....
racterística tan potente que puede cambiar no solamente la apariencia visual sino también
Rarely-In proportlon te the vast numbers of Isesceles blrths-Is a genulne and
los contenidos de una página. Con sólo insertar un fragmento de código HTML dentro certlflable Equal-Slded Trlangle produced fi"om Isosceles parents. "WhDl.ud ofa
de los paréntesis, podemos crear una estructura DOM enteramente nueva de la nada. certtjlCtlle?" a Space1a;d crtdc may ast.' "/$ 1101lhe prrJC1'f!allon of SqllQn Son G ~rt!ftctJ(efrom
Q
Narure hose/f. pnnoJng lile EquaJ...Jldedlll!Ss 01 Ih~ Fatlrer?" I reply ,hal no Lady olO1ly posmon wlll
marry an uncerlifo'd 1HiJngle.. Squan offtprl'W II(JJ sometlmllS resulted from a slightly 'TmplaT
"
)
Deberíamos recordar, una vez más, el peligro inherente de poner cierta funcionalidad, Pero, ¿dónde están los vínculos para volver arriba y el ancla? ¿No deberían aparecer
atractivo visual o información textual disponible solamente a aquellos con navegadores en la página? La respuesta es no. Aunque las dos líneas crean los elementos, todavía no
Web que pueden (o tienen habilitado) utilizar JavaScript. La información importante añaden los elementos a la página. Para hacer esto, podemos utilizar uno de los muchos
debería estar accesible a todos, no solamente a las personas que parece que están métodos de inserción jQuery.
utilizando el software correcto.
}) ; .prependTo(/body!); ~
) ;
El método. after () realizaría lo mismo que. insertAfter (), pero con la expre-
sión selector precediendo el método en lugar de a continuación. Utilizando. after (), Este código tradicional inserta el ancla al principio del «body»: en otras palabras, al
la primera línea dentro de $ (document) . ready () se parecería a esto: principio de la página. Ahora, con el método. insertAfter () para los vínculos y el
método . prependTo () para el ancla, tenemos un conjunto plenamente operativo de
$ (!div.chapter p!) .after(/<a href="#top">back to top<!a>!); vínculos back to top para la página.
Con vínculos así, no tiene mucho sentido hacer que aparezcan cuando el principio
Con. insertAfter (), podemos continuar actuando sobre el elemento <a> creado al
encadenar métodos adicionales. Con. after (), los métodos adicionales actuarán sobre de la página sigue siendo visible. Una mejora rápida al script empezaría los vínculos
los elementos coincidentes por el selector $ (/ di v . chapter p /) en su lugar. solamente después del cuatro párrafo, por ejemplo.
Por lo tanto, ahora que hemos insertado los vínculos en la página (y en el DOM) des- Esto se consigue rápidamente con un pequeño cambio a la expresión selector:
pués de cada párrafo que aparece dentro de <di v c La s s chapter" >, aparecerán los e "
. insertAfter (/ di v. chapter p: gt (2) /) . ¿Por qué e12 aquí? Recuerde que el in-
vínculos back to top, como se ve en la figura 5.2. dexado de JavaScript empieza en O; por lo tanto, el primer párrafo se indexa como O, el
segundo es 1, el tercero es 2, y el cuatro párrafo es 3. Nuestra expresión selector empieza
Flatland: A Romance of Many Dlmenslons insertando los vínculos después de cada párrafo cuando el índice alcanza 3, porque ése
by Edwln A. Abbott
es el primero mayor que 2.
Part 1, Section 3 El efecto de este cambio es ahora evidente, como se ve en la figura 5.3.
Concernlng the Inhabltants o, Flatland
lln excerpt
Our Professlonal Men and Gentlemen are Squares (to whlch dass 1 myself belong)
and Flve-Slded FIgures or Pent.agons. Flatland: A Romance of Many Dimensions
~ by Edwin A. Abbott
Next above these come the NobUity, af whom there are several degrees, begfnnlng Part 1, Section 3
at Slx·Slded Figures, or Hexagons and from then.ce r1slng In the number of their
sldes tlll they receíve the honourable tltle of Polygonal, or many-Slded. Flnally concerning the Inhabltants of Flatland
when the number o, the sldes becomes so numerous, and the sldes themselves so an excerpt
small, that!he figure cannot be dlstlngulshed from o clrde, he 15Included In the
Circular or Prfestiy arder; .nd thls Is the hlghest dass of all. Qur Professlonal Men and Gentlemen are Squares (to whlch class 1 myself belong)
and Five-Sided Figures or Pentagons.
back te toa
Next above these come the Nobilttv, of whom there are several degrees, beginnlng
It Is a Law of Nature wl!h us that a male chlld shall have one more Ilde than hls at Slx-Slded Figures, or ~ end from thence rislng In the number of thelr
father, 50 that each generatlon shall rlse (es a rule) one step in the scale of sldes till they receive the honourable tltle of ~, or many-Slded. Fln.lly
development and noblllty. Thus the son of a Square 15a Pentagon; the son of a when the number of the sldes becomes so numerous, and the sldes themselves so
Pentagon, a Hexagon; and so on. small, that the figure cannot be dlstlngulshed from • elrele, he Is included in the
b.ck te top Clreular or Prfestiy arder; and !hls Is tne hlghest dass al all.
But thls rule applles not always te the Tradesman, and stlll less often lo the lt is a l..aw of Neture with us that a male chlld shall have one more .Ide than his
Soldlers, and to the Workmen; who Indeed can hardly be sald te deserve the name father, so that each generation shall rise (as a rule) one step In the scale Q'f
of human Figures, slnce they have not all thelr sldes equaí, Wlth them therefore development and noblllty. Thus the son of a Square Is a Pentagon; the son of a
the tsw of Nature does not hold¡ and the son of en Isosceles (Le. a Trlangle wlth Pentagon, a Hexa.gon; and so on.
two sldes equal) remains Isosceles stllI. Nevertheless( al! hope 15not such out,
even lrom the lsosceles, !hat hís posterlty m.y ultlmately ríse abave hls degraded But this rule applles not .Iways to the l'rBdesman, and stlll tess often to tne
condítíon .... Soldle'rs, and to the Workmen; who Indeed can hardly be said te deserve the name
of human Figures, slnce !hey have not .11 thelr sldes equal. WI!h them therefore
~ !he Law of Noture does not hold; and the son al an tsoscetes (Le. a Trl.ngle wlth
two sldes equal) remalns Isosceles stlll. Nevertheless, ali hope 15not such out,
Rarely-In proportlon te !he vas! numbers 01 lsosceles blrths-Is a genulne and even from the lsosceles, !hat hls posterlty m.y ultlmately ríse above hls degraded
condltlon ....
Figura 5.2. Los vinculas para volver arriba después de cada párrafo.
~
Desafortunadamente, los vínculos no funcionan todavía. Todavía necesitamos inser- P.arely-In proportlon lo the vas! numbers o/ lsosceles blrths-is a genulne and
certlfiable Equ.I-Slded Trl.ngle produced from Isosceles parents. "WhOln«dofa
tar el ancla con id= 11 top". Para esto, podemos utilizar uno de los métodos que inserta
elementos dentro de otros elementos. Figura 5.3. Mejorar el script.
mi 5. Manipulación DOM
Aprende jQuery 1.3 lIlII
Mover elementos Cada uno de estos párrafos tiene un solo pie de página dentro de <span
class=" f oot.not;e "></span>. Al codificar elHTML de esta forma, podemos preservar
Con los vínculos back lo top, hemos creado nuevos elementos y los hemos insertado el contexto del pie de página. Con una regla CSS aplicada a la hoja de estilo para poner
en la página. También es posible tomar elementos de un lugar en la página e insertarlos en cursiva los pies de página, los tres párrafos se parecen a lo que aparece en la siguien-
en otro lugar. Una aplicación práctica de este tipo de inserción es el posicionamiento te figura.
dinámico y formato de notas a pie de página.
Un pie de página ya aparece en el texto Flatland original que estamos utilizando para Ra.rely-ln proportlon to the vast numbers of tsoscetes blrths-Is a genulne and
certlfiable Equal-Slded 1)-langle produced from lsosceles parents. "Whal need ojo
este ejemplo, pero también designaremos un par de otras partes del texto como pies de curtftcaJtJ?" Spau'land crltlc may asA: "'$ no' me prot:rmlJon ojo Squ012 Soll a certiflCluefrom
página para la finalidad de esta explicación: Q
NlJJrJJW heNfl/j províng me EqUIJ/aJidednesS 011M FOllter?" / rtply that 110 Lody o/any posttton wl/l
mar", Q1I uncenljluJ ntangle. St¡utm! offspTÜ1g ha.r sometimes ruultedfrom a sllghtly Irregular
<p>Rarely—in proportion to the vast numbers oE Isosceles TrlDllglt; but In almos! e\!O)' meA case the lrngularlty oltltejll'lt gvJUOri01I ts vtsued 011 tire third;
births—is a genuine and certifiable Equal-Sided whJclt e.lllter faJu 10 altatn the PellIogonaJ rani; 01' relapsl!J U) me Triangular. Such a blrth
requlres, as Its antecedents, not only a series o, carefully arranged Intermarriages,
Triangle produced from Isosceles parents. <span
ll
but also a long-contlnued exerclse of frugallty and self-control on the part of the
class=lIfootnote >IIWhat need of a certificate?!I a Spaceland would-be ancestors of the comlFlg Equll!lteral, and a patlent, systematlc, and
critic may ask: !lIs not the procreation of a Square Son a contlnuous development of the lsosceles Intellect through many generatlons.
certificate from Nature herself, proving the Equalsidedness
of the Father?" I reply that no Lady of any position will
~
marry an uncertified Triangle. Sguare offspring has The blrth af a Ttue Equllateral Trtangle from Isosceres parents 15the subject of
rejolclng In our country for many furlongs round. After a strict exemrnetton
sometimes resulted from a slightly Irregular Triangle;
conducted by the Sanltary and SOCial Board the Infant, If certlfled as Regular, is
l
but in almost every such case the Irregularity of the wlth solemn ceremonial admltted Into the class of Equllaterals. He Is then
first generation is visited on the third; which either Immedlately taken from hls proud yet sorrowlnq parents and adopted by some
fails to attain the Pentagonal rank, or relapses to the chlldless Equllateral. TIu: EqulÚlreral ts bowuJ by oaUt never ro pumil Me: child henceforth 10 enter
tus former hom~ or so mucJt as 10 /001: uptm Itls 1'f!la(Ú)1Uagaln./or fear test JJrBfreshly deveJ~
Triangular.</span> Such a birth requires, as its
o~ nJd)t, lryfOrCB ofunconrclow tmilation.fa/l bacJc agah'llnlo his hmxJtrary leve!
antecedents, not only a series of carefully arranged
intermarriages, but also a long-continued exercise of ~
frugality and self-control on the part of the would-be How admira ble Is the Law of Compensatlon 1And how pgrfecl a proof of ,he MlUrat ftmess
ancestors of the coming Equilateral, and a patient, ando 1 may olmost soy. rhe:dJvfne orlgin o/Ilre artnocrattc consrlRltion of the Sratl!f ofFlaltand! By a
systematic, and continuous development of the Isosceles judldous use of thls Law of Nature, the Pofygons and arcles are almost always
intellect through many generations. able to stiñe sedltlon In Its very cradle, taklng advantage of the lrreprasslbte and
</p> boundless hopefulness of the human mlnd ....
anatn ÜIe Pentagona/ ranA;, OT reiapser 10 rhe THonguJar.T1tJ! EquJlacuaJ ir bound by oath never to permu flte child hl!Jlcefortlt lO en/u hts former
home 07 $0 mua. tU 10 1001 upon hls relatlcms agaJ.n.for fear ¡#.SIrhefreshly dtveloped mganlsm may. by force o!tmeon.sdous imflOt/on,faJl back "
again 11IlO h1s ht.redltary leveJ.A.nd Jww perfect a proofofthe naturalftmen and.l may ahnosr soy, the divIne origtn o/Ihe ortnocranc conslftutiorl
oIrA.Sr.ru ofFIarú»rd/ Marcar, numerar y vincular el contexto
Figura 5.5. Pies de página sin separación.
Ya estamos listos para marcar y numerar el lugar desde el que sale el pie de página:
Una solución a este problema es modificar el CSS, haciendo que los elemen-
$(doc ument) .ready(function() {
tos c spari» se muestren como bloques, pero solamente si no están dentro de -cd i v $(/<01 id="notes"></ol>/) .insertAfter (/div.chapter/) ;
c l a s s e " chapter" >: $ (/span.footnote/) .each(function(index)
$(this)
span.footnote ( .before(
font-style, italic; [/<a href=lI~foot-note-/,
font-family: "Times New Reman", Times, serif¡ index+l,
display: block;
/" id="context-j,
margin: lem O; Lndex- j ,
) In class="context">/,
.chapter span.footnote /<sup>/ + (index+l) + /</sup>/,
display: inline;
/</a>/
l.join(j/)
Los pies de página están empezando ahora a tomar forma, como muestra la figura 5.6. }) ;
)l;
"mrat nn!d ola certJ.flcateP" a Spacelt11td cruíc mayo..rk: "b 1t00lhept'OCJ'Mlion o/a Squore Son a certtjicfIkfrom NlJIU1'8lime//, p10Vlng tlJe
Equal·,sJdednas ofth! Fatlter'" 1nrply tltat no Lady ofany posfdan will many cm uncsrtifted Triangle. Square offiprlng has somlfimu reJulted
fromas/lg}lrly 1""KU/(JJ' 1HlI1Igl.; bur In olm." every sucñ cos. rh, JrreguJarlry oflb.flrsrgenuaJúm ts vis/red •• lb. thJJT/;wMch drher f.11s ro AqUÍ empezamos con el mismo selector que hemos utilizado con el ejemplo más sen-
attaln th~ PentagOllaJ nm.t:, or ~ ta tñe 1Hl11IguJar.
cillo de pie de página, pero le encadenamos el método . each ( ) . Dentro de . each ( )
lle
fear
EquJlaJeroIls bound by 0iJl.JJnevu 10 pmnJ/ lJte chtJd lIeru:efMIt 10 enrer hlsformt:r homs
íest lb.¡;"'hlydeve/oped cvgan/sm lIUl)! by fo"," ofWlCO=_ Imlrorton.fall bockagaln
or so m,wch a! ID look. upo.n hu rekutOlU
Into A/s hendluuy I...,L
agalll, {01 empezamos con $ (thi s) , que representa cada pie de página en sucesión, y le encadena-
mos el método. before () . El resultado de la tabla unida dentro del paréntesis del mé-
And how peifllCr. proof ofrh. noJural /1m ess and. 1 may tzImMt soy. rh. divino or/gbl oflb. orl3rocratJc conninaúJn ojrk Sr.U$ ofFltJJland/
todo. before () es un vínculo superíndice, que se insertará antes de cada pie de página
Figura 5.6. Los pies de página toman forma. « span». El primero, por ejemplo, se parecerá a esto cuando se inserta en el DOM:
ea href="#foot-note-l" id="context-l"
Al menos ahora son pies de página distintos; aún así todavía se puede trabajar mucho
class=!lcontextn><SUP>l</sup></a>
con ellos. Una solución de pie de página más robusta sería:
La sintaxis puede parecer familiar a primera vista, por lo que dediquemos un momen-
1. Marcar la ubicación en el texto de dónde sale cada pie de página.
to a investigar lo que está sucediendo. Dentro de los paréntesis del método. before (),
2. Numerar cada ubicación, y proporcionar un número coincidente para el propio empezamos con un par de corchetes cuadrados, [ ], que representan una tabla literal.
pie de página. Todo elemento dentro de la tabla va seguido de una coma (excepto, muy importante, el
último elemento). Hemos situado cada elemento en su propia línea por legibilidad. Luego,
3. Crear un vínculo desde la ubicación de texto a su pie de página coincidente, y
una vez que se construye la tabla, lo convertimos a una cadena de nuevo al utilizar el
desde el pie de página de vuelta a la ubicación de texto.
método JavaScript . j oin () . Este método toma una cadena vacía como su argumento,
Estos pasos se pueden conseguir desde el método . each ( ) ; pero primero establece- representado por un par de comillas sencillas, porque no queremos que aparezca nada
remos un elemento contenedor para las notas en la parte inferior de la página: entre cada elemento de tabla cuando se muestra como HTML.
$ (document) .ready(function() {
Observe el uso de Lnde x- L. Puesto que el contador empieza en 0, añadimos 1 para
$(/<01 id="notes"></ol>/) .insertAfter(/div chapter/); empezarlos atributos href en #footnote-1, los atributos iden #context -1 yel texto
)) ; del vínculo en 1. El href es particularmente importante porque debe coincidir exacta-
mente con el atributo id del pie de página (no incluido el # por supuesto).
Parece suficientemente razonable utilizar una lista ordenada <01 id= "notes" ></ Para estar seguro, el mismo resultado se puede conseguir con una cadena concate-
01> para los pies de página; después de todo, queremos que estén numerados. ¿Por qué nada extensa en lugar de una tabla unida:
mi 5. Manipulación DOM
Aprende jQuery 1.3 mi
.before (/<a href="#foot-note_/ + (Lndex-i j ) +
/n id=ucontext_/ + (index+l) + $(this)
/n class=lIcontexclI><sup>/ + .befo!:"e(
(index+l) + /</sup></a>/); [/<a href="#foot-note-/.
i,ridex+l,
Aún en este caso, la técnica de tabla parece más manejable. /" id="context-/.
index+l,
/It class=lIcontextll>/,
/<sup>/ + (index+l) + /</sup>/.
/</a>/
Mucho se ha escrito en la Web sobre las diferencias de rendimiento entre tablas unidas J .join(//l
y cadenas concatenadas. Para aquellos que sean muy curiosos, el siguiente artículo )
trata una serie de pruebas utilizando las dos técnicas: http://www.sitepen.com/ .appendTo(/#notes/)
blog/200B/OS/09/string-performance_an_analYSis/. )l;
)l;
Sin embargo, en la mayoría de situaciones, estas diferencias son imperceptibles. Si el
rendimiento de un script es un problema, existen una serie de otras áreas que tienen Es importante recordar que . appendTo () se sigue encadenando a $ (t h i s) r por
mucho mayor impacto (como guardar selectores en caché, que ya hemos tratado). lo que jQuery dice, Anexar el pie de página al elemento con un ID de /notes/. Para cada uno
de los pies de página que hemos movido, anexamos otro vínculo, éste de vuelta al nú-
Nuestros tres marcadores de pie de página vinculados ahora se parecen a lo que mero en el texto:
aparece en la figura 5.7. •
$ (document) .ready(function() {
Rarely-In proportlon te the vest numbers of Isosceles blrths-Is a genuine and $(/<01 id="notes"></ol>/) .insertAfter(/div.chapter/);
certlfiable Equal-Slded 'lrlangle produced I'rom lsosceles parents. ~ Such a blrth $ (/span.footnote/) .each(function(index) {
requlres, as Its antecedents, not only a series o., ClIrefully arrnnged ~rmarr1ageSI $ (this)
but afso a long-contlnued exerclse of frugallty and self..control on the of the .before(
would-be ancestors of the COmlng Equllateral, and a patlen~ systematlc,' d
[/<a href="#foot-note-/.
contlnuous development of the lsosceles ínteñect through many generatloi
index+l,
~ /" id="context-/.
The blrth cf a True Equllateral TtIangle from I",sceles parents Is the subject of index+l,
rejolcln9 In our country for many furfongs round. Arter a strfct examlnatlo /11 class="context">/,
conducted by Ihe Sanltary end Social Board. the Infant. If gu ar, I /<sup>/ + (index+l) + /</sup>/.
wlth solernn ceremonial admltted loto the d eraIs. He 15 the
Immedlately taken I'rom hls ng parents and ad~
/</a>/
chlldless Equll.teral. : .join (//)
Tres marcadores )
~ de pie de página .appendTo(/#notes!)
.append ( / «a href="#context-/ + (index+l) +
How admIrable 15 the Law of Compensatlonl ~ By a JUdlclous use of thls law of
Nature. the Polygons and Clreles are almost always able to stIfle sedltlon In Its /">contextc/a»/ );
very cradle, taklng advantage of the irrepres51ble and boundless hopefulness of }) ;
the human mlnd ....
}l;
Figura 5.7. Marcadores de pie de página vinculados. Observe que el href apunta al id del marcador correspondiente. En la figura 5.8
puede ver los pies de página de nuevo con un vínculo anexado a cada uno.
Anexar pies de página
"WhaJ need of a cenJflcate'/" a SpactlandcrltJc maytl$k: "13not tñe procreanon OJO - o.>qlM.U .• (''''''_ _n rl!rtlflcat1?jrom
•.•-._-- •••••••.• _ ~
NallIn Aenel[. provmg tA. Equal-ndednen o/tIoe Fatlo •••,. 1 ,..ply thar no lAdy 01 a.y posUton wIll"""'l' an uncorifled
El siguiente paso es mover los elementos e span c Las s e « footnote" », como hicimos Tnangle. Square offiprlng hiB $ometJmts raultedjrom tJ sllghlly Irregular 1Hanglf!!: bu( In olmosllNf:l'Y such CQ.Jf!! 1M
IrreguÚJrlty ofth~ firsl general10n l.r vtslled 011tIu: thtrd; whlcn eitlrer lall~(o anatn u.e Penlog01lal ral'l~ or reJapses (O
con el ejemplo más sencillo. Sin embargo, esta vez, los dejamos en los recién creados <01 th. Triallgul"" (r!1I!!mJ ••••• _
id: "notes" >. Utilizaremos. appendTo () aquí, de nuevo para mantener la ordenación Th.e EqullatertJt ts botmd by oalll nn>er fO pmnlf 1M eA/id hencelonh 10 en(eF~"l.rlomtulíoñkÚ]8 MCM-BiD
adecuada, ya que cada pie de página sucesivo se insertará al final del elemento: ~ hb ",10/10113 agabl,lor fear les/ tM fraAIy deve/oped organ/sm '"".Y. M""
o(un co w;'o/f!
(¡U=rf8I'
vínculos
agamlnlO h/s huedItary Ieve/. (~4 V",
! • .:_ anexados
$ (document) .ready(function() { And how pofect a proof olthe noJur)Újlm~
tIo. Sta/es 01 FúztlandJ (WJm) •••• ;.
ss1 . r7 . ¡S;. Iki Yv" orlgt. 01 tIo. arlstOCNlt1c constuutum al
$(/<01 id="notes"></ol>/) ,insertAfter (/div. chapter/) ;
$ (/span.footnote/) .each(function(index) (
Figura 5.8. Pie de página con vínculo para volver al texto.
181 5. Manipulación DOM '1
L,í
our Professlonal Men and Gentlemen are Squares (to whlch ctass 1 myself belong)
and Five-Slded Figures or Pentagons.
1. "W'1JIn.need al centftcare'!" a Spac.eltmd crma may llfl: "151JOI the procreado71 of a Square Son cutiftcatefrr>m
Q Q
Nmure Iferselj; provmg ,he Equalwsfdedness o/tire FtnlJerJ" f rep1y rhar no Lody 01 any postuon wJll marry an uncertified
Next abcve these come the Noblllty, of whom there are several degrees, beglnnlng
ttiangle. Square offsprlJrg has!fCmet!mes resultedfrom a sJightJy Irregular 1Tiang/e; buzin almos! every sud case tñe
at SIx-Slded Figures, or Hexagons. and from thenca rlslng In the number of thelr
IrreguJarity ofdie finl generalion iI vJsUed on tire thtrrJ; wh.¡ch etthu fails to attatn me Penragonal ran~ 01'reJapsu fa
lb. Tri<mgrtlar. Cco_,) sldes tlll they receJve the honourable tltle of Polygonal, or many-Slded. Finally
when the number of the sldes becomes so numerous, and the sides themselves so
2. ne Equila/.eral ts bound by oatlt ne¡.oey IDpermiJ me child hencefanll to emer h.isformer home 01'so much ar lb loot small, that the figure cannot be dl5tlngulshed from a elrele, he 15Induded In the
upo1J his relations again, lor 'fear lest tbe ~hly developed organism may.1Jyforce ofunconsciow imiltJtiOll,jall bad Circular or Pnestly arder; and thls 15the hlghe5t dass of all.
again tmo bis fwoeditoJy ¡<veI. (cenee.u)
It Is a Law of Nature wlth U5 that a male chíld 5han have one more slde than his
3. AM bow perfea a p1"Of)f ofthe 1Jt11JUal~ 1111d.
lmay almost say, (he dtvJne orlgin O/Me artuocraitc constuuüan o/ father, so that eech generatJon shaü rtse (as a rule) one step In the sea te of
lbe Stases al FlaJlandl (con,ml
development and 'nobllltv. Thus the son of a SQuare is a Pentaoon; the son of a
Figura 5.9. Pies de páginanumeradosy vinculados. Figura 5.10. Preparado para crear una copiadel primerpárrafo.
Por supuesto, los números se podrían haber insertado antes de cada pie de página Para continuar el ejemplo, podemos hacer que el párrafo cIonado aparezca antes de
de igual forma que en los párrafos, pero existe algo profundamente satisfactorio acerca <di v class=" chapter" »:
de tener código semántico generado de forma dinámica por JavaScript. $ (/div.chapter p,eq(O)/) .clone() .insertBefore(/div.chapter/);
l:ImI 5. Manipulación DOM Aprende jQuery 1.3 lEiI
Ahora el primer párrafo aparece dos veces, como se ve en la figura 5.11, y puesto que Observe que el párrafo empieza con « span c La s s« "pull-quote" >. Ésta es la.clase
la primera instancia ya no está dentro de <di v c La s s chapter" », no retiene los es-
e "
a la que nos dirigiremos para clonar. Una vez el texto copiado dentro de ese c span» se
tilos asociados con el di v (más notablemente, la anchura). pega en otro lugar, necesitamos modificar sus propiedades de estilo para separarlo del
resto del texto. e'.
Concernlng the Inhabltants of Flatland
sn excerpt
Our Professlonal Men and Gentlemen are Squares (to whlch class 1 myself belong) and Five-Slded Figures or Pentagons. Una desviación CSS
Our ProfessJonal Men and Gentlemen are Squares (to whlch dass 1 myself belong)
and Flve-Slded Figures or Pentaaons. Para conseguir este tipo de estilo, añadiremos una clase pulled al « ap an » copiado
Next above these come the Nobillty, of whom there are several degrees, beglnnlng / y asignaremos a la clase la siguiente regla de estilo en la hoja de estilo:
at Stx-Síded Figures. or Hexagons and from thence rfslng In the number of thelr
sldes tlll they reeetve the honourable tltle of Polygonal, or many-Slded. Flnally .pul led {
when the number of the sldes becomes so numerous, and the sldes themselves so
background, #e5e5e5;
small, that the ftgure eannot be dlstingulshed from a clrde, he 15induded In the
Circular or Prtestly order; and thls 15the hlghest dess of all. position: absolute¡
width, 145px;
It Is a L2w o, Nature with us that a maie chltd shatl haya one more slde than his
t op : -20px;
father, so that each generatlon shaU nse (as a rute) one step In the scale of
development and noblllty. ThU!~the son at a Square 15a Pentagon; the son of a right, -180px;
padding, 12px 5px 12px 10px;
Figura 5.11. El párrafo se muestra dos veces. ,~ font: italic 1.4em "Times New Roman", Times, serif¡
Por lo tanto, utilizando una analogía con la que la mayoría de la gente debería estar
familiarizada, . clone () es a los métodos de inserción como copiar es a pegar. La cita ahora recibe un fondo gris claro, algo de relleno, y una fuente diferente.
Aún más importante, se posiciona de forma absoluta, 20 píxeles por encima y 20 píxe-
les a la derecha del ancestro posicionado (absolute o relative) más cercano en
Clonar con eventos el DOM. Si ningún ancestro tiene posicionamiento (aparte de static) aplicado, la
cita se posicionará relativa al documento <body>. Debido a esto, necesitamos asegu-
El método. clone () por defecto no copia ningún evento que está vinculado al ramos en el código jQuery que el elemento padre de la cita clonada tiene establecida
elemento coincidente o cualquiera de sus descendientes. Sin embargo, puede tomar position:relative.
un solo parámetro booleano que, cuando se establece en true, clona eventos también: Aunque el posicionamiento superior es bastante intuitivo, puede no quedar claro al
. el one (t rue) . Este adecuado clonado de evento nos permite evitar tener que tratar con principio cómo se posicionará la cita 20 píxeles a la izquierda de su padre posicionado .
volver a vincular eventos manualmente, como se ha tratado en un capítulo anterior. Derivamos el número primero del ancho total del cuadro de cita, que es el valor de la
propiedad width más el relleno izquierdo y derecho, ó 145 px + 5 px + 10 px, o 160 px.
Luego establecemos la propiedad right de la cita. Un valor de O alineará el lado de-
Clonar citas recho de la cita con el de su padre. Por lo tanto, para situar su lado izquierdo 20 px a la
derecha del padre, necesitamos moverlo en una dirección negativa 20 píxeles más que
Muchos sitios Web, como sus equivalentes impresos, utilizan citas para enfatizar pe- su ancho total, o -180 px.
queñas partes de texto y atraer la atención del lector. Podemos conseguir esto fácilmente
con el método. clone () . En primer lugar, echemos otro vistazo al tercer párrafo de
nuestro texto de ejemplo:
De vuelta al código
<p> Ahora podemos llegar a jQuery. Empecemos con una expresión selector para todos
<span class=!lpull-quote >It is a Law of Nature
1l
los elementos c s pari c La s s e "pull-quote" », y anexemos un método. each () de
<span class="drop!l>with us</span> that a male child shall
have <strong>one more side</strong> than his father</span>,
modo que podemos llevar a cabo múltiples acciones según pasamos por ellos:
so that each generation shall rise (as a rule) one step in
$ (document) .ready(function() (
the scale of development and nobility. Thus the son of a
$ (/span.pull-quote/) .each(function(index)
Square is a Pentagon¡ the son of a Pentagon, a Hexagon¡ and
// ...
so on.
}) ;
</p>
}) ;
''-.....,
) ;
var $clonedCopy = $(this) .clone();
$clonedCopy
) ;
.addClass(/pulled/)
Una vez más, almacenamos cualquier selector que utilizaremos más de una vez en .find(/span.drop/)
,.. .html{/…/)
una variable para mejorar rendimiento y legibilidad. .end()
Ahora podemos estar seguros de que el CSS está establecido y listo para la cita. En .prependTo($parentParagraph);
este punto podemos clonar cada e span», añadir la clase pulled a la copia, e insertarla var clonedText = $clonedCopy.text();
$clonedCopy.html(clonedText);
al principio del párrafo: ) ;
) ;
$ (document) .ready(function() (
$ (/span.pull-quote/) .each(function(index) (
var $parentParagraph = $(this) .parent(/p/); Por lo tanto, empezamos el proceso de donado esta vez al almacenar el don en una
$parentParagraph.css(/position/, /relative/); variable. La variable es necesaria esta vez porque no podemos trabajar en ella completa-
$(this) .clone() mente dentro de la misma cadena. Observe, también, que después de encontrar <span
.addClass(/pulled/)
.prependTo($parentParagraph);
c Laas vdr op:' >y reemplazar su HTML con unaelipsis (…), utilizamos. end ()
e
) ;
para regresar de la última consulta, . f ind (/ span . drop / ) . De esta forma, estamos in-
}) ; sertando toda la copia, no solamente la elipsis, al principio del párrafo.
Al final, establecemos una variable más, clonedText, a los contenidos de sólo texto
Puesto que estamos utilizando posicionamiento absoluto para la cita, la ubicación
de la copia; luego utilizamos estos contenidos de sólo texto como un sustituto para el
dentro del párrafo es irrelevante. Siempre y cuando permanezca dentro del párrafo, se
HTML de la copia. Ahora, la cita se parece a la figura 5.13. Evidentemente, se ha añadi-
posicionará en relación con la parte superior y derecha del párrafo, basándose en nues-
do otro -cs pa n c Las s» "pull-quote" > al párrafo posterior para asegurarse de que el
tras reglas CSS. Si, sin embargo, quisiéramos aplicar un float a la cita en su lugar, su
código funciona para múltiples elementos.
ubicación dentro del párrafo afectaría a su posición vertical. El párrafo, junto con su cita,
ahora se parece a la figura 5.12.
Circular ar Prtestiy areler; and tIlls Is the hfghest dass of all.
Embellecer las citas
It Is a Law of Naturo wlth us that a male chlld shall have one more ,"Ide than hls
father, so that each generatlon shan rlse (as a rule) one step In the sea le of Las citas ahora funcionan según lo esperado, con elementos hijo quitados y elipsis
development and noblllty. Thus the son of a Square 15 a Pontagan; rhe son of a
añadidas donde debería dejar de aparecer texto. Puesto que uno de los objetivos es añadir
Pentagon, a Hexagon; and so on.
atractivo visual, sin embargo, haríamos bien al asignar a las citas esquinas redondeadas
But thls rule applles not always to the Tradesman, and stlll less aften to the
Soldlers, and to the Workmen; who Indeed can hardly be sald to deserve the name con sombra. Sin embargo, la altura variable de los cuadros de citas es problemática por-
of human figures, slnce they have not all thelr sldes equat. Wlth them therefore que necesitaremos aplicar dos imágenes de fondo a un solo elemento, lo que es imposible
tIle Law of N.turo does not hold; and the son af an !soscsles (I.e. a Triangle wlth
para cada navegador en el momento excepto las versiones más recientes de Safari.
Figura 5.12. Posicionarla cita en el párrafo. Para superar esta limitación, podemos situar otro <di v » alrededor de las citas:
Éste es un buen comienzo, pero las citas típicamente no guardan formato de fuente $ (document) .ready(function() {
como hace ésta con el texto en negrita one more side. Lo que queremos es el texto de $ (/span.pull-quote/) .each(function(index) (
var $parentParagraph = $(this) .parent(/p/);
« span c l as s« "pull-quote" », quitado de cualquier e s t r onq>, <em>, <a hr ef » u
$parentParagraph.css(/position/, /relative/);
otra etiqueta en línea. Además, sería bueno poder modificar la cita un poco, eliminado var $clonedCopy = $(this) .clone();
algunas palabras y reemplazándolas con elipsis. Para esto, hemos situado algunas pa- $clonedCopy
labras de texto en el ejemplo en una etiqueta <span>: <span c l as s« "drop" swi t h .addClass(/pulled/)
.find(/span.drop/)
us c z apan >.
11m 5. Manipulación DOM
Aprende jQuery 1.3 IBII
.html(/…/)
.end () Aquí, algunas de las reglas anteriormente aplicadas a e apan c La s s "pu Ll ed" > se e
.prependTo($parentparagraph)
aplican a <di v class= "pulled-wrapper" > en su lugar. Un par de ajustes de ancho
.wrap{/<div clasS="pulled-wrapperll></div>/);
var clonedText = $clonedCopy.text(); y relleno toman en cuenta el diseño de los bordes de la imagen de fondo, y la regla. pu-
$clonedCopy.html(clonedText) ; lled tiene sus propiedades pos t oá y display modificadas para que aparezcan co-
í í
}) ;
rrectamente para todos los navegadores.
}) ;
En la figura 5.14 tiene un aspecto final de las citas recién adornadas en su hábitat.
rcular or
ess or a
lt 15a Law of Nature wlth us that a male chlld 5hall have one more slde than hls lt is a Law of Nature
tather, so that each generatlon shall rtse (as a rule) one step In the scale of
It ts a L..awo, Nature wlth us that a male chlld shall heve one more slde than his ~, Ik,
·._:<tñata male chi/d father, so that each generatlon shall rfse (as a rule) one step In the seale of 11 is aEa'w ofNq{ure
development and noblllty. Thus the SOn of a Square Is a Pentagon; the son of a
development and noblllty, Thus the son of a Square 15 a Pentagon; the son of a
Pentagon, a Hexagon; and so on. shál/ have (in" mere ... thot li'4nak child
Penrngon, a Hexagon; and so on.
sitie than hiSfather shall halk one more
But this rule applles not always to the Tradesman, and stlll less aften to the
But thls rule applles not always to the Tradesman, and stlflless often to the sitie thanhis father
Soldlers, and te the Workmen; who Indeed can hardly be sald to deserve the name
of human Figures, slnce they nave not all thelr sldes equal. Wlth them therefore
Soldlers, 8'1d to the workmen; who Indeed can hardly be sald to deserve the name .
of human Figures, slnee they have nat all thelr sldes equal. Wlth them therefore
the ~w of Nature does not hold; and the son of an lsosceles (Le. a Tnangle wlth the Law of Nature does not hold; and the son ot an Isosceles (I.e. a Trlangle wlth
Me sldes equal) remalns IsosceJes stffl. Nevertheless, 811hope 15not such out, two sldes equal) remalns Isoseeles stIl1. Nevertheless, al! hope Is not such out,
even from the Isosceles, that hls posterlty may ultlmately ríse above hls degraded
condltlon ..., even from the tsoscetes, that hls posterlty may ultlmately rtse ebove hls degraded
condltíon. ..
~ ~
~rely-In proportlon to the vast numbers of lsosceles blrths-Is a genulne and Rarely-In proportion to the vast numbers of lsosceles blrths-is a genulne and
certlRable Equal-Slded Trlangle produced from lsosceles parents. :. Such a blrth
certiflable Equal-Slded Trlangle produced from [sosceles parents. :. Such a blrth
requlres, as Its antecedents. not only a series ot carefully arT"i5lngedIntermamages, requlres, as Its antecedents, not only a series of carefulty 8rT"i51ngedIntermarrtages,
but elso a Iong-conttnued exerose of frug.lity .nd self-control on the part of the but also a kmg~continued exerdse of frugallty and se'f..control on the part of the
would·be ancest:ors ot the comlng Equl1ateral~ and a patíent, systematlc, and
wouíd-be ancestors of the eomlng EquUateral, and a panent, systematlc, and
contlnuous development of the Isosceles Intelled through many generatlons.
contlnuous development of the lsosceles Intellect through many generations.
~ bad< to top
The bfrth of a 'nue E.qullateral Tnangle from Isosceíes parents Is the subject of
rejo1dng In our country for many furtongs round. After a str1ct examlnatlon
The blrth of • 1hJe Equllateral Tnangle from lsosceles perents Is the subJect of :. '_ '".> ,. /,'
reJolclng in our cauntry for many fur10ngs round. After a stríct examinatlon ; 11re bidh.of a);UI!"
conducted by the S.nltary and Social Board, the Infant, II cert,"ed as Regular, Is
wft:h solemn ceremonJaI admltted Into the cíass of EqulJaterals. He 15then
conducted by the Sanltary and Social Board, the Infant, II certlfied as Regular, Is
wlth solemn ceremonial admltted loto the class of Equliate~is. He 15then
·l:quiTárd'a/:iHá,.gle
Immedlately taken from hls proud yet sorrowlng parents and adopted by some
frnmedlately taken from his proud yet sorrowlng parents and adopted by some ftyrn Il!fi§Cér~
chlldless Equll.teral. : párentsJ,(lhe
chlldless Equlleterat. : ' ~
~ ~
subject olrejoicing I
inOlP"oo~"; ... ~,
Figura 5.13. Contenidos de sólo texto en la cita. How admirable Is the Law of Comoensatlonl 3 By a ludldous use 01 thls Law 01 "
Figura 5.14. Citas mejoradas.
También necesitamos modificar el CSS,por supuesto, para tener en,cuenta el nuevo
<di v » y las dos imágenes de fondo:
.pulled-wrapper
background, url(pq-top.jpg)
(
no-repeat left top;
Métodos de manipulación DOM
position: absolute¡
width, 160px;
right, -lBOpx;
Los amplios métodos de manipulación DOM que proporciona jQuery varían de
padding-top, lBpx; acuerdo a su tarea y su ubicación. El siguiente esquema puede servir como un recorda-
} torio de qué métodos se pueden utilizar para llevar a cabo cualquiera de estas tareas,
.pulled (
casi en cualquier lugar.
background, url(pq-bottom.jpg) no-repeat left bottom;
position: relative¡
1. Para crear nuevos elementos desde HTML, utilizar la función factory $ ( ) .
display, block;
width, 140px; 2. Para insertar nuevos elementos dentro de cada elemento coincidente, utilizar:
padding, O lOpx 24px lOpx;
font: italic 1.4em "Times New Roman", Times, serif¡ • . append ()
• . appendTo ( )
lB 5. Manipulación DOM
• .prepend ()
• .prependTo ()
3. Para insertar nuevos elementos adyacentes a cada elemento coincidente, uti-
"':,
lizar:
• . after ()
• .insertAfter()
• . before ()
• .insertBefore()
4. Para insertar nuevos elementos alrededor de cada elemento coincidente, uti-
lizar:
• . wrap ()
• . wrapAll ()
• . wraplnner ()
5. Para reemplazar cada elemento coincidente con nuevos elementos o texto, uti-
lizar:
• . html ()
• . text ()
• .replaceAll()
• .replaceWith()
6. Para eliminar elementos dentro de cada elemento coincidente, utilizar:
• . empty ()
7. Para eliminar cada elemento coincidente y descendientes del documento sin
eliminarlos en realidad, utilizar:
• . remove ()
Resumen
En este capítulo hemos creado, copiado, reorganizado y embellecido contenido uti-
lizando los métodos de modificación DOM de jQuery. Hemos aplicado estos métodos a
una sola página Web, transformando un conjunto de párrafos genéricos en un extracto
con pie de página, citas, vinculado y con estilo.
El apartado de tutorial del libro está casi acabado, pero antes de pasar a examinar ejem-
plos más complejos, realicemos un viaje al servidor vía los métodos AJAX de jQuery.
,,
'.
.'
En los últimos años, ha resultado común juzgar sitios basándose en su uso en tecno-
logías específicas. Una de las palabras de moda más destacadas utilizadas para descri-
6
bir nuevas aplicaciones Web es "realizado con AJAX". Esta etiqueta se ha utilizado para
significar muchas cosas diferentes, ya que el término engloba un grupo de posibilidades
de uso y técnicas relacionadas.
Técnicamente, AJAX es un acrónimo para Asynchronous JavaScript and XML (Iavaficript
asíncrono y XML). Las tecnologías implicadas en una solución AJAX incluyen:
AIAX •
•
JavaScript, para capturar interacciones con el usuario u otros eventos relacionados
con el navegador.
El objeto XMLHttpReque s t , que permite que las peticiones se realicen al servidor
sin interrumpir otras tareas de navegador.
• Archivos XML en el servidor, o a menudo otros formatos de datos similares como
HTMLoJSON.
• Más JavaScript, para interpretar los datos desde el servidor y presentarlo en la
página.
La tecnología AJAX ha sido aclamada como el salvador del escenario Web, transfor-
mando páginas Web estáticas en aplicaciones Web interactivas. Han surgido muchos
marcos de trabajo para ayudar a los desarrolladores a domado, debido a las inconsisten-
cias en las implementaciones de los navegadores del objeto XMLHttpRequest; jQuery
no es ninguna excepción.
Perrnítanos ver si AJAX puede realmente realizar milagros.
lmI 6.AJAX
,
l· Aprende jQuery 1.3 lIiI
A <div class="definition">
Independent, irresponsible. An absolute rnonarchy is one
ª
c.
in which the sovereign does as he pleases so long as he
pleases the assassins. Not rnany abso1ute monarchies are
left, most of them having been replaced by limited
º monarchies, where the sovereign's power for evil (and for
good) is greatly curtai1ed, and by repub1ics, which are
Figura 6.1. Añadir reglas CSS a la página.
governed by chanceo
</div>
Ahora nos podemos centrar en tener contenido en la página. </div>
IEI 6.AJAX
, Aprende jQuery 1.3 IBI
f
ABDICATION ",
A ABDICATION n.
An act whereby a soverelgn attests hls sensa of the hlgh temperatura or the throne.
n.
~ Poor lsabefla's Dead, whose abdlcatlon
An act whereby a sovereign attests bis sense of the high temperature of!he throne. Set all longue. wagglng In lile Spenlsh nanco.
Poor IsabelIa's Dead, whose abdication ,(!
For Ihat performance 'twere unfalr to scold her:
Set aIl tongues wagging in the Spanish nation. She wlsely left a throne too hot 10 hold ner,
For that performance 'twere unfair to scold her:
She wisely lett a throne 100 hot to hold her.
º To Hlstory sha'lI be no royal rlddle -
Merely a plaln parched pea !hat lumpad lile grlddle.
G.J.
lb History she'll be DO roya! riddle _
Merely a plain parched pea that jumped tbe griddle.
GJ. ABSOLUTE .dj.
Indepen~ent. Irresponslble. An absoluta monarchy is one In whtch the soverelgn does as he píeeses
ABSOLUTE so long as he plsases the assasslns. Not many absoluta monarchles are leñ. mast of them havtng
been replaced by IImlted monarchles. where lile soverelgn's power Ior evll (and for 9000) is greatly
curtaJled, and bY republlcs, Whlch are govemed by chanceo
adj.
lndependent, irresponsible. An absolute monarchy is one in which the sovereign does as he pleases so long as be
ACKNOWLEDGE v.l
pleases the assassins. Not many absolute monarchies are left, most of them baving been replaced by l.Gnited
To confess. Ac:knowledgement of one another's faufts 15!he htghest duty Imposed by our leve ot truth.
monarchíes, where!he sovereign's power for evil (and for good) is greatly curtailed, and by republics, which are
governed by chanceo
AFFIANCED pp.
ACKNOWLEDGE Fltted wlth an ankle-ring for !he bell-and-chaln.
espera a que se recuperen los datos. Si las acciones se deben retrasar antes que se haya l1definitionll: IIA convenient deity invented by the ... ,
completado la carga, jQuery proporciona una rellamada para esto. A continuación se "guote": [
proporciona un ejemplo. "Ls public worship, then, a a i.n i :',
En este caso, necesitamos recuperar los datos en una estructura que podemos recorrer "part": I1V.t. U
con JavaScript. "definition": "To speak of a man as you find him when ..
),
Con los selecto res de jQuery, podríamos recorrer el HTML que recibimos y manipu-
(
larlo, pero primero se debe insertar en el documento. Un formato de datos JavaScript "term": "BEARD1',
más nativo puede significar incluso menos código. "pa r t :': IIn.lI,
Como a menudo hemos visto, los objetos JavaScript son simplemente conjuntos de Para recuperar los datos, utilizaremos el método $ . getJSON ( ) r que busca el archivo
pares clave-valor, y se pueden definir sucintamente utilizando llaves (O). Las tablas y lo procesa, proporcionando el código llamante con el objeto JavaScript resultante.
JavaScript, por otro lado, se definen en el momento con corchetes ([l). Al combinar estos
dos conceptos, podemos fácilmente expresar algunas estructuras de datos muy comple-
jas y ricas. Funciones jQuery globales
El término JavaScript Object Notation (JSON) se acuñó por Douglas Crockford para En este punto, todos los métodos jQuery que se han utilizado se han anexado a un
sacar provecho de esta sencilla sintaxis. Esta notación puede ofrecer una alternativa objeto jQuery que hemos creado con la función factory $ ( ) . Los selectores nos han per-
concisa al formato XML:
mitido especificar un conjunto de nadas DOM con los que trabajar, y los métodos han
operado sobre ellos de alguna forma. Esta función $ . getJSON ( ) , sin embargo, es di-
"key": "va Lue :',
ferente. No existe elemento DOM lógico al cual se pueda aplicar; el objeto resultante se
"key 2":
"array" ,
tiene que proporcionar al script, no incluido en la página. Por esta razón, getJSON () se
"of" , define como un método del objeto global jQuery (un solo objeto denominado j Query
"items" o $ definido una vez por la biblioteca jQuery), en lugar de una instancia individual de
objeto jQuery (los objetos que creamos con la función $ ( ) ).
Si JavaScript tuviera clases como otros lenguajes orientados a objetos, llamaríamos a
$. getJSON () un método de clase. Para nuestros fines, haremos referencia a este tipo
de método como una función global; de hecho, son funciones que utilizan el espacio de
nombre jQuery para no entrar en conflicto con otros nombres de función.
Para información sobre algunas de las ventajas potenciales de JSON, así como imple-
Para utilizar esta función, le pasamos el nombre de archivo como antes:
mentaciones en muchos lenguajes de programación, visite http://j son. org/.
$ (document] .ready(function() (
$('#letter-b a') .click(function()
Podemos codificar nuestros datos utilizando este formato de muchas formas. $ . getJSON ('b.j son' ) ;
Situaremos algunas entradas de diccionario en un archivo JSON que denominaremos return false¡
b . j son, que empieza de la siguiente forma: }) ;
}) ;
Este código no tiene efecto aparente cuando hacemos clic en el vinculo. La llamada de
IItermll: I1BACCHUSll,
"par t v. IIn.
función carga el archivo, pero no le hemos dicho a JavaScript lo que hacer con los datos
resultantes. Para esto, necesitamos utilizar una función de rellamada.
IIEI 6.AJAX Aprende jQuery 1.3 DBI
tro y una función de rellamada como su segundo. Cada vez que se pasa por el bucle, }} ;
el índice actual de iteración y el elemento actual en la tabla o mapa se pasan como dos if (entry [,author' J) {
htrnl += '<div class="quote-author">, + entry['author 1
] +
parámetros a la función de rellamada.
'</div>' ;
$ (document) .ready(function(} { }
html += '</div>';
$ ('#letter-b a') .click (function () {
$.getJSON('b.json', function(data}
htrnl += '</div>' i
$('#dictionary') .empty(};
htrnl += '</div>';
$.each(data, function(entrylndex, entry} {
$('#dictionary') .append(html) ;
var htrnl = '<div class=lIentry">';
htrnl += I <h3 c l a ss« "term" > I + entry [1 term 1] + I </h3:> I i
}l;
htrnl += "c d.i.v c las se vpar t vs ' + ent.xy l vpe r t t l + l</div>l¡
}} ;
html += '<div class="definition">, i ~
return false¡
htrnl += entry['definition'] i
}) ;
html += '</div>1 i
}} ;
html += '</div>' i
$('#dictionary') .append(html};
)l; Con este código en su lugar, podemos hacer clic en el vínculo B y confirmar nuestros
}} ; resultados, como se ve en la figura 6.4.
return false;
}l;
}) ;
Antes del bucle, vaciamos <di v id= "dictionary" > de modo que podamos relle-
El formato JSON es conciso, pero no tolerante. Cada llave, corchete, comillas, y coma
nado con nuestro HTML recién construido. Luego utilizamos $ . each () para examinar debe estar presente y tenida en cuenta, o el archivo no se cargará. En la mayoría de los
cada elemento en turno, construyendo una estructura HTML utilizando los contenidos navegado res, no recibiremos ni siquiera un mensaje de error; el script simplemente
del mapa de entrada. Por último, convertimos este HTML en un árbol DOM al anexado fallará en silencio.
al «d i v ».
mi 6.AJAX Aprende jQuery 1.3 lI!iI
Los scripts que se van a buscar de esta forma se ejecutan en el contexto global de la
The Devll's Dlcttonary página actual. Esto significa que tienen acceso a todas las funciones y variables defini-
by Ambroso BI""'" das globalmente, entre otras jQuery. Podemos por lo tanto seguir el ejemplo JSON para
preparar e insertar HTML en la página cuando se ejecuta el script, y situar este código
BACCHUS n. en e. J. s: '
A
A convenrent delty Invented by Ihe andents as an excuse tor gertlng drunk.
8 var entries
Is pu.bllc worshlP. !hen, a sin,
That ror devotlons pald 10 Bacchus
!"; Tha lIoto •• dare 10 run us In, "t.e rmv : n CALAMITY " I
º Jorace
L
"definition": tiA more than commonly plain and ... 11
BACKBrrE v.l, {
To speak 01 a man as you find hlm whon he can' find you. "t.e rmv. 11 CANNIBAL " ,
"pa r t.:' . IIn. ",
IIdefinitionll: !lA gastronome of the old school who..
BEARD n.
Tha halr tIlalls oommonly cut off by !hose who justly execrate tIle absurd Chlne •• custom 01 shavlng
L
{
!he hoad.
"t.e rmv. "CHILDHOOD" ,
"par t v: IIn.lI,
~ It"lI.,
"pa r t v. IIn.lI,
hasta que ocurre alguna interacción de usuario. Podríamos introducir etiquetas < ser ipt > "de f í.n i t Lono. "A politician of the seas. rr
en el momento cuando se necesitan, pero una forma más elegante de incorporar código
1;
adicional es hacer que jQuery cargue el archivo . j s directamente."
Incorporar un script es tan sencillo como cargar un fragmento HTML. En este caso, var html - ! r •
utilizamos la función global $ . getScript (), que, como sus hermanos, acepta una URL
que localiza el archivo de script:
$.each(entries, function() (
html += '<div class="entry">','
$ (docurnent) .ready(function() {
htrnl += '<h3 class="term">' + t hi s l t t e rm "I + r</h3>' ¡
$('#letter-c a') .click(function()
htrnl += '<div class=r'part"> + this['partt] I + t</div>t;
$ . getScript ( 'c. j s ') ;
html += '<div class=ttdefinition,,>r + t h í.s lt de f í.n i t i on t l + '</div>';
return false¡
html += '</div>';
}) ;
}) ;
}) ;
$('#dictionary') .htrnl(htrnl);
En nuestro último ejemplo, necesitábamos procesar los datos de resultado de modo
que pudiéramos hacer algo de utilidad con el archivo cargado. Con un archivo de script, Ahora hacer dic en el vínculo e tiene el resultado esperado, como se puede ver en
sin embargo, el procesamiento es automático; el script simplemente se ejecuta. la figura 6.5.
l!nI 6.AJAX
§
ª A more than oommonly pIaln and unmlstakable remlnder !hat the affalrs of th1s IIte are not o( our own
orde~ng. CaIBmltl8s are 01 lwo klnds: mlstortuoo lo ourselves, and good fortune lo othOlS.
<line:>Would fly abandoned
</line>
Virtue's grass advances.
CANNIBAL n.
</guote>
º A gastronome
!he of the old schoor Who preserves !he simple tastes and adheres to the nab.lral dlet of
pro'POr!< pertod.
</entry>
<entry terrn="DIElI part="n. > U
<definition>
;' The singular of "dice.u We seldorn hear the word,
CHfLDHOOD n.
because there is a prohibitory proverb, "Never say
TI1& portod 01 human Iffe Int""""dlole belwaen !he Idlocy 01 Intancy and Ih. toJly ot YOUth-lwo die. At long intervals, however, Borne one says: "The
ti
removes trcm the sin of manhood and three from the remorne of age.
die is cast,lI which is not true, fer it is cut. The
werd is found in an irnmortal ceuplet by that eminent
CLARlONET n.
poet and demestic economist, Senator Depew:
An I""trument 01 torture cperated by a persen wllh cotton In hls eara. There are lwo Instrumenls !ha! </definition>
are '"'""" !han a clanonet -lwo cJanonets.
<quote>
<line>A cube of cheese no larger than a die</line>
COMFORT n. <line>May bait the trap to catch a nibbling mie.</line>
A stat. 01 mlnd ProdUoed by contemplafton 01 B nelghbol's uneesJnass. </guote>
</entry>
</entries>
Figura 6.5. Resultado de ejecutar el script.
Estos datos se podrían expresar de muchas formas, por supuesto, y algunas reflejarían
de forma más aproximada la estructura que hemos establecido para el H1ML o JSON
Cargar un documento XMl utilizado anteriormente. Aquí, sin embargo, estamos ilustrando algunas de las caracte-
rísticas del XML diseñado para que sea más legible para las personas, corno el uso de
atributos para term y part en lugar de etiquetas.
XML es parte del acrónimo AJAX, pero en realidad no hemos cargado ningún XML
Empezaremos nuestra función de una manera familiar:
todavía. Hacerla es sencillo, y sigue la técnica JSON bastante de cerca. En primer lugar,
necesitaremos un archivo XML d . xml que contiene datos que deseamos mostrar: $ (document) .ready(function() {
<Pxm l version="l. O" encoding::;IIUTF_8 ?>
$('#letter-d a') .click(function() (
11
return false¡
To lie about another. To tell the truth about another. }) ;
</definition>
</entry> }) ;
externos como fragmentos HTML, siempre y cuando los datos no se necesiten en otras 'The lady, indignant, removed her ear.·,
aplicaciones también. En casos donde los datos se reutilizarán, JSON a menudo es una IIII will not stay, ti she said, with a pout, 1 I
buena elección debido a su rendimiento y tamaño. Cuando la aplicación remota no se I"To hear rny character lied about! ti I ,
) ,
conoce, XML proporciona la mayor garantía de interoperabilidad posible. Más que cual- I author , => ICopete Sherany',
quier otra consideración, debemos considerar si los datos ya están disponibles. Si es así, ) ,
es probable que esté en uno de estos formatos, tenemos que tomar una decisión. 'EDIBLE' => array(
ID 6.AJAX
,, Aprende jQuery 1.3 1m
·partl => 'adj. 1,
l
'definition => 'Good to eat, and wholesome to digest, as
a worm to a toad, a toad to a snake, a snake to a pig, EAVESDROP'
a pig to aman, and a rnan to a worm.
) ,
vJ. .'
'EDUCATION' => array( Secretiy lo overhear a catalogue of the crimes and Vltes of another or yourself.
'part r => "n , I
A lady with one ofherears appJied
'definition' => 'That which discloses to the wise and 10 an open keyhole heard, ínside,
disguises frorn the foolish their lack of Two female gossíps in converse free -
understanding. I I The subject engaging them was sbe.
) , '1 think,' said one, 'and my husband thinks
s , 'Iba! sbe's a prying, inquisitive minx!"
?> ,.. As soon as DO more of it she couId hear
The lady, indignant, removed her ear.
"I will no! stay,' she said, with a pout,
En una versión en producción de este ejemplo, los datos probablemente se almacena- "Io hear my character lied about!'
rían en una base de datos y se cargarían bajo demanda. Puesto que los datos son parte Gopete Shenmy
del script aquí, el código para recuperarlo es bastante sencillo. Examinamos los datos
que se han publicado y diseñamos el fragmento HTML a mostrar:
<?php Figura 6.8. Fragmento correspondiente a la petición.
$terrn = strtoupper($_REQUEST['terrn']);
if (isset ($entries [$terrn])) { .' Una vez más, observamos la ausencia de formato que ya vimos con los fragmentos
$entry = $entries[$terrn];
HTML anteriores, porque las reglas CSS no se han aplicado.
$html = 'cdiv class="entryn>, ; Puesto que estamos mostrando cómo se pasan los datos al servidor, utilizaremos
$html .= 'ch3 claS9=lItermll>' i un método diferente para solicitar entradas aparte de los botones solitarios que hemos
$html .= $terrn;
$htrnl .= '</h3>';
venido utilizando hasta el momento. En su lugar, presentaremos una lista de vínculos
$htrnl .= 'cdiv class="part">I; para cada término, y haremos que un dic en cualquiera de ellos cargue la definición co-
$htrnl .= $entry ['part '] ; rrespondiente. El HTML que añadiremos para esto se parece a lo siguiente:
$htrnl .= '</div>';
$htrnl .= '<div c1ass="definitionn>, i <div c1ass="letter'l id=l'letter-e">
$htrnl .= $entrY['definition']; <h3>E</h3>
if (isset ($entry [,guote' ] )) { <u1>
$htm1 .= '<di v c las s« quate 11>I; '1 <li><a href="e.php?term=Eavesdrop">Eavesdrap</a></li>
foreach ($entrY['guote'] as $line) { <li><a href="e.php?term=Ediblell>Edib1e</a></li>
$htm1 .= '<div c1ass="Quote-1ine">'. <li><a href="e.php?term=Education">Education</a></li>
$line .'</div>l¡
) <li><a href="e.php?term=E1oquencell>E1oquence</a></1i>
if (isset ($entry r: author' ] )) { <1i><a href="e.php?term=E1ysiumll>E1ysium</a></1i>
$htrn1 .= '<div c1ass="quote-author"> I. $entry <li><a href="e.php?term=Emancipation >Emancipation</a>
1t
[ 'author' ]
.'</div>l¡ </lb
) <1i><a href="e.php?term=Emotionrr>Emotion</a></li>
$html .= '</div>'; <li><a href=lIe.php?term=Enve1ope">Enve1ape</a></li>
) <li><a href=lle.php?term=Envy">Envy</a></li>
$htrnl .= '</div>'; <1i><a href=ne.php?term=Epitaphrr>Epitaph</a></li>
$htrnl .= '</div>'; <1i><a href=lIe.php?term=Evangelistll>Evange1ist</a></li>
print ($htrnl) ; </ul>
</div>
?>
Ahora tenemos que conseguir que nuestro código JavaScript invoque el script PHP
Ahora las peticiones a este script, que denominaremos e . php, devolverán el frag- con los parámetros correctos. Podríamos hacer esto con el mecanismo . load () nor-
mento HTML correspondiente al término que se envió en los parámetros GET. Por ejem- mal, anexando la cadena de consulta la URL y buscando datos con direcciones como
plo, cuando se accede al script con e. php?term:eavesdrop, recibimos lo que se ve e . php? t e rme eave sd.rop directamente. En su lugar, sin embargo, podemos hacer que
en la figura 6.8. jQuery construya la cadena de consulta basándose en un mapa que proporcionamos a
la función $ . get ( ) :
IDI 6.AJAX Aprende jQuery 1.3 1m
$ (document) .ready(function() { AJAX, incluso esta distinción es invisible para el usuario medio. Por lo general, la,única
$ (' #letter-e a') .click (function () {
$.get('e.php', ('term': $(this) .text()}, function(data) (
razón para elegir un método frente a otro es ajustarse a las normas del código del lado
$('#dictionary') .html(data); del servidor, p proporcionar amplias cantidades de datos transmitidos; GET tiene un lí-
}) ; mite más estricto, Hemos codificado.nuestro ejemplo PHP para hacer frente igualmente
return false¡
bien con cualquiera de los métodos, por lo que podemos cambiar de GET a POST con
}) ;
}) ;
sólo cambiar la función que invocamos:
$ (document) .ready(function() {
Ahora que hemos visto otras interfaces AJAX que proporciona jQuery, la operación $('#letter-e a') .click(function() {
de esta función parece familiar. La única diferencia es el segundo parámetro, que nos $.post('e.php', ('term': $(this) .text()}, function(data) {
permite proporcionar un mapa de claves y valores que forman parte de la cadena de ,/ $('#dictionary') .html(data);
consulta. En este caso, la clave es siempre term pero el valor se toma del texto de cada ».
return false¡
vínculo. Ahora, hacer clic en el primer vínculo en la lista hace que aparezca su defini- }) ;
ción, como muestra la figura 6.9. }) ;
Los argumentos son los mismos, y la petición ahora se realizará por medio de POST.
Tha Davll's Dlctlonary Podemos simplificar aún más el código al utilizar el método. load (), que utiliza POST
by Ambrose Bleroo
por defecto cuando se proporciona con un mapa de argumentos:
"
$ (document) .ready(function() {
A EAVESDROP v.1.
$('#letter-e a') .click (function () {
&!creUy te>ave""'.r a catalogue of 1ho crtmes aOO vlces 01 ena1her or yourseW.
S A lady wt1h oroe or he< ea", app/Ied
$ (,#dictionary' ) .load ('e.php', {, term': $ (this) .text () }) ;
k
To an opan keyhole heare!, Inslde, .:~, return false¡
Twolemale gossIps In conve"," flee- , }) ;
The SUb)ed engaglng 1hem was she.
~,
})
º
E
"11II1nk," saJd one, "and my husband 1hInks
TIla! .he's a pryIng, InqUlsltlve mlnXl"
As soon es no more o, tt she 00UId hear
;
Esta versión reducida funciona de igual forma cuando se hace clic en un vínculo,
The lady, Indignen!, removed her ear.
~ , wm oot _tay; she sald, wt1h a pout, como muestra la figura 6.10.
~ "ro hear my character Dad 8boutf"
~ GopeIaS"",,"ny
~
~
Emanoioeuon Tha Oavll's Dlctlonary
~ byAmbroseBleroe
~
~
~
~ A EMANCIPATION n.
A bondman's cI1angelillm !he tyranny a' anolher lo lile despotlsm of hlmself.
Figura 6.9. Hacer clic en un vínculo para mostrar su definición. ª~ He was a atave: at word he went and cama;
HIs !ron ooIlar cut hlm lo 11I. bona.
Then Llberty •••• sed hls ownafs name,
11ghtened 1he I1veIS 800 1nsa1bed hls own.
Todos los vínculos aquí tienen direcciones asignadas, aunque no las estamos utilizando
en el código. Esto proporciona un método alternativo para navegar por la información E
º G.J.
para usuarios que tienen desactivado JavaScript o no disponible (una forma de mejora
Ea•••• """,
progresiva). Para impedir que se puedan seguir de forma normal cuando se hacen clic, Edlble
E<lucatlon
el manejador de evento tiene que devolver falseo ~
}) ;
$html = 'cdiv class="entry">';
;.,
$htrnl .= 'ch3 class=l1term">'¡
Ahora el mismo script funcionará para enviar el formulario, incluso a medida que
$html .= $term;
$html .= '</h3>'; aumente el número de campos. Cuando realizamos una búsqueda, las entradas coinci-
$htrnl .= 'cdiv class="part">'¡ dentes se muestran, como se ve en la figura 6.11.
$html .= $entry['part'];
$html .= '</div>';
$htrnl .= 'cdiv class="definitionll>'¡
$html .= $entry['definition']; A FIDDlE n.
if (isset($entrY['guote'])) ( An lns1Jument 10 nckle hUman 06'" by f11ctIon af. ho,.e'8 taH en th8 en!mll. ola cal
foreach
$html
($entrY['guote']
.= 'cdiv
as $line)
class="quote-line">'.
(
$line .'c/div>'¡
ª
~
To Reme B81d Non>: "~Io smoJ<eyou tum
Ishall not ceaselo flddle wt11ieyou bUm."
To Nero Rome replled: "Pmy do your won¡~
) "Ils rrry excuse that you wem IIddllng nn;t"
.o onn Pludge
if (isset($entrY['author'])) (
$html .= 'cdiv clasS="quote-author">'. E
$entry f ' author J . c/div> i
I I I FIDEllTY n.
~ A vlrtue pecuHar ID !hose who ara about ID be betreyed.
) ~
~
$html I</div>' ; ~
~
ErnancipI!!!on
$html r </div> t ; ~
Enve!ooe
print($html); ~
~Jlh
~
F
invocan cuando ocurre cualquier comunicación AJAX, con independencia del código .insertBefore('#dictionary')
que lo inicie. Podemos utilizar estos métodos para proporcionar información al usuario .ajaxStart(function() {
$ (this) . show () ;
en el caso de una conexión de red lenta. El HTML para la página puede tener un men-
}) ;
saje de carga anexado: }) ;
<div id=lIloadinglr>
Loading ... Podemos encadenar el comportamiento de ocultar en esto:
</div>
$ (document) .ready(function() {
$ ( <di v id= IIloading ti >Loading ... < / di v» ' )
Este mensaje es sólo una parte de HTML arbitrario; podría incluir una imagen GIF
I
.insertBefore('#dictionary'l
animada para proporcionar un ihrobber, por ejemplo. En este caso, añadiremos algunos .ajaxStart (function() (
estilos al archivo CSS, de modo que cuando se muestra el mensaje, la página se parece $ (this) .show () ;
a la figura 6.12. }).ajaxStop(function()
$ (this) .hide ();
}) ;
The D.vll's Dlctionary }) ;
by Ambrose Blerce
Voilli.! Tenemos nuestra información de carga.
Una vez más, observe que estos métodos no tienen relación con las formas determi-
A
nadas con las que empiezan las comunicaciones AJAX.. load () anexado al vínculo A
B y . getJSON () anexado al vínculo B hacen que ocurran estas acciones.
e En este caso, este comportamiento global es deseable. Si necesitamos ser más especí-
Q ficos, sin embargo, tenemos algunas opciones a nuestra disposición. Algunos de los mé-
E
todos de observador, como. aj axError ( ) , envían a su retrollamada una referencia al
objeto XMLHttpRequest. Esto se puede utilizar para diferencia una petición de otra, y
~ proporcionar diferentes comportamientos. Una gestión más específica se puede conseguir
~
~ al utilizar la función de bajo nivel $ . aj ax ( ) , que trataremos un poco más adelante.
~
~
Emancfpa1!on
La forma más común de interactuar con la petición, sin embargo, es la rellamada suc-
E!!!2l!lm cess. Ya la hemos utilizado en varios de nuestros ejemplos para interpretar los datos que
~ proceden del servidor y completar la página con los resultados. Se puede utilizar para
];00
~ otro tipo de información, también. Considere una vez más nuestro ejemplo .load ( ) :
~
F $ (document) .ready(function() {
$('#letter-a a') .click(function() (
Figura 6.12. Proporcionar información al usuario. $('#dictionary') .load('a.html');
lmiI 6.AJAX Aprende jQuery 1.3 lIlIII
return false¡ nunca cambia. En este caso, anexaremos el manejador click al documento utilizando
p;
p;
. 1i ve () y capturando nuestros clics de esta forma: '
$ (document) .r~ády(function() {
Podemos crear una pequeña mejora aquí al hacer que el contenido cargado aparezca $ (' . t.erm ") .live ('click', function O. (
gradualmente a la vista en lugar de aparecer de repente .. load () puede hacer que se $(this) .siblings(' .definition') .slideToggle() ;
active una rellamada cuando se termine: });
}) ;
$ (document) .ready(function() {
$('#letter-a a') .click(function() ( El método . 1i ve () le dice al navegador que observe todos los clics en cualquier parte
$('#dictionary') .hide() .load('a.html', function() { ,de la página. Si (y sólo si) el elemento en el que se ha hecho clic coincide con el selector
$ (this) .fadelnO;
}) ;
" . term, entonces el manejador se ejecuta. Ahora el comportamiento de alternar ocurrirá
return false¡ en cualquier término, incluso si se añade por una transacción AJAX posterior.
p;
}) ;
+ r </div> 1;
tiene un efecto sobre el entorno local. Los servicios que publican scripts que son ejecuta- html += '<div class="definition">, ;
bles de esta forma también proporcionarán una API con la que interactuar con el script htrnl += entry[tdefinition'l;
remoto. if lentry t: quote' J) {
htrnl += '<div class="quote">'¡
Otra opción es utilizar la etiqueta H1ML <Íframe> para cargar datos remotos. Este / .eachlentry['quote'J, functionllinelndex, line)
elemento permite que se utilice cualquier URL como la fuente para sus datos a buscar, html += '<div class="quote-line">' + line
incluso si no coincide con el servidor de la página host. Los datos se pueden cargar y + </div>';
I
mostrar fácilmente en la página actual. Sin embargo, manipular los datos normalmen- )) ;
te requiere la misma cooperación necesaria para el enfoque de etiqueta e ac r p t s: los
í
if lentry['author'J)
html += '<div class="quote-author >'
ll
scripts dentro de e i f r arne » necesitan proporcionar explícitamente los datos a objetos + entry['author l) + '</div>';
en el documento padre. }
html += '</div>'¡
"
)
Utilizar JSONP para datos remotos htrnl += '</div>';
htrnl += '</div>';
$I'#dictionary') .appendIhtml) ;
La idea de utilizar etiquetas « sc r pt s para ir a buscar archivos JavaScript desde
í
)) ;
una fuente remota se puede adaptar para recuperar archivos JSON desde otro servidor }) ;
también. Para hacer esto, necesitamos modificar algo el archivo JSON en el servidor, sin return false¡
}) ;
embargo. Existen varios mecanismos para hacer esto, uno de los cuales se soporta direc- }) ;
tamente por jQuery: JSON with Padding, o JSONP.
El formato de archivo JSONP consta de un archivo JSON estándar que se ha situado Normalmente no se nos permitiría ir a buscar JSON desde un servidor remoto (exam-
entre paréntesis y precedido con una cadena de texto arbitrario. Esta cadena, el "relle- ples .lear~ingj query. com en este caso). Sin embargo, puesto que este archivo está
no", se determina por el cliente que solicita los datos. Debido a los paréntesis, el cliente configurado para proporcionar sus datos en el formato JSONP, podemos obtener los
puede hacer que se invoque una función o que se establezca una variable dependiendo datos al anexar una cadena de consulta a nuestra URL, utilizando? como un marcador
de lo que se envíe como cadena de relleno. de posición para el valor del argumento callback. Cuando se realiza la petición, jQuery
Una implementación PHP de la técnica JSONP es bastante sencilla: reemplaza la ? por nosotros, analiza el resultado, y lo pasa a la función de éxito como
<?php data como si esto fuera una petición JSON local.
pz i nt, I$_GET ['callback' J .' 1'. $data .')'); Observe que las mismas precauciones de seguridad se mantienen aquí como antes;
?> cualquier cosa que el servidor decida devolver al navegador se ejecutará en el ordena-
dor del usuario. La técnica JSONP solamente se debería utilizar con datos procedentes
Aquí, $data es una variable que contiene una representación de cadena de un archi- de una fuente de confianza.
vo JSON. Cuando se invoca este script, el parámetro de cadena de consulta callback
se anexa al archivo resultante que se devuelve al cliente.
Para demostrar esta técnica, solamente necesitamos modificar ligeramente nuestro Opciones adicionales
ejemplo JSON anterior para invocar esta fuente de datos remota en su lugar. La función
$ . getJSON () hace uso de un carácter de marcador de posición especial, ?, para con- El cuadro de herramientas AJAX proporcionado por jQuery está bien surtido. Hemos
seguir esto.
tratado varias de las opciones disponibles, pero simplemente nos hemos quedado en
$Idocument) .readYlfunctionl) { la superficie. Aunque existen muchas variantes a tratar aquí, proporcionaremos una
var url = 'http://examples.learningjquery.com/jsonp/g.php'; visión de conjunto de algunas de las formas más destacadas de personalizar las comu-
$ I '#letter-g a') .clicklfunction() { nicaciones AJAX.
lIiDI 6. AJAX Aprende jQuery 1.3 lID
$.aj axSetup I(
El método AJAX de bajo nivel url: ra.ht~lll
type: 'POST r ,
dataType: 'html'
Hemos visto varios métodos que inician todas las transacciones AJAX. Internamente, }); r
jQuery mapea cada uno de estos métodos en variantes de la función global $ . aj ax ( ) . ".,
$.ajaxl{
En lugar de suponer un tipo determinado de actividad AJAX, esta función toma un mapa type: 'GET' f
type: 'GET', la llamada $. aj axSetup (), por lo que no se tiene que proporcionar cuando se invoca
dataType: 'html', $ . aj ax () . Por el contrario, se asigna al parámetro type un valor predeterminado de
success: functionldata) ( POST, pero esto se puede anular en la llamada $ . aj ax () a GET.
$I'#dictionary') .htmlldata);
}
});
Cargar partes de una página HTML
"
Necesitamos especificar explícitamente el método de petición, el tipo de datos que La primera y más sencilla técnica AJAX que hemos tratado fue ir a buscar un frag-
se devolverá y lo que hacer con los datos resultantes. Claramente, esto es un uso menos mento HTML y situarlo en una página. Algunas veces, sin embargo, el servidor ya pro-
eficiente de esfuerzo de programador; sin embargo, con este trabajo adicional viene una porciona el HTML que necesitamos, pero está rodeado por una página HTML que no
gran cantidad de flexibilidad. Algunas de las posibilidades de uso especiales que vienen queremos. Cuando no es conveniente hacer que el servidor proporcione los datos en el
con utilizar una llamada $ . aj ax () de bajo nivel incluyen: formato que deseamos, jQuery puede ayudamos en el lado del cliente.
Considere un caso como nuestro primer ejemplo, pero en el que el documento que
• Impedir que el navegador guarde en caché respuestas del servidor. Esto puede
contiene las definiciones de diccionario es una página HTML completa como ésta:
ser de utilidad si el servidor produce sus datos dinámicamente.
e ht.m'L xmlns= '~http://www . w3 .org/1999/xhtml l1xml: lang= 11en"
• Registrar funciones de rellamada separadas para cuando la petición se completa
lang=11 entt>
con éxito, con un error, o en todos los casos. <head>
<meta http-equiv=I1Content-Type"
• Suprimir los manejadores globales (como los registrados con $. aj axStart ()) content="text/htrnl¡ charset=utf-8 /> U
que se activan normalmente por todas las interacciones AJAX. <title>The Devil's Dictionary: H</title>
• Proporcionar un nombre de usuario y contraseña para autenticación con el host <link rel=lIstylesheetll href=lIdictionary.cssll
remoto. type=lItext/csslI media=lIscreenll />
<!head>
Para detalles sobre utilizar éstas y otras opciones, consulte la Guía de referencia jQuery <body>
o consulte la referencia API online (http://docs.j query. com/Aj ax/j Query. <div id=lIcontainerll>
<div id=lIheader >
rt
ajax).
<h2>The Devil's Dictionary: H</h2>
<div class=lIauthorll>by Ambrose Bierce</div>
<!div>
Modificar opciones predeterminadas <div id=lIdictionary">
<div class="entry">
La función $ . aj axSetup () nos permite especificar valores predeterminados para <h3 class="te::rm">HABEAS CORPUS</h3>
cada una de las opciones utilizadas cuando se invocan los métodos AJAX. Toma un <div class="part">n.</div>
<div class="definition">
mapa de opciones idénticas a las disponibles para $. aj ax () , y hace que estos valores A writ by which aman may be taken out of jail
se utilicen en todas las siguientes peticionesAJAX a menos que se anulen. when confined for the wrong crime.
;
lIiII 6. AJAX Aprende jQuery 1.3 llliI
c/div> Para eliminar estos elementos extraños, podemos utilizar una nueva característica del
cjdiv>
método .load () . Cuando se especifica la URL del documento a cargar, también pode-
cdiv class=tlentryll> mos proporciqnar una expresión de selector jQuery. Si está presente, esta expresión se
ch3 class=lIterml1>HABITc/h3> utiliza para localizar una parte del doc,umento cargado. Solamente la parte coincidente
cdiv class="part">n.c/div> del documento se inserta en la página. En este caso, podemos utilizar esta técnica para
cdiv class="definition">
A shackle for the free.
extraer solamente las entradas de diccionario del documento e insertarlas:
c/div>
$ (document) .ready(function() {
</div>
$('#letter-h a') .click(function()
</div>
$('#dictionary/).load('h.html .entry');
/ return false i
</div>
}) ;
</body>
}) ;
</htmb
Ahora las partes irrelevantes del documento se excluyen de la página, como mues-
Podemos cargar todo el documento en nuestra página utilizando el código que hemos
escrito anteriormente: , tra la figura 6.14.
$ (document) .ready(function() {
The Devll's Dlctlonary
$ ('#letter-h a') .click(function() {
$('#dictionary') .load('h.html');
" by Ambmse BIerce
return false¡
}) ;
HABEAS CORPUS n.
A
}) ;
A wrtt by whlch a men may be taken out 01¡aft when conftned for tl1e wrong orIme.
Sin embargo, esto produce un efecto extraño debido a las piezas de la página HTML ªlO HABIT n.
que no queremos incluir, como se ve en la figura 6.13. A shackle lo< !he free.
º E
The Devll's Dlctlonary HALF n.
by Ambrose BIerce ~ Oneoftwoequal parislnlo whIch a tIllng may be dlvlded, orconsldered asdlvlded.ln tIlefllurteenll1
E'<llble oentury B heated dlscusslon arose among theoIoglsts ami phllooophe rs as lo _ Omnlsdence
EducaUOn oould patt en object InID II1ree halves; olld !he pIous Father Aldrovlnus pubDcIy pmyed In th8
E!oquence calhedral al Rouen lila! God would derroostrate 111.aHlrmatIve 01 tIle proposItion In SOOle slgnal 8lld
Elvslum unm_1e way, and parUcular1y (If tt should pIease HIm) upon th8 body 01 tila! hardy blasphemer,
A The Devil's D.ictlonary: H Emenc!oa1!on ManutJus Prodnus. who malntalned the nega1tve. ProcfnUs, however, was spared 10 die of the bita of
~ a >1per.
ªlO by Ambrose BIerce ~
IDoo'
~ HANO n.
~
E
º HABEAS CORPUS n.
A writ by which aman may be taken out of jaD when confined fer the wrong Clime.
F
A singular InslJUment wom
pocIcet
at !he eml 01!he human snn and oornmonly thrUst In!o somebody's
~ HAPPINESS n.
~
~ HABIT n.
~ Figura 6.14, Página resultante.
~
Emanc!patlon
A shackle ter !he free.
~
Eswe!ooe
IDoo' HALF n.
§l!!!m!!
Evana.lIs! One of two aqual parts Into whlch s Ihing may be dlvided, o, consldered as divided.
Resumen
In Ihe fourtsenlh cenlury a healed disaJsslon arase amongllleotogists aOO
F phñcsopbers as lo whetherOmniscience could part en object Into Ihree halves; and
lhe pious Falhe, Aldrovinus publlcJy prayed In !he call1edral al Rouen !ha! God
Hemos aprendido que los métodos AJAX proporcionados por jQuery nos pueden
would demonslrale lile aflirmalive of lile prcposition in some signsl aOO ayudar a cargar datos en formatos muy diferentes desde el servidor sin un refresco de
unmlstakable wav. and particularlv CIf itshould pisase Him} ueon lile bodv of Ihal página. Podemos ejecutar scripts desde el servidor bajo demanda, y devolver datos al
Figura 6.13. Página con HTML que no se quiere incluir. servidor.
ID 6.AJAX
,
ri
/
También hemos aprendido cómo tratar con desafíos comunes de técnicas asíncronas
de carga, como mantener manejadores vinculados después de que ha ocurrido una carga
y cargar datos desde un servidor de terceros.
Esto termina la parte de tutorial del libro. Contamos con las herramientas principales "
ofrecidas por jQuery: selectores, eventos, efectos, manipulación DOM y peticiones de
servidor asíncronas. Éstas no son las únicas formas en las que jQuery puede ayudamos;
trataremos algunas de las muchas posibilidades de uso conferidas por plug-ins jQuery
en capítulos posteriores. Pero primero, examinaremos algunas combinaciones de estas
técnicas que mejoran nuestras páginas Web en nuevas e interesantes formas.
./
"
,/
"
.'
En los seis primeros capítulos, hemos explorado la librería jQuery en una serie de tu-
toriales que se han centrado en cada uno de los componentes jQuery y utilizado ejemplos
7
como una forma de ver esos componentes en acción, Desde el capítulo 7 al 9 invertimos
el proceso; empezaremos con ejemplos de problemas del mundo real, y veremos cómo
podemos utilizar métodos jQuery para solucionarlos.
Aquí, utilizaremos un librería online como nuestro sitio Web modelo, pero las téc-
nicas que preparamos se pueden aplicar a una amplia variedad de otros sitios también,
Manip_ulación
desde weblogs a portfolios, desde sitios empresariales dirigidos al mercado a intranets
corporativas. El capítulo 7 y 8 se centran en dos elementos comunes de la mayoría de
sitios (tablas y formularios), mientras que el capítulo 9 examina un par de formas para
mejorar visualmente conjuntos de información utilizando barajadores y rotativos.
Puesto que el movimiento de los estándares Web se ha vuelto muy generalizado en
de tabla los últimos años, el diseño basado en tabla se ha ido abandonando en favor de diseños
basados en CSS. Aunque las tablas a menudo se empleaban como una medida necesa-
ria en los años 90 para crear diseños de múltiples columnas y otros diseños complejos,
nunca se pensaron para utilizarse de esa forma. Por otro lado, CSS es una tecnología
expresamente creada para estas tareas de presentación.
Pero éste no es el lugar para una discusión ampliada sobre el papel adecuado de las
tablas. Basta decir que en este capítulo utilizaremos jQuery para aplicar técnicas para
aumentar la legibilidad, usabilidad y atractivo visual de contenedores de datos tabulares
marcados semánticamente. Para un mayor detalle sobre aplicar HTML semántico yacce-
sible a tablas, un buen lugar en el que empezar es la entrada de blog de Roger J ohansson,
"Bring on the Tables" en http://www . 456bereastreet. com/archive/200410/
bring_on_the_tables/.
B 7. Manipulación de labIa Aprende jQuery 1.3 lf.iI
Algunas de las técnicas que aplicamos a tablas en este capítulo se pueden encontrar </th>
</tr>
en plug-ins como Table Sorter de Christian Bach. Para más información, visite el jQuery </thead>
Plugin Repository en http://plugins . j query. com/. ctbody>
En este capítulo, tratamos:
</tbody>
• Ordenar </table>
• Paginación El servidor puede reaccionar al parámetro de cadena de consulta al devolver los con-
• Resaltar filas tenidos de la base de datos en un orden diferente.
• Descripciones emergentes
Impedir que la página se refresque
• Contraer y expandir filas
Esta configuración es sencilla, pero requiere que la página se refresque para cada
• Filtrado
operación de ordenar. Como hemos visto, jQuery nos permite eliminar esos refrescos de
página al utilizar métodos AJAX. Si tenemos los encabezados de columna configurados
como vínculos como antes, podemos añadir código jQuery para cambiar esos vínculos
Ordenar y paginar por peticiones AJAX:
$ (document) .ready(function() {
Dos de las tareas más comunes realizadas con datos tabulares son ordenar y paginar. $ ('#my-data th a') .click(function() {
En una tabla extensa, ser capaz de reorganizar la información que estamos buscando es $ ('#my-data tbody') .load($(this) .attr('href'));
de un valor incalculable. Desafortunadamente, estas operaciones de utilidad pueden ser return false;
}) ;
de lo más difícil de poner en acción.
}) ;
En primer lugar, examinaremos lo que se necesita para llevar a cabo una ordenación
de tabla, reordenando datos en una secuencia que es de más utilidad para el usuario. Ahora cuando se hace clie en los anclas, jQuery envía una petición AJAX al servidor
para la misma página. Cuando se utiliza jQuery para realizar una petición de página utili-
zando AJAX, establece el encabezado HTTPx-Requested-WithenXMLHttpRequest
Ordenación del lado del servidor de modo que elservidor puede determinar que se está realizando una petición AJAX.
El código de servidor se puede escribir para que devuelva solamente el contenido del
Una solución común para ordenación de datos es llevarla a cabo en el lado del servi- propio elemento « t.bcdy», y no la página de alrededor, cuando este parámetro está
dor. Los datos en tablas a menudo proceden de una base de datos, lo que significa que presente. De esta forma podemos tomar la respuesta e insertarla en lugar del elemento
el código que lo extrae de la base de datos puede solicitarlo en un orden determinado «t body» existente.
(utilizando, por ejemplo la cláusula ORDERBY del lenguaje SQL). Si tenemos código del Éste es un ejemplo de mejora progresiva. La página funciona perfectamente bien sin
lado del servidor a nuestra disposición, es muy sencillo empezar con una ordenación ningún JavaScript, ya que los vínculos para ordenación del lado del servidor siguen es-
predeterminada razonable.
tando presentes. Cuando JavaScript está disponible, sin embargo, AJAX secuestra la pe-
Sin embargo, ordenar es de mucha utilidad cuando el usuario puede determinar el tición de página y permite que la ordenación ocurra sin una carga de página completa.
orden de ordenación. Un método comúnes convertir los encabezados de tabla « th» de
columnas que se pueden ordenar en vínculos. Estos vínculos pueden ir a la página actual,
pero con una cadena de consulta anexada indicado la columna por la que ordenar: Ordenación JavaScript
e t ab l,e id= my-data
11 11 >
cthead> -/ Sin embargo, existen momentos, cuando o bien no queremos esperar respuestas del
ctr>
servidor cuando ordenamos, o no tenemos un lenguaje de script del lado del servidor
cth class=lIname ll>
- TIUo
Learnlng Mambo: A
SIep.by-Step l\JlOrial
lo Building You,
Aulho,(s)
Hagen Graf
Douglas Paterson
_;m:mml·F.'I(;1I11m1
Feb2007
0002006
$oW.49
$oW.49
Nos gustaría convertir los encabezados de tabla en botones que ordenen los datos
por sus columnas respectivas. Exploremos algunas formas de hacer esto.
Webs~. Observe nuestro uso de las etiquetas «t.he ad» y « t body» para segmentar los datos
en agrupaciones de fila. Muchos autores HTML omiten estas etiquetas, pero pueden
MoodIe E-I..earnlng
Course Oevelopmenl
WllUam Rice May2006 $35.99 resultar de utilidad al proporcionamos selectores CSS más convenientes a utilizar. Por
ejemplo, suponga que deseamos aplicar una distribución típica de fila par /frnpar a esta
AJA)( 3M PHP: Cr1sllanDerte, MlhaJBuclca, flllp
tabla, pero solamente al cuerpo de la tabla:
Building Rasponslve Che~ T0$8, Bogdan Mar 2006 531.49
Web Applloatlons Brtnzarea $ (document) .ready(function() {
$('table.sortable tbody tr:odd') .addClass('odd');
OpenVPN: Building $('table.sortable tbody tr:even') .addClass('even');
Bnd Integrallng Virtual
Prtvata Networ1<s
Mar1<usFellner May2006 553.99 n.
Esto añadirá colores alternas a la tabla, como se ve en la figura 7.2, pero deja el en-
Figura 7.1. Tabla inicial con datos.
.' cabezado sin tocar.
ctable claS9=t'sortable">
c::thead>
c::tr>
<th></th>
TIUe Autho,(s) I~.'C].2!@i'.m,.a~
,~,;' ." .
, ..;'\1::-:< ':~ ;f,;j-;;;~~"
<th>Title</th>
<th>Author(s) </th>
<th>Publish Date</th>
<th>Price</th>
</tr>
</thead>
<tbody>
c::tr>
c t do e i.mq src=" ../images/covers/small/184 7192386 .png"
width="49" height="61 alt="Building Websites with
11
</td>
<td>Building Websites with Joomla! 1.5 Beta l</td>
<td>Hagen Graf</td>
<td>Feb 2007</td>
<td>$40.49</td>
</tr>
c::tr>
<td><img src»" ../images/covers/small/1904811620 .png" Figura 7.2. Añadir colores alternas a la tabla.
width=tl49" height=1I61" alt="Learning Mambo: A
Step-by-Step Tutorial to Building Your Website" /> Utilizando estas etiquetas de agrupación de fila, seremos capaces de seleccionar y
</td>
manipular fácilmente las filas de datos sin afectar al encabezado.
<td>Learning Mambo: A Step-by-Step Tutorial to Building Your Website
</td>
<td>Douglas Paterson</td> Ordenación alfabética básica
<td>Dec 2006</td>
<td>$40.49</td>
Ahora llevemos a cabo una ordenación sobre la columna Title (título) de la tabla.
</tr>
</tbody> Necesitaremos una clase en la celda del encabezado de tabla de modo que podamos se-
</table> leccionarlo adecuadamente:
\;":""''''
Para hacer esto, podemos proporcionar una función comparador al método . sort ( ) : click (), esto no nos permitiría capturar fácilmente una parte crucial de información:
la columna índice del encabezado que se hace clic. Puesto que . each () pasa el índice
arr.sort(function(a,b)
de iteración a su función de rellamada, podemos utilizarlo para encontrar la celda rele-
if (a < b)
return -1;
vante en cada fila de los datos más adelante.
if (a > b) Una vez que hemos encontrado la celda encabezado, recuperamos una tabla de todas
return 1; las filas de datos. Esto es un estupendo ejemplo de cómo. get () es de utilidad al trans-
return O;
formar un objeto jQuery en una tabla de nadas DOM; aunque los objetos jQuery actúan
}),
como tablas en muchos aspectos, no tienen ninguno de los métodos de tabla nativos
Esta función devuelve un número negativo si a debería venir pririiero en la tabla disponibles, como . sort ( ) .
ordenada, un número positivo si b debería venir primero, y cero si el orden de los ele- Ahora que tenemos una tabla de nadas DOM, podemos ordenarlos, pero para hacer
mentos no importa. Con esa información a mano, el método. sort () puede secuenciar esto necesitamos escribir una función comparador apropiada. Deseamos ordenar las filas
los elementos de forma apropiada: de acuerdo a los contenidos textuales de las celdas de tabla relevantes, por lo que ésta
será la información que la función comparador examinará. Sabemos qué celda examinar
[1, 3, 3, 4, 9, 10, 52, 62, 63, 64, 97}
porque capturamos el índice de columna en la llamada . each ( ) .
Convertimos el texto en mayúscula porque las comparaciones de cadena en JavaScript
Utilizar un comparador para ordenar filas de tabla
son sensibles a mayúscula y minúscula y queremos que nuestra ordenación no sea sen-
Nuestra rutina de ordenación inicial se parece a esto: sible a mayúscula y minúscula. Almacenamos los valores clave en variables para evitar
cálculos redundantes, compararlos, y devolver un número positivo o negativo como se
$ (document) .ready(function() ( ha tratado antes.
$('table.sortable') .each(function() {
var $table = $(this),
Por último, con la tabla almacenada, pasamos en bucle por las filas y las insertamos
$('th', $table) .each(function(column) en la tabla. Puesto que . append () no clona nadas, esto las mueve en lugar de copiar-
var $header = $(this), las. Nuestra tabla está ahora ordenada.
~
•• 7. Manipulación de labia
Aprende jQuery 1.3 1&1
Esto es un ejemplo del equivalente de mejora progresiva, es decir, degradación ele- $header.removeClass('hover') i
gante. A diferencia de la solución AJAX tratada anteriormente, esta técnica no puede l,) .click(function() (
funcionar sin JavaScript¡ asumimos que el servidor no dispone de lenguaje de script var rows = $table.find('tbody > tr') .get();
rows.sort(function(a, b) (
disponible para este ejemplo. Puesto que se necesita JavaScript para el tipo de trabajo,
var keyA = $(a) .c~ldren('td') .eq(column) .text()
estamos añadiendo la clase clickable por medio del código solamente, asegurándo- .toUpperCase() ;
nos así que la interfaz indica que la ordenación es posible (con una imagen de fondo) var keyB = $(b) .children('td') .eq(column) .text()
solamente si el script se puede ejecutar. La página se degrada en una que sigue siendo .toUpperCasel);
funcional, aunque sin ordenación disponible. if (keyA < keyB) return -1;
if IkeyA > keyB) return 1;
Hemos movido las filas, de ahí que nuestros colores alternos de fila estén ahora des- return O;
controlados, como muestra la figura 7.3. l) ;
/ $.each(rows, function(index, row) {
$table.children('tbody') .append(row);
l) ;
alternateRowCo1ors($table);
l) ;
l
l) ;
l) ;
l) ;
Necesitamos volver a aplicar los colores de fila después de llevar a cabo la ordena-
ción. Podemos hacer esto al situar el código de color en una función que podemos in-
vocar cuando sea necesario:
$ (document) .ready(function() (
var alternateRowColors = function($table)
$('tbody tr:odd', $table)
.removeClass('even') .addClass('odd');
$ ('tbody tr:even', $table)
.removeClass{'odd').addClass('even')¡
}; Figura 7.4. Solucionar el problema de color de fila.
$('table.sortable') .each(function()
var $table = $(this);
alternateRowColors($table);
$('th', $table) .each(function(column) El poder de los plug-ins
var $header = $(this);
if ($header.is(' .sort-alpha'» ( La función al ternateRowColors () que escribimos es un perfecto candidato para
$header.addClass('clickable') .hover(function()
convertirse en un plug-in jQuery. De hecho, cualquier operación que deseemos aplicar
$header.addClass('hover');
l, functionl) ( a un conjunto de elementos DOM se puede expresar fácilmente como un plug-in. Para
conseguir esto, necesitamos modificar nuestra función existente solamente un poco:
mi 7. Manipulación de tabla Aprende jQuery 1.3 mil
jQuery.fn.alternateRowColors = function() { .toUpperCase();
$('tbody tr:odd', this) if (keyA < XeyB) return -1;
.removeClass('even') .addClass('odd'); if (keyA > keyB) return 1;
$('tbody tr:even', this) return O; ¡
.removeClass(lodd ,addClass('even') i
1) j); "
return this¡
}; Podemos extraer la computación clave y realizar esto en un bucle aparte:
Problemas de rendimiento Establecemos la propiedad expanda en null después de haber terminado con ella
para limpiar. Esto no es estrictamente necesario en este caso, pero es un buen hábito
Nuestro código funciona, pero es bastante lento. El culpable es la función compara- establecerlo porque las propiedades expanda que se van dejando atrás pueden ser la
dor, que está llevando a cabo una gran cantidad de trabajo. Este comparador se invo- causa de pérdidas de memoria. Para más información, véase el Apéndice C.
cará muchas veces durante el transcurso de una ordenación, lo que significa que cada
momento adicional que pasa procesando se magnificará.
El algoritmo de ordenación utilizado por JavaScript no se define por el estándar. En lugar de utilizar propiedades expando, jQuery proporciona un mecanismo de
Puede ser una sencilla ordenación como una ordenación burbuja (el mejor caso de 0(n )
2 almacenaje de datos alternativo que podríamos utilizar. El método. data () establece
en términos de complejidad computacional) o un enfoque más sofisticado como orde- o recupera información arbitraria asociada con elementos de página, y el método . re-
nación rápida (que es 0(n log n)). Sin embargo, es seguro decir que duplicar el número moveData () se libra de cualquier información almacenada:
de elementos en una tabla será más del doble el número de veces que se invoca la fun- $.each(rows, function(index, row) {
ción comparador. $(row).data('sortKey', $ (row) .children('td')
El remedio para nuestro comparador lento es precalcular para la comparación. .eq(column) .text() .toUpperCase(»;
) ;
Empezamos con nuestra función de ordenación actual y lenta: rows.sort(function(a, b) (
if ($(a) .data('sortKey') < $(b) .data('sortKey'»
rows.sort(function(a, b) (
return -1;
var keyA = $(a) .children('td') .eq(column) .text()
if ($(a) .data('sortKey') > $(b) .data('sortKey'»
.toUpperCase() ;
return 1;
var keyB = $(b) .children('td') .eq(column) .text()
lID 7. ManipuLación de tabla AprendejQuery 1.3 ••
-
así por el apellido, corno muestra la figura 7.5.
niente, ya que a menudo estamos trabajando con objetos jQuery en lugar de directamen-
te con nadas DOM. También evita problemas potenciales con pérdidas de memoria de
Internet Explorer. Sin embargo, para el resto de este ejemplo, nos ceñiremos a las pro- • Tltl. • Autllor(s) d:rrl:lIm:W1 Ftttlllill!l :1.1
piedades expanda para practicar el alternar entre operaciones en nadas DOM y opera-
ciones en objetos jQuery.
$table.alternateRowColors() /
basándonos en la clase aplicada al encabezado de tabla: }) /
jQuery.fn.alternateRowColors = function() ( }
$('tbody tr:odd', this) }) /
.removeClass('odd') .addClass(reven') i
return this¡ La variable f indSort:Key se duplica como la función para calcular la clave, y existe
}/ una indicación para indicar si el encabezado de columna se marca con una clase permi-
$ (document) .ready(function() (
tiendo que se pueda ord-enar. Ahora podemos ordenar por fecha o precio, como mues-
$('table.sortable') .each(function()
var $table = $(this)/ , tran las figuras 7.6 y 7.7, respectivamente.
$table.alternateRowColors() /
$('th', $table) .each(function(column)
var $header = $(this) /
var findSortKey;
.'
if ($header.is('.sort-alpha'») {
findSortKey • function($cell) {
return $cell.find('.sort-key')
.text() .toUpperCase() o', "
el se if ($header. is ( ,.sort-numeric' l I {
findSortKey = function($cell) {
var key = $cell.text() .replace(/A[A\d.l*/. ");
key. parseFloat(key);
return isNaN(key) ? o : key;
} ;
}
else if ($header.is('.sort-date'» { Figura 7.6. Ordenar por fecha.
findSortKey = function($cell) {
return Date.paree('l + $cell.text(»¡
I
=:. ntlo I-::
Author(s}
};
if (findSortKey) {
$header.addClass('clickable') .hover (function ()
$header.addClass('hover')/
}. function () (
$header.removeClass('hover') /
}) .click (function () (
var rows = $table.find('tbody > tr') .get() /
$.each(rows, function(index, row) (
var $cell = $ (row) .children('td') .eq(column) /
row.sortKey = findSortKey($cell);
}) /
rows.8ort(function(a. b) (
if (a.90rtKey < b.sortKey) return -1/
if (a.8ortKey > b.sortKey) return 1/ Figura 7.7. Ordenar por precio.
IEI 7. Manipulación de tabla r'" Aprende jQuery 1.3 lID
Resaltar columna Si sortDirection es igual a 1, entonces la ordenación será la misma que ant~s. Si
es igual a -1, la ordenación se invertirá. Podemos utilizar clases para mantener registro
Puede ser una buena mejora de interfaz de usuario recordar visualmente al usuario del orden actual de una columna:
1
lo que se ha hecho en el pasado. Al resaltar la columna que se ha utilizado recientemente
jQuery.fn.alternateRowColors = function()
para ordenar, podemos centrar la atención del usuario en la parte de la tabla que es más $('tbody tr:odd', this)
probable que sea relevante. Afortunadamente, puesto que ya hemos determinado cómo ,removeClass('even') .addClass('odd') i
seleccionar las celdas de tabla en la columna, aplicar una clase a esas celdas es sencillo: $('tbody tr:even', this)
.removeClass{'odd') .addClass{'even') j
rows.sort(function(a, b) {
if (a.sortKey < b.sortKey) return -sortDirection; if (a.sortKey < b.sortKey) return -sortDirection¡
if (a.sortKey > b.sortKey) return sortDirection¡ if (a.sortKey > b.sortKey) return sortDirection;
ID 7. Manipulación de tabla
r~"
! Aprende jQuery 1.3 lID
return O;
Como con ordenar, la paginación a menudo se realiza en el servidor. Si los datos a
}) ;
A 4 E 1
B 5 e 2
e 2 G 3
D 7 A 4
E 1 B 5
F 8 H 6
G 3 D 7
H 6 F 8
Antes Después
Figura 7.9. Aplicar estilo a los encabezados de columna.
Figura 7.10. Ordenar por la primera y segunda columna.
.-- no queremos que los encabezados o pies de página desaparezcan cuando nos movemos
A 4 e 2
---
I a la segunda página. Para seleccionar las filas que contienen datos, ocultamos todas las
B 5 A 4 filas primero)uego seleccionamos las filas en la página actual, mostrando las filas se-
e
D
2
7
B
D
5
7
I~ leccionadas. El método. slice () mostrado aquí funciona como el método de tabla del
mismo nombre; reduce la selección a los elementos entre las dos posiciones dadas.
La tarea más propensa a error al escribir este código es formular las expresiones a
Antes Después
utilizar en el filtro. sl ice () . Necesitamos encontrar los índices de las filas al principio
Figura 7.11. Resultado de añadir paqlnación, y final de la página actual. Para la fila inicial, simplemente multiplicamos el número de
la página actual por el número de filas en cada página. Multiplicar el número de filas
Solamente los datos ya presentes en la página se pueden manipular por JavaScript. por uno más que el número de página actual nos da la fila inicial de la siguiente página;
Para impedir que esto sea un problema, debemos llevar a cabo ambas tareas en el servi- el método. slice () va a buscar las filas hasta y no incluido este segundo parámetro.
dar (preguntando al servidor el conjunto de datos correcto en cada página u operación
de ordenación), o ambas en el navegador (con todos los posibles datos disponibles para Mostrar el paginador
JavaScript en todo momento), de modo que los primeros resultados mostrados son en
realidad las primeras filas en el conjunto de datos, como muestra la figura 7.12. Para añadir interacción de usuario a la mezcla, necesitamos situar un pagina dar
junto a la tabla: un conjunto de vínculos para navegar a diferentes páginas de datos.
Podríamos hacer esto al insertar simplemente vínculos para las páginas en el código
A 4 E 1 HTML, pero esto violaría el principio de mejora progresiva que hemos estado adop-
B 5 e 2 tando. En su lugar, deberíamos añadir los vínculos utilizando JavaScript, por lo que los
e 2 G 3 usuarios sin posibilidades de script disponibles no sean inducidos a error por vínculos
que no pueden funcionar.
D 7 A 4
Para mostrar los vínculos, necesitamos calcular el número de páginas y crear un nú-
Antes Después mero correspondiente de elementos DOM:
Figura 7.12. Resultado correcto. var nurnRows = $table.find('tbody tr') .length;
var numpages = Math.ceil{nurnRows / numPerPage) i
Este código muestra la primera página, diez filas de datos. Una vez más nos basa-
mos en la presencia de un elemento « t.body» para separar datos de los encabezados; Figura 7.13. El paginador en la tabla.
lliEI 7. Manipulación de tabla r Aprende jQuery 1.3 1m
Habilitar los botones del paginador Para corregir este problema, aprovecharemos una de las características más avanza-
das de los métodos de vinculación de eventos de jQuery. Podemos añadir un conjunto
Para hacer que estos nuevos botones funcionen en realidad, necesitamos actualizar la de datos de eyento personalizado al manejador cuando lo vinculamos que seguirá es-
variable currentPage y luego ejecutar nuestra rutina de paginación. A primera vista, tando disponible cuando se invoque el,manejador. Con esta posibilidad de uso a nuestra
parece que deberíamos poder hacer esto al establecer currentPage en page, que es el disposición, podemos escribir:
valor actual del iterador que crea los botones: $(I<:span class="page-numberll></span>r) .text(page + 1)
.bind('click', {newpage: page} , function(event) (
$Idocument) .readylfunctionl) (
currentPage = event.data['newPage');
$I'table.paginated') .eachlfunctionl)
repaginate() ;
var currentPage = Di
}) .appendTo($pager) .addClass('clickable');
var numPerpage = la;
var $table = S(this);
var repaginate = functionl) ( El nuevo número de página se pasa al manejador por medio de la propiedad data
$table.find('tbody tr') .hide() del evento. De esta forma el número de página escapa a los peligros del cierre y se con-
,slice(currentPage * numPerpage, gela en el tiempo en el valor que contenía cuando el manejador se vinculó. Ahora los
(currentPage + 1) * numPerPage) , vínculos de nuestro paginador pueden llevamos de forma correcta a diferentes páginas,
.show();
};
como muestra la figura 7.15.
var numRows = $table.findl'tbody
var numpages = Math.ceillnumRows
tr') .length;
/ numperpage);
var $pager = $('<div class="pager"></div>') i
.'
for (var page = O; page < numPages; page++) {
$(I<span clas9=lIpage-number"></span>') .text(page + 1)
.clicklfunctionl) (
currentpage = page;
repaginate() ;
}) .appendTol$pager) .addClass('clickable');
}
Spager.insertBefore($table);
n.
}) ;
}) ; .slice(currentPage * numPerpage,
»; (currentPage
.show() ;
+ 1) * numPerpage)
Ahora tenemos un indicador del estado actual del pagínador, corno muestra la fi- }) ;
gura 7.16. •
Ahora en los lugares donde estábamos invocando repaginate (), podemos invocar:
$table.trigger('repaginate') ;
Podemos lanzar esta llamada en nuestro código de ordenación también. No hará nada
si la tabla no tiene un paginador, por lo que podemos mezclar las dos posibilidades de
uso de la forma deseada.
El código terminado
El código completo para ordenar y paginar en su totalidad se incluye a continuación:
jQuery.fn.alternateRowColors = function() {
$('tbody tr,odd', this)
.removeClass('even') .addClass('oddr) i
rows.sort(function(a, b) {
if (a.sortKey < b.sortKey) return -sortDirection; Modificar la apariencia de la tabla
if (a.sortKey > b.sortKey) return sortDirection;
return O;
}I; Hemos examinado algunas formas de ordenar las filas de datos en una tabla para
$.each(rows, function(index, row) ( proporcionar ayuda al usuario a la hora de encontrar la información deseada. Sin em-
$table.children('tbody') .append(row); bargo, a menudo es el caso que hay muchos datos que buscar después de llevar a cabo
row.sortKey = null¡
}) ;
cualquier ordenación o paginación. Podemos ayudar al usuario al manipular no sólo el
$table.find('th') .removeClass('sorted-asc') orden y cantidad de filas mostradas, sino la apariencia de aquéllas que se muestran.
.removeClass('sorted-desc') ;
if (sortDirection == 1) (
$header.addClass('sorted-asc') ; Resaltar filas
}
else (
$header.addClass('sorted-desc') ; Una forma práctica de dirigir la vista del usuario a las filas resaltadas es proporcio-
} nar una indicación visual sobre los datos que son importantes. Para examinar algunas
$table.find('td') .removeClass('sorted') estrategias de resaltado, necesitamos una tabla con la que trabajar. Esta vez, empezare-
.filter (',nth-child (' + (column + 1) + ')')
mos con una tabla de elementos de noticias. La tabla será un poco más complicada que
.addClass('sorted') ;
$table.alternateRowColors(); la última; incluirá algunas filas utilizadas como subencabezados, además de una fila de
$table.trigger('repaginate'); encabezado principal. La estructura HTML es como sigue:
}) ;
} <table>
}); <thead>
}) ; <tr>
}I; <th,Date</th,
$ (document) .ready(function() <th,Headline</th,
lmI 7. Manipulación de tabla Aprende jQuery 1.3 lmiI
<th,Author<!th,
<th,Topic<!th,
</tr>
JQuery, MIcrosoft, and NokIa John Reslg 1I11rd.party
<!thead,
<tbody' JQuery Conferenoo 2008 Agend\l ReyBango oonference
ctr> JQuery.comStte Red.slgn • JohnReslg announcement
cth colspan="4">2008c/th> Reglstratlon Open for JQuety Conrerence 2008 Kan Swedberg conferenoe
<!tr, jQuery VI 1.5.2 Paul Bakaus !elease
ctr> Jun 26 JQuery UI 1.5.1 Paul8akaus rele •••
<td,Sep 28<!td, Jun 26 JQuery carne 2008 Announced Rey Bango conference
<td,jQuery, Microsoft, and Nokia<!td,
Jun 9 JQueryUI v1.5 Roleased, Foous on Conslstent API and E1Iects Paul Bakaus
<td,John Resig<!td, rol ••••
Jun 4 JQuery 1.2.6: Evon'" 100% faster John Reslg
<td,third-party<!td, release
Mar 7 JQueryVI Wor1dwideSprinl: Man:ll14-15 Rlchartl Worth
<!tr, oonference
Feb8 JQuery 1.2.3: AlR, Namespaclng, and UI Alptta John Reslg !el••••
ctr> Jan 23 jQuery VI and bayo"": The JQuety·Ueray par1nershlp Paul Bakaus announcement
<td,Jan 15<!td, Jan 15 jQuery 12.2: 2nd Blrthday Present John ResIg roIease
<td,jQuery 1.2.2: 2nd Birthday Present<!td, 2007 •
<td,John Resig<!td, Dec8 JQuery Pluglns sHeupdated Mlke HosteUer announcement
ctd>releasec/td> Doc6 FIot, a now pIottlng plugln ror JQuery Bmdley Sapos plug.-ln
<!tr,
Nov2 Google Uslng JQuery Roy Bango Ihlrd-parly
<!tbody,
et.body» Figura 7.17. Resaltar friasen la tabla.
<tr>
cth colspan="4":;.2007</th>
<!tr, Como hemos visto, aplicar un diseño de franjas a las filas puede ser tan sencillo como
ctr:> un par de líneas para añadir clases a las filas pares e impares:
<td,Dec 8<!td,
<td,jQuery Plugins site updated<!td, $ (document) .ready(function() {
<td,Mike Hostetler<!td, $('table.striped tr:odd) .addClass('odd');
ctd>announcement</td> $('table.striped tr:even) .addClass('even');
<!tr, }) ;
ctr> Aunque este código funciona bien para estructuras sencillas de tabla, si introducimos
<td,Jan ll<!td,
filas adicionales que no queremos que se le apliquen las franjas alternas de color (como
<td,Selector Speeds<!td,
<td,John Resig<!td, las filas subencabezado que estamos utilizando para los años en nuestra tabla), el patrón
ctd>sourcec/td> básico par-impar no es suficiente. Si, por ejemplo, la fila 2006 se clasificara como even,
<!tr, las filas antes y después de ella serían odd, lo que probablemente no es deseable, como
<!tbody, muestra la figura 7.18.
<!table,
Observe el uso de múltiples secciones e t.body». Esto es código HTML válido para
agrupar conjuntos de filas juntas. Hemos situado los subencabezados de sección dentro
de estas agrupaciones, utilizando elementos «t.h». Con CSS básico añadido, esta tabla
se muestra como se ve en la figura 7.17.
Cada grupo de filas ahora empieza con una fila odd, pero las filas subencabezado se En un capítulo anterior presentamos el método. f il ter () para seleccionar elemen-
incluyen en este cálculo. Por lo tanto, podríamos no considerar las filas subencabezado tos de página de una forma muy flexible. Recuerde que. filter () puede tomar no
con la pseudo-clase :has () como muestra la figura 7.20. solamente una expresión selector, sino también una función filtro, y podemos escribir:
$ (document) .ready(function() {
$ ('table.striped tbody') .each(function() {
$ (this) .find ('tr: not ( :has (th) ) ,) .filter (function (index)
return (index % 6) < 3;
}) .addClass ( "odd ') ;
}) ;
}) ;
Cuando el usuario hace clie en una celda en la tercera columna, qltér~mos que el texto
de la celda se compare con el de la celda de la misma columna en cualquier otra fila, Si
coincide, la clase highl ight se alternará. Es decir, la clase se añadirá si no está ya ahí, y
Dee6 "plu~ln
se eliminará si lo está. De esta forma, podemos hacer clic en una celda de autor para elimi-
Nov2 Ihiril,party
Sep 17
nar el resaltado de la fila si ya se ha hecho clic en esa celda o una con el mismo autor.
ennouncement
Sep 10 John Reslg release l' $Idocument) .readyIfunction1) (
Sep 6 John Reslg conference var $authorCells = $ l' t ab l.e.striped t d .nth-child (3) ') ;
Aug24 Jchn Resig release $authorCells.clicklfunctionl) (
var authorName = $Ithis) ,textl);
$authorCells,eachlfunctionlindex) (
Figura 7.22. Estiloaplicadoa aqrupacionesde fila.
if lauthorName == $ (t.hí.s l. text ()) (
$Ithis) ,parentl) ,toggleClassl'highlight'); "l
}
Resaltar filas basado en interacción del usuario }) ;
}) ; t-
;~
Otra mejora visual que podemos aplicar a nuestra tabla de artículos de noticias es re- }) ;
saltar la fila basándose en la interacción del usuario, Aquí responderemos a hacer clic en
El código funciona bien en este punto, excepto cuando un usuario hace clie en dos
el nombre de un autor al resaltar todas las filas que tienen el mismo nombre en su celda
nombres de autor en sucesión. En lugar de cambiar las filas resaltadas de un autor al si-
Author. Podemos modificar la apariencia de 'estas filas resaltadas al añadir una ,clase:
guiente como podríamos esperar, acabamos con la clase highlight en ambos grupos
#content tr.highlight de filas. Para evitar este comportamiento, podemos añadir una sentencia else al códi-
background, #ff6; go, eliminado la clase highlight para cualquier fila que no tiene el mismo nombre de
I :i autor que en el que se ha hecho clie:
Es importante que asignemos a esta nueva clase highl ight especificidad adecuada, $Idocument) .readylfunctionl) (
de modo que el color de fondo anulará el de las clases even y odd. var $authorCells = $I'table.striped td,nth-child(3) ');
$authorCells.clicklfunctionl) (
Ahora necesitamos seleccionar la celda apropiada y anexarle comportamiento utili-
var authorName = $Ithis) ,textl);
. ¡
zando el método. click (): $authorCells.eachlfunctionlindex) (
~ :1 if (aut.horNarne == s Lth i.s) .textl)) (
$Idocument) .readylfunctionl)
$Ithis) .parentl) .toggleclassl'highlight');
~1 i var $authorCells = $I'table.striped td,nth-child(3) ');
:'. irr
u11'"
iIilL.:J!"J':;
-, I
mi 7. Manipulación de labia Aprende jQuery 1.3 BiD
else { if (authorName == $(this) .text(» (
$(this).parent().removeClass('highlight'); $(this) .parent() .toggleClass('highlight');
} }
}I ; ~lse (
}) ; $(this) .parent() .remqveClass('highlight'l;
}) ; }
Ahora cuando hacemos clic en Rey 8ango, por ejemplo, podemos ver todos sus ar- }) ;
}) ;
tículos mucho más fácilmente, como aparece en la figura 7.23. }) ;
~ Headllne .~11'I1Ua¡ IJ!FlI La clase c1ickab1e es un paso en la dirección correcta, pero todavía no le dice al
11;
usuario qué sucederá cuando se hace clic en la celda. Por lo que conoce el usuario de la
'-:-:¡f:::\~-
!le1l28 18<
página, ese clic podría fácilmente activar otro comportamiento, como enviar el usuario
ReyBlÍngo conference
a otra página. Alguna otra indicación de lo que va a pasar al hacer clic está en orden.
Las descripciones emergentes son una característica familiar de muchas aplicaciones
de software, incluidos los navegadores Web. Podemos abordar este desafío de usabili-
dad al mostrar una descripción emergente cuando el ratón pasa por encima de una de
las celdas Author. El texto de la descripción emergente puede describir a los usuarios
el efecto que tendrá su acción, con un mensaje como Highlightall articles by Rey 8ango
(Resaltar todos los artículos de Rey Bango). Este texto se encontrará en un <di V>, que
podemos anexar a -e body ». La variable $tooltip se utilizará en todo el script para
hacer referencia a este elemento recién creado:
var $tooltip = $(!<div id=UtooltiplI></div>t) .append'ro t ibody t ) i
Existen tres operaciones básicas que tendremos que llevar a cabo de forma repetida
en nuestra descripción emergente:
Nov2 Google Uslng JQuery ReyBango 1hIrd-party
,,..--.~'t, 1. Mostrar la descripción emergente cuando el ratón está sobre el elemento interac-
Figura 7.23. Resultados para un autor determinado. tivo, .
2. Ocultarlo cuando el ratón abandona el área.
Si luego hacemos clic en John Resig en cualquiera de las celdas, se eliminará el re-
saltado de las filas de Rey Bango y se añadirá a las de [ohn. 3. Volver a posicionar la descripción emergente cuando el ratón se mueve.
Escribiremos funciones para cada una de estas tareas primero, luego las uniremos a
Descripciones emergentes eventos de navegador utilizando jQuery.
Empecemos con positionToo1 tip, al que haremos referencia cuando el ratón se
Aunque el resaltado de una fila podría ser una característica de utilidad, hasta el mueve sobre cualquiera de las celdas Author:
momento no es aparente para el usuario que la característica exista. Podemos empezar
var positionTooltip = function (event)
a remediar esta situación al asignar a todas las celdas de autor una clase c1ickab1e, var tPosX = event.pageX¡
a las que hemos aplicado estilo para cambiar el cursor del ratón a un puntero cuando var tPosY = event.pageY + 20;
está dentro de la celda: $tooltip.css({top: tPosY, left: tposX});
};
$ (documentl .ready(function() {
var $authorCells = $('table.striped td:nth-child(3) '); Aquí utilizamos las propiedades pageX y pageY del objeto event para establecer las
$authorCells
.addClass('clickable')
posiciones top y 1eft de la descripción emergente. Cuando se invoca esta función en
.click(function() ( respuesta a un evento de ratón, como mousemove, event .pageX y event. pageY nos
var authorName = $(this) .text(); dará las coordenadas del cursor del ratón, por lo que tPosX y tPosY harán referencia
$authorCells.each(function(index) a una ubicación de pantalla 20 píxeles por debajo del cursor del ratón.
BiIiI 7. Manipulación de tabla Aprende jQuery 1.3 lIiiI
A continuación necesitamos escribir nuestra función showTool t ip ( ) r para situar .addClass('clickable')
la descripción emergente en la pantalla. .hover~showTooltip, hideTooltip)
.mousemove(positionTooltip)
var showTooltip = function(event) ( .click{function(event) (
var authorName = $(this) .text(); var authorName = $(this) .t~t();
$tooltip $authorCells.each(function(index) {
.text('Highlight all articles by , + authorName) if (authorName == $(this) .text(» (
.show() ; $(this) .parent() .toggleClass('highlight');
positionTooltip(event) ; }
}; el se (
$(this) .parent() .removeClass('highlight');
La función showTool t ip () es bastante sencilla. Completamos los contenidos de la }
descripción emergente utilizando una cadena creada desde los contenidos de la celda }) ;
y ahora que tenemos funciones para mostrar, ocultar, y posicionar la descripción Un problema con nuestra implementación actual es que la descripción emergente
emergente, podemos hacerles referencia en los lugares apropiados en nuestro código: continúa para sugerir hacer clic en una celda para resaltar los artículos incluso después
de que esos artículos se han resaltado, como muestra la figura 7.25.
$ (document) .ready(function() {
var $authorCells = $('table.striped td:nth·child(3) ');
var $tooltip = $('<div id="tooltip"></div>') .appendTo('body');
J\Jn 4 jQuery 1.2.6: Events 100% rnster Jo~ Reslg reIease
var positionTooltip = function(event)
~~rt.. jQl¡errYI Wort(M1d.(~ntMarch 14.1~., Rl~J'ftia~~~
'''~IHlghlight.Ü
var tPosX = event.pageX¡ a.rticlu by John Resig¡
Fab8 JQuery 12.3: AIR, Namespaclng, 800 UI Alpha Johi~eleiSStI'-_.J
var tPosY = event.pageY + 20;
$tooltip.css({top: tPosY, left: tPOSX});
}; Figura 7.25. La descripciónemergente sigue en pantalla.
var showTooltip = function(event) (
var authorName = $(this) .text();
Necesitamos una forma de cambiar el texto de la descripción emergente si la fila tiene
$tooltip
.text('Highlight all articles by , + authorName)
la clase highlight. Podemos realizar esto al situar una prueba condicional en la fun-
.show () ; ción showTool tip () para comprobar la presencia de la clase. Si el « t r » padre de la
positionTooltip(event) ; celda actual tiene la clase highlight, queremos utilizar la palabra Unhighlight en lugar
}; de Highlight cuando creamos la descripción emergente:
var hideTooltip = function()
$tooltip.hide() ; var action = Highlight i
I I
Esto elige de forma correcta el texto de la descripción emergente cuando el ratón entra 28 Ml~;;""3~!'í¡,l¡!~
Aug31 jQuery Contera""" 2008 Agenda
en una celda, pero también necesitamos volver a calcular la etiqueta en el momento en
~;~n~W;.~;
que se hace clic en el ratón. Para ello, necesitamos invocar la función showTooltip ()
.~~~)5, R~l.k •.!l.i!';c:ip,'!Q for)d~~,9'J'hf~'~º~
dentro del manejador de evento click: j~114' ''J.ri~~~.y, i5,'2 ~:r ''''~7':\i>~:'_·
;~'.
$authorCells.each(function(index) {
.attr('alt', collapseText)
if (authorName == $(this) .text()) (
.prependTo($section.find('th')) ;
$(this) .parent() .toggleClass('highlight');
}) ;
} });
el se (
$(this) .parent() .removeClass('highlight');
}
Hemos almacenado las ubicaciones de los íconos, así como sus representaciones
}) ; textuales alternativas, en variables al principio de la función. Esto nos permite hacerles
ahowTooltip.call(thia, event); referencia fácilmente, y proporciona una forma sencilla de realizar cambios si fuera ne-
}l;
cesario. Hemos realizado la incorporación de imagen en un bucle . each () , lo que de-
}l;
mostrará ser adecuado más adelante cuando necesitemos hacer referencia al elemento
Al utilizarla función JavaScript call (), podemos invocar showTooltip () como si «t body» de nuevo¡ lo tendremos disponible por medio de la variable $section que
se estuviera ejecutando dentro del ámbito del manejador el ick de la celda con el nombre hemos definido aquí.
del autor. Necesitamos hacer esto de modo que la palabra clave this haga referencia al A continuación, haremos que los iconos activen el contraer y expandir las filas. La
objeto correcto (esta celda de tabla) durante la ejecución de showTooltip () . incorporación de una clase clickable proporciona el feedback de usuario necesario,
lJIlI 7. Manipulación de tabla Aprende jQuery 1.3 BII
y una clase en el elemento « t body» nos ayuda a mantener registro de si las filas están Los artículos del 2007 no se han eliminado; simplemente se han ocultado hasta que
en realidad visibles o no. hacemos clic en el icono para expandir el apartado que ahora aparece en esa fila.
$ (document) .ready(function() {
var collapselcon = ' .. fimagesfbullet_toggle_minus.png';
var collapseText = 'Collapse this section'¡
var expandlcon = ' .. fimagesfbullet_toggle-plus.png';
var expandText = tExpand this section'¡ Las filas de tabla presentan ciertos obstáculos a la animación, ya que los navegadores
$ ('table. collapsible t body") .each (function () utilizan valores diferentes (t ab 1e - row y block) para su propiedad di sp 1ay visible,
var $section = $(this); Los métodos . hide () y . show (), sin animación, se pueden utilizar siempre con
$('<img f,') .attr('src', collapselcon)
.attr('alt', collapseText)
filas de tabla. Si se desea animación, . fadeln () y . fadeOut () se pueden utilizan
.prependTo($section.find('th')) también .
.addClass('clickable')
.click(function() {
if ($section.is(' .collapsed'))
$section.removeClass('col1apsed')
.find('tr:not(:has(th)) ') .fadeln('fast');
Filtrado
$(this) .attr('src', collapselcon)
.attr('alt', collapseText);
Anteriormente hemos examinado ordenar y paginar como técnicas para ayudar a los
}
else {
usuarios a centrarse en partes relevantes de los datos de una tabla. Vemos que ambos
$section.addClass('collapsed') se pueden implementar con tecnología del lado del servidor, o con JavaScript. El filtra-
.find('tr:not(:has(th)) ') .fadeOut('fast'); do completa este arsenal de estrategias de organización de datos. Al mostrar al usuario
$(this) .attr('src', expandlcon)
solamente las filas de tabla que coinciden con un criterio dado, podemos eliminar dis-
.attr('alt', expandText);
tracciones innecesarias.
}
}) ; Ya hemos visto cómo realizar un tipo de filtro: resaltar un conjunto de filas. Ahora
». ampliaremos esta idea para ocultar filas que no coinciden con el filtro.
}) ;
Podemos empezar creando un lugar donde situar nuestros vínculos de filtrado. En una
estrategia típica de mejora progresiva, insertamos estos controles utilizando JavaScript
Cuando ocurre un clic, realizamos lo siguiente:
de modo que las personas sin programación disponible no ven las opciones:
1. Añadir o eliminar la clase collapsed en el elemento e t.body», para mantener
$ (document) .ready(function() {
registro del estado actual de la sección de tabla. $('table.filterable') .each(function()
var $table = $(this);
2. Localizar todas las filas en el apartado que no contiene encabezados, y mostrar
u ocultadas utilizando una transición de desvanecer. $table.find('th') .each(function(column)
if ($ (this) .is (' .filter-column')) {
3. Alternar el estado actual del icono, cambiando sus atributos src y al t para que
var $filters = $('cdiv class="filters"></div>');
refleje la acción que se activará ahora cuando se hace clie. $ ('<h3,<fh3,')
.text('Filter by , + $(this) .text() + ': ')
Con este código en su lugar, hacer clic en el icono para contraer este apartado junto .appendTo($filters) ;
a 2007 hace que la tabla se parezca a la figura 7.27. $filters. insertBefore ($table) ;
}) ;
}) ;
}) ;
Headllne
Fllter by Top.lc:
Figura 7.28. Preparar un filtro.
~
release
Opciones de filtro
Ahora podemos pasar a implementar un filtro. Para empezar, añadiremos filtros
para un par de temas conocidos. El código para esto es bastante similar al ejemplo de
resaltar autor anterior:
Figura 7.29. Incorporarfiltrospara temas conocidos.
$ (document) .ready(function() (
$('table.filterable') .each(function()
var $table = $(this);
Recopilar opciones de filtro del contenido
$table.find('th') .each(function(column)
if ($(this).is('.filter-column')) {
var $filters = $('cdiv class=l'filters"></div>');
Ahora necesitamos ampliar las opciones de filtro para abordar el rango de temas
$ (, <h3></h3>') disponibles en la tabla. En lugar de incorporar en el código todos los temas, podemos
.text ( ,Fil ter by , + $ (thi s) .text () + ':') recopilarlos del texto que se ha incorporado en la tabla. Podemos cambiar la definición
.appendTo($filters) ; de keywords para que sea:
var keywords = ['conference', 'release'] i var keywords = {};
$.each(keywords, function(index, keyword) { $table.find('td:nth-child(' + (column + 1) + ') ')
$('<div class=tlfilter"></div>') ,text(keyword) .each(function() (
.bind('click', {key: keyword}, function(event) ( keywords[$(this) .text()] = $(this) .text();
$('tr:not(:has(th))', $table) .each(function() { });
var value = $('td', this) .eq(column) .text();
if (value == event.data['key']) { Este código se basa en dos trucos:
$ (thi s) .show () ;
} 1. Al utilizar un mapa en lugar de una tabla para albergar las palabras clave a medida
else (
$ (this) .hide () ; que se encuentran, eliminamos duplicados automáticamente; cada clave puede
} tener solamente un valor, y las claves son siempre únicas.
}) ;
$(this) .addClass('active') 2. La función $ . each () de jQuery nos permite trabajar sobre tablas y mapas de
.siblings() .removeClass(lactive'); forma idéntica, por lo que el código siguiente no tiene que cambiar .
}) .addClass('clickable') .appendTo($filters) ;
}); Ahora tenemos un complemento completo de opciones de filtro, como muestra la
$filters.insertBefore($table); figura 7.30.
}
}) ;
}l;
}) ;
Invertir los filtros
Empezando con una tabla estática de palabras clave por las que filtrar, pasamos en Para un mayor nivel de detalle, necesitamos una forma de regresar a la lista com-
bucle por ellas y creamos un vínculo de filtrado para cada una. Como con en el ejemplo pleta después de haberla filtrado. Añadir una opción para todos los temas es bastante
de paginado, necesitamos utilizar el parámetro data de . bind () para evitar problemas sencillo:
l'
mi 7. Manipulación de tabla
,, Aprende jQuery 1.3 lID
$('<div class=lIfilter">all</div>') .click(function()
$table.find('tbody tr') .show(); Alternar color de fila
$(this) .addClass('active')
.siblings() .removeClass('active'); El aplicar, color de fila alterno avanzado que hemos incorporado anteriormente se
}) .addClass('clickable active') .appendTo($filters);
confunde por nuestros nuevos filtroszl'uesto que no se vuelve a aplicar el color alterno
de fila después de llevar a cabo un filtro, las filas mantienen su color como si las filas
Filler by Tople: filtradas siguieran presentes.
Para tener en cuenta las filas filtradas, el código de filas de colores alternos necesita
oonference
poder encontrarlas. La pseudo-clase jQuery : visible puede ayudamos al recopilar el
announoement conjunto correcto de filas a aplicar color alterno.
reieeee Mientras realizamos este cambio, podemos preparar nuestro código de filas alter-
plug-ln nas que se va invocar desde otros lugares al crear un tipo de evento personalizado para
standanls
ello, como hicimos cuando hicimos que ordenación y paginación funcionaran conjun-
documenlalion
MOIIal
tamente.
mlscellaneous
$ (document) .ready(function() (
souroe
$('table.striped') .bind('stripe', function() {
$('tbody', this) .each(function() (
Figura 7.30. Ampliar las opciones de filtro. $(this) .find('tr:visible:not(:has(th» ')
.removeClass('odd') .addClass('even')
.filter(function(index) {
Esto nos proporciona un vínculo ali que simplemente muestra todas las filas de la return (index % 6) < 3;
tabla, como se ve en la figura 7.31. Este nuevo vínculo se marca como act i ve para em-. }) .removeClass('even') .addClass('odd');
pezar. ».
}) .trigger('stripe');
}),
Headllne Filler by Tople:
En nuestro código de filtrado, ahora podemos invocar $table .trigger ( stripe' ) I
thlrd-party cada vez que .ocurre una operación de filtrado. Con el nuevo manejador de evento y sus
oonference activadores en su lugar, la operación de filtrado respecta el color alterno de fila, como
announoement
se ve en la figura 7.32.
nlleasa
plug~n
standards
documentstl,on ~
11; 2008
Headllne •• ea Filler by Tople:
tutotial all
mlscenaneous thlrd-party
source conference
announcement
M &¡:;:;¿¡::.¡¡¡¡:;:¡~_>
Figura 7.31. Posibilidad de regresar a la lista completa. ~~il
pI~n
',Jün~4 ,l ~.~ standards.
documentaBon
F~8
Interactuar con otro código Morlal
mlscellaneous
Hemos aprendido con nuestro código de ordenar y paginar que no podemos tratar sourca
las diferentes características que escribimos como islas. Los comportamientos que crea-
mos pueden interactuar en formas algunas veces sorprendentes; por esta razón, merece
la pena volver a visitar nuestros esfuerzos anteriores para examinar cómo coexisten con
las nuevas posibilidades de filtrado que hemos añadido. Figura 7.32. Tener en cuenta el color de fila alterno.
EDil 7. Manipulación de tabla ~- Aprende jQuery 1.3 BfI
r.
Expandir y contraer var showTooltip = function(event) {
var aut'horName = $ (this) .text () ,
El comportamiento de expandir y contraer añadido anteriormente también entra en var ac~ion = 'Highlight' i
conflictocon nuestros filtros. Si una sección se contrae y se elige un nuevo filtro, entonces if ($ (·this).parent () .is (, .hi9lVight'»
action = lUnhighlight';
los elementos coincidentes se muestran, incluso si están en la sección contraída. Por el }
contrario, si la tabla se filtra y se expande una sección, entonces todos los elementos en $tooltip
la sección expandida se muestran con independencia de si coinciden o no con el filtro. .text(action + ' all articles by I + authorName)
Una forma de abordar la última situación es cambiar la forma en que mostramos y .show() ,
positionTooltip(event) ,
ocultamos filas. Si utilizamos una clase para indicar que se debería ocultar una fila, no },
necesitamos invocar explícitamente. hide () y . show ( ) . Al reemplazar. hide () con var hideTooltip = function()
.addClass (' filtered') y. show() con. removeClass ( ' f i Lt.er-ad ' ) .junto con $tooltip.hide() ,
};
una regla CSSpara la clase, podemos llevar a cabo el ocultar y mostrar, pero trabajamos
mejor con el código que se contrae. Si se elimina la clase y la fila se contrae, la fila no se
$authorCells
mostrará sin darse cuenta. .addClass('clickable')
Introducir esta nueva clase filtered también nos ayuda con el problema contra- .hover(showTooltip, hideTooltip)
rio. Podemos comprobar la presencia de filtered cuando llevamos a cabo una am- .mousemove(positionTooltip)
.click(function(event) (
pliación de sección, saltando estas filas en lugar de mostrándolas. Comp.robar esta clase
var authorName = $(this) .text(),
es una sencilla cuestión de añadir: not ( . filtered) a la expresión selector utilizada $authorCells.each(function(index) {
durante la expansión. if (authorName == $(this) .text(» (
Ahora nuestras características funcionan bien, cada una pudiendo ocultar y mostrar $(this) .parent() .toggleclass('highlight'),
las filas independientemente. }
else (
$(this) .parent() .removeClass('highlight'),
}
El código terminado }),
showTooltip.call(this, event),
}),
Nuestra segundo ejemplo ha demostrado alternar color de fila, resaltar, descripcio- }),
nes emergentes, contraer/expandir, y filtrado. Tomadas juntas, el código JavaScript
para esta página es: $ (document) .ready(function() {
var collapselcon = '../images/bullet_toggle_minus.png',
$ (document) .ready(function() ( var collapseText = 'Collapse this section' i
$('table.striped') .bind('stripe', function() { var expandlcon = '../images/bullet_toggle-plus.png',
$('tbody', this) .each(function() ( var expandText = 'Expand this section'¡
$(this) .find('tr:visible:not(:has(th» ') $('table.collapsible tbody') .each(function()
.removeClass(rodd') .addClass('even') var $section = $(this),
.filter(function(index) { $('<img />') .attr('src', collapselcon)
return (index % 6) < 3, .attr('alt', collapseText)
}) .removeClass ('even' l addClass ('odd' ) , .prependTo($section.find('th'»
j), .addClass('clickable')
}) .trigger('stripe'), .click(function() {
}) , if ($section.is(' .collapsed'» (
$section.removeClass('collapsed')
$ (document) .ready(function() { .find('tr:not(:has(th» :not(.filtered) ')
var $authorCells = $('table.striped td:nth-child(3) '), .fadeln('fast') ,
var $tooltip = $('<div id="tooltip"></div>') .appendTo('body'), $(this) .attr('src', collapselcon)
.attr('alt', collapseText),
var positionTooltip = function(event)
var tPosX = event.pageX¡ el se (
var tPosY, = event ,pageY + 20 i $section.addClass('collapsed')
$tooltip.css({top: tPosY, left: tPosX}), .find('tr:not(:has(th» ')
mi 7. Manipulación de tabla
r
I
Aprende jQuery 1.3 lJJlI
.fadeOut('fast', function() (
$(this).css('display', "norie L,
}) ;
t
Resumen
$(this) .attr('src', expandlcon)
.attr('alt', expandText);
} En este capítulo, hemos explorado ,illgunas de las formas de descomponer y analizar
$section.parent() .trigger('stripe'); tablas en nuestros sitios, reconfiguránClolas en contenedores funcionales para nuestros
}) ; datos. Hemos tra tado ordenar datos en tablas, utilizando diferentes tipos de datos (pala-
}) ;
}) ;
bras, números, fechas) junto con paginar tablas en bloques que se vean fácilmente. Hemos
aprendido sofisticadas técnicas de alternar el color de filas y descripciones emergentes
$ (document) .ready(function() ( gracias a JavaScript. También hemos examinado cómo expandir y contraer contenido al
$('table.filterable') .each(function() .igual que filtrar y resaltar filas que coinciden con los criterios facilitados.
var $table = $(this); ,r Hemos incluso tratado brevemente algunos temas bastante avanzados, como orde-
$table.find('th') .each(function(column)
nar y paginar con código del lado del servidor y técnicas AJAX, calcular dinárnicamente
if ($(this).is('.filter-column')) { coordenadas de página para elementos, y escribir un plug-in jQuery.
var $filters = $('cdiv class="filterslI></div>') i Como hemos visto, lastablas HTML engloban una gran cantidad de sutilizas y com-
$ (, <h3></h3>')
plejidad en un pequeño paquete. Afortunadamente, jQuery puede ayudamos a domar
.text ('Filter by , + $ (this) .text () + ':')
.appendTo($filters);
fácilmente a estas criaturas, permitiendo que salga a la superficie el verdadero potencial
de los datos tabulares.
$('cdiv class="filter">allc/div>') ,click(function()
$table. find ('tbody t r removeClass ('filtered')
i ) • ;
$(this) .addClass('active')
.siblings() .removeClass('active') i
$table.trigger('stripe') ;
}) .addClass('clickable active') .appendTo($filters);
}) ;
}) ;
/
¡
~
.. .'\
Casi todo sitio Web que requiera información por parte del usuario empleará un
formulario en una forma u otra. A lo largo de la vida de Internet, los formularios han
desempeñado el rol de mula de carga, llevando información desde el usuario final de
8 vuelta al editor del sitio Web, de forma fiable, pero con muy poca gracia o estilo. Quizá
esta ausencia de estilo ha sido causada por el repetido y arduo viaje al servidor y vuel-
ta; o quizá tenía que ver con los elementos inflexibles con los que el formulario tenía
que trabajar y su falta de deseo de seguir la última moda. Cualquiera que sea la razón,
no ha sido hasta recientemente, con el resurgimiento de la programación del lado del
Formularios
- "
cliente, que los formularios han encontrado nuevo vigor, propósito y estilo. En este ca-
pítulo, examinaremos formas en las que podemos infundir nueva vida a los formularios.
Mejoraremos su estilo, crearemos rutinas de validación, los utilizaremos para cálculos,
y enviaremos sus resultados al servidor mientras nadie mira.
19 by Phcne
I _J (requlnld when co"""pondlng checl<box checked) " </lb
<li>
9byFex
I ] (requlnld when correspondlng checkboX checkea) <label for=lIby-phonell>
<input type="checkbox" name="by-contact-type"
value= 11 Phone" id=lIby-phone" />
Figura 8.1. Conjunto de campos de formulario. by Phone
</label>
Aunque ciertamente parece funcional y contiene numerosa información para guiar <input class="conditional" type="textlt name=lIphone"
id="phone" />
al usuario por cada uno de los campos, definitivamente podría aceptar cierta mejora. <span> (required when corresponding checkbox
Mejoraremos progresivamente este grupo de tres formas: checkedl</span>
</lb
1. Modificar el DOM para permitir aplicar estilo flexible a' degend>. <-li>
<label for="by-fax">
2. Cambiar el mensaje de campo obligatorio (required) por un asterisco (") y el <input type="checkboxlI name=lIby-contact-type"
mensaje de campo especial (required only when the corresponding checkbox is value=IIFax" id="by-fax" />
checked, u obligatorio sólo cuando la casilla de verificación correspondiente está by Fax
</label>
seleccionada) a un asterisco doble ("").Poner en negrita la etiqueta para cada campo <input class="conditional" type=lItext" name="fax"
obligatorio y situar una clave en la parte superior del formulario que explique lo id=lIfaxlI />
que significa el asterisco sencillo y doble. <span>(required when corresponding checkbox
checkedl</span>
3. Ocultar la entrada de texto correspondiente de cada casilla de verificación al car- </li>
garse la página, y luego alternarlo, visible y oculto, cuando el usuario selecciona </ul>
</li>
o deselecciona las casillas. </01>
</fieldset>
Empezamos con el HTML de <fieldset>:
<fieldset> Una cosa a destacar aquí es que cada elemento de pares de elementos se considera
<legend>personal Info</legend>
un elemento de lista (db). Todos los elementos se sitúan dentro de una lista ordena-
<01>
<li> da «01», y las casillas de verificación (junto con sus campos de texto) se sitúan den-
c::label for="first-nametl>First Namec::/label> tro de una lista sin ordenar anidada (eu l »), Además, utilizamos el elemento «Labe I.»
<input c Las s e vr'equd r'ed" type=lItext" name="first-name 11
para indicar el nombre de cada campo. Para los campos de texto, « Labe L» precede a
id=lIfirst-name" />
<input>; para casillas de verificación, engloba <input>. Aunque no existe estructura
<span> (required) </span>
mil 8. Formularios con funciones
Aprende jQuery 1.3 ••
La leyenda Situar un <span> dentro de <legend> tiene al menos dos ventajas frente a reem-
plazar <Le qerid» con un <h3 >: mantiene el significado semántico de <legend> para
La leyenda del formulario es un elemento muy difícil de aplicar estilo con CSS. lectores de pantalla con soporte ]avaScript y requiere menos trabajo en la parte del script.
Las inconsistencias de navegador y las limitaciones de posicionamiento hacen que tra- La desventaja es que hace que el estilo del encabezado sea un poco más difícil de lograr.
bajar con ello sea un ejercicio de frustración. Aún así, si nos preocupa utilizar elemen- Al menos, tenemos que establecer la propiedad position de <fieldset> y e spans.,
tos de página significativos, y bien estructurados, la leyenda es una elección atractiva, así como el padding-top del « f e Lds et;» y el width de <span>:
í
si no visualmente llamativa, para mostrar un título en el <fieldset > de nuestro for- fieldset {
mulario. position: relative¡
padding-top: 1.5em;
Solos con HTML y CSS, estamos obligados a comprometer la elección de código se-
mántico o diseño flexible. Sin embargo, podemos cambiar el HTML cuando se carga la
página, convirtiendo cada <Leqerid » en un <h3> para las personas qllÍevisualizan la legend span (
página, aunque las máquinas que leen la página, y aquellos con ]avaScript disponible, position: absolute¡
width: 100%;
siguen viendo <legend>. Esto se puede realizar de forma sencilla utilizando el método
. replacewi th () de jQuery:
$ (document) .ready(function() ( Si elegimos reemplazar los elementos <Leqerid» del formulario o insertar un <span s
$('legend') .each(function(index) ( en ellos, tienen suficiente estilo para nuestros fines, es el momento de limpiar los men-
$(this) .replaceWith('<h3>' + $(this) .text() + '</h3>'); sajes de campo obligatorio.
».
}l;
Observe aquí que no podemos basamos en la iteración implícita de jQuery. Con cada
Mensajes de campo obligatorio
elemento que reemplazamos, necesitamos insertar los contenidos de texto únicos de ese
En nuestro formulario de contacto, los campos obligatorios tienen class=" required"
elemento. Para esto nos basamos en el método. each ( ) , que nos permite dirigimos al
para permitir aplicar estilo al igual que respuesta a la entrada del usuario; los campos de
texto determinado con $ (thi s) .
entrada de datos para cada tipo de contacto tienen aplicado class=" condi t ona l''.
Ahora, cuando aplicamos un fondo azul y color de texto blanco a <h3> en la hoja
í
Vamos a utilizar estas clases para cambiar las instrucciones impresas dentro del parén-
de estilo, el primer conjunto de campos del formulario se parece á lo'que muestra la fi-
tesis a la derecha de cada entrada de datos.
gura 8.2.
Empezamos por establecer variables para requiredFlag y conditionalFlag, y
luego completamos el elemento «span s junto a cada campo obligatorio y condicional
Personal Info con el texto almacenado en esas variables:
FlmI Name [ ~ (mqulred) $ (document) .ready(function() {
Las! Name I I(mqulred) var requiredFlag = ' * ';
Haw would yoo IIko lo be con1Dclec!? (oooose SI laest ene method) var conditionalFlag = ** I l.
.filter('.conditional')
.next('span') .text(conditionalFlag);
Figura 8.2. Aplicar estilo al primer conjunto de campos de formulario. }) ;
DI 8. Formularios con funciones Aprende jQuery 1.3 mi
$ (document) .ready(function()
Utilizar . end () nos permite ampliar la cadena de métodos de modo que continuamos
var requiredFlag = ' * ';
trabajando con el mismo conjunto de elementos y mantenemos la creación de objetos al var conditlonalFlag = ' .* 'i
mínimo. Todo método . end () devuelve la selección un paso hacia atrás, volviendo el
conjunto coincidente de elementos a lo que era antes del último método transversal Aquí var requiledKey = $('input.required:first')
.next('span .text(); ~,
utilizamos dos en una fila: el primer . end () revierte el conjunto coincidente a . f i 1-
l)
var conditionalFlag
Las dos primeras líneas adicionales declaran variables (requiredKey y condi-
$ ( 'form :input' )
tiona1Key) para almacenar el texto de cada tipo de campo. Las segundas dos líneas
.filter(' .required') modifican el texto en esas variables, concatenando cada indicador, y su texto respectivo,
.next('span') .text(requiredFlag) .end()
" menos el paréntesis. Quizá la expresión regular, junto con su método . rep1ace () ,
.prev('label').addClass('req-label').end()
merece una explicación más detallada .
.end ()
.filter(' .conditional')
.next('span') .text(conditionalFlag); Un paréntesis sobre la expresión regular
».
La expresión regular se encuentra dentro de dos barras inclinadas y se parece a esto:
Una cadena tan larga de métodos puede ser difícil de seguir, por lo que un salto de
/ A \ +) \) $ /. El primer carácter,
( ( • indica que lo que sigue necesita aparecer al princi-
A,
línea consistente y un patrón de sangrando es esencial. El conjunto de campos con el
pio de la cadena. Está seguido por dos caracteres, \ {,que buscan un paréntesis de inicio.
texto modificado y la clase añadida ahora se parece a la figura 8.3.
La barra invertida escapa el carácter que sigue, diciéndole al analizador de la expresión
regular que lo trate literalmente. Esto es necesario porque los paréntesis están entre los
Personal Info caracteres que tienen significado especial en expresiones regulares, como veremos a
FI •• t Horno c=------n- _n-=.Jo continuación. Los siguientes cuatro caracteres, (. + ) , buscan uno o más caracteres (re-
LatNlIML :::oJo presentado por +) de cualquier tipo dentro de la misma línea (representado por.) y los
How would yoo Ilke ID be ccn1acIDd7 (clloose al leas! one mothod) sitúan en un grupo por el uso de los paréntesis. Los últimos tres caracteres, \) s, buscan
E!I by E-Mail I 1- un paréntesis de cierre al final de la cadena. Por lo tanto, todo junto, la expresión regular
está seleccionado un paréntesis de inicio, seguido por un grupo de caracteres, y termina
El byPhono 1 1- con un paréntesis de cierre. El método. rep1ace () busca dentro de un contexto de-
El by Fax 1 1- terminado una cadena representada por una expresión regular y lo reemplaza con otra
cadena. La sintaxis se parece a esto:
Figura 8.3. Conjuntode campos mejorado. 'context'.replace(/regular-expression/, 'replacement')
No del todo mal. Los mensajes de campo obligatorio y condicional no eran en reali- Las cadenas de contexto de nuestros dos métodos. replace () son las variables re-
dad tan malos después de todo; simplemente eran demasiado repetitivos. Tomemos la quiredKey y condi tiona1Key. Ya hemos examinado la parte de expresión regular de
primera instancia de cada mensaje y mostrémoslo por encima del formulario junto a la esto, contenida dentro de dos barras inclinadas. Una coma separa la expresión regular
indicación que estamos utilizando para simbolizarlo. y la cadena de sustitución, que en nuestros dos casos es '$1 El marcador de posición
I •
Antes de completar los elementos < span» que albergan los mensajes con sus indica- $1 representa el primer grupo en la expresión regular. Nuevamente, puesto que nues-
ciones respectivas, necesitamos almacenar los mensajes iniciales en un par de variables. tra expresión regular tiene un grupo de uno o más caracteres, con un paréntesis en cada
Luego podemos quitar los paréntesis al utilizar una expresión regular: lado, la cadena de sustitución estará todo dentro de, y no incluidos, los paréntesis.
T¡_
, .
$('<p></p>')
I!i! by Phone I J"
.addClass('field-keys') lB by Fax L__ ==:::J"
.append(requiredKey + '<br />')
.append(conditionalKey)
.'
.insertBefore('#contact'l; Figura 8.4. Mejorar la leyenda de los campos .
}) ;
5. Insertar el párrafo y todo lo que hemos anexado dentro de él delante del formulario 8byPhone
de contacto. 8byFax
Cuando se utiliza. append () con una cadena HTML, como hacemos aquí, tenemos
Figura 8.5. Interfaz modernizada.
que tener cuidado que cualquier carácter HTML especial se escape adecuadamente. En
este caso, el método . text () que hemos utilizado cuando declaramos las variables ha
Para hacer que aparezcan los campos de entrada de texto y las indicaciones, anexa-
hecho esto por nosotros. Cuando definimos algunos estilos para. field-keys en la
mos el método. click () a cada casilla de verificación. Haremos esto dentro del con-
hoja de estilo, el resultado se parece a lo que aparece en la figura 8.4.
texto de cada entrada de texto condicional de modo que podamos establecer un par de
Nuestro trabajo jQuery para el primer conjunto de campos está casi completo.
variables para reutilización:
$ (document) .ready(function() (
Campos mostrados condicionalmente $('input.conditional') .next('span') .andSelf() .hide()
.end() .end()
.each(function() {
Mejoremos aún más el grupo de campos que piden a los visitantes cómo les gusta-
var $thisInput = $(this);
ría que se les contactara. Puesto que las entradas de texto se tienen que incorporar so- var $thisFlag = $thisInput.next('span');
lamente si sus casillas de verificación correspondientes están seleccionadas, podemos $thisInput.prev('label').find(':checkbox')
&IiI 8. Formularios con funciones Aprende jQuery 1.3 &11
.click(function() { $thislnput.show() ;
// código continúa $~hisFlag.show() ;
}) ; $(this) .parent('label') .addClass('req-label');
}) ; e11'e {
}) ; $thislnput. hide () ; . .,
$thisFlag.hide();
Aquí nuevamente hacemos uso de dos métodos . end ( ) , esta vez de modo que po- $(this) .parent('labe1')
,removeClass('req-label');
damos anexar el método. each () al selector $ ( , input. condi tional ' ) original.
}
Ahora tenemos una variable para la entrada de texto actual y el indicador actual. }) ;
Cuando el usuario hace clic en la casilla de verificación, vemos si está seleccionada; }) ;
var $thisFlag = $thislnput.next('span'); Antes de añadir validación a cualquier formulario con jQuery, necesitamos recordar
$thislnput. prev ('label ') .find (, .checkbox ') una regla importante: la validación del lado del cliente no es sustituto para validación
"
.click(function() { del lado del servidor.
if (thia.checked) {
Nuevamente, no podemos basamos en que los usuarios tengan JavaScript activado.
$thislnput.ahow();
$thisFlag.show(); Si realmente necesitamos que se incorporen ciertos campos, o que se incorporen en un
$(this) .parent('label') .addClass('req-labe1'); formato determinado, JavaScript sólo no puede garantizar el resultado que demandamos.
} Algunos usuarios prefieren no activar JavaScript, algunos dispositivos simplemente no
}) ;
lo soportan, y algunos usuarios pueden de manera intencionada enviar datos maliciosos
}) ;
al eludir las restricciones JavaScript.
». ¿Por qué entonces preocuparse por implementar validación con jQuery? La validación
Para comprobar si hay una casilla seleccionada aquí, se prefiere thi s . checked por- de formulario del lado del cliente utilizando jQuery puede ofrecer una ventaja frente
que tenemos acceso directo al nodo DOM vía la palabra clave this. Cuando el nodo a la validación del lado del servidor: feedback inmediato. El código del lado del servi-
DOM no está accesible, podemos utilizar $ ( , selector' ) . is ( , : checked ") en su dor, tanto si es ASP, PHP, o cualquier otro acrónimo, necesita que la página se vuelva
lugar, ya que. is () devuelve un booleano (true o false). a cargar para surtir efecto (a menos que se acceda asíncronamente, por supuesto, que
Nos quedan por hacer dos cosas: en cualquier caso requiere JavaScript). Con jQuery, podemos sacar provecho de la rápi-
da respuesta del lado del cliente al aplicar validación a cada campo obligatorio cuando
1. Aseguramos de que las casillas de verificación no están seleccionadas cuando la pierde foco (en blur), o cuando se pulsa una tecla (en keyup).
página se carga inicialmente, ya que algunos navegadores mantendrán el estado
de elementos de formulario al refrescarse la página.
Campos obligatorios
2. Añadir una condición else que oculta los elementos condicionales y elimina la
clase req-label cuando la casilla de verificación no está seleccionada. Para nuestro formulario de contacto, comprobaremos la presencia de la clase requi-
red en cada entrada de datos cuando el usuario entra o sale de cada entrada de datos.
$ (document) .ready(function() (
$ ('input.conditiona1') .next('span') .andSelf() .hide()
Antes de empezar con este código, sin embargo, deberíamos regresar a nuestros campos
.end() .end() de texto condicionales. Para simplificar nuestra rutina de validación, podemos añadir la
.each(function() ( clase required al <input> cuando se muestra, y eliminar la clase cuando el <input>
var $thislnput = $(this); posteriormente se oculta. Esta parte del código ahora se parece a esto:
var $thisF1ag = $thislnput.next('span');
$thislnput. prev (,label' ) .find (,,checkbox' ) $thislnput.prev('label') .find(' ,checkbox')
.attr('checked', falae) .attr('checked', false)
.click(function() { .click(function() {
if (this.checked) ( if (this.checked) {
&!I 8. Formularios con funciones Aprende jQuery 1.3 &El
$thislnput.show() .addClass('required'); $('<span></span>')
$thisFlag.show() , .addClass('error-message')
$(this) .parent('label') .addClass('req-label'), .text(errorMessage}
else ( r . appendTo($listltem) ;
$thislnput.hide() .removaClass('required'); ·$listltem.addClass('war~ng')'
$thisFlag.hidel) ,
$(this) .parentl'label') .removeClassl'req-label'), }
} }) ;
j), }) ;
Con las clases required en su lugar, estamos listos para responder cuando el usua- Nuestro código funciona bien la primera vez que el usuario deja un campo en blanco;
rio deja uno de estos campos vacíos. Se situará un mensaje detrás del indicador obliga- .' sin embargo, dos problemas con el código son evidentes cuando el usuario entra y deja
torio, y el elemento <1i > del campo recibirá estilos para alertar al usuario por medio ( más tarde el campo, como se muestra en la figura 8.6.
de class="warning":
Personallnfo
$ (document) .ready(function() {
$('form :input') .blur(function() (
Flrst Nama ¡Mar" ---J .
if ($(this) .hasClass('required'» ( Lost Nom. ¡Tum.,
I
J-I
var $listltem = $(this) .parents('li:first'), How wouId )'<lOIIke lo be oon1BCI8d? (choose alleast CM melllod)
if Ithis. value == ,,) (
var errorMessage = 'This is a required field'¡
" • b)'E •••• U [ =::J - ThI.1s a raqulred IIeId, when Its lBIated ehed<box Is chedcec1ThI •
l. a requlred IIeId, whon Ito mlated ehed<box Is _
$ l'<span></span>')
.addClass('error-message') I!!! byPhcna
.textlerrorMessage)
.appendTo($listltem) ,
I!!! by Fax
$listltem.addClass('warning') ;
Figura 8.6. Dejar un campo en blanco.
}
j),
}) ; Si el campo permanece en blanco, el mensaje de error se repite tantas veces como el
usuario abandone el campo. Si se incorpora texto en el campo, c Las s e "warning" no
El código tiene dos sentencias if para cada entrada de formulario en bl ur: la prime- se elimina. .
ra comprueba la clase required, y la segunda comprueba la presencia de una cadena Obviamente, solamente queremos un mensaje por campo, y queremos que el men-
vacía. Si ambas condiciones se cumplen, construimos un mensaje de error, lo situamos saje se elimine si el usuario soluciona el error. Podemos solucionar ambos problemas
en c s'pari c Las s» "error-message" », y lo anexamos al <li> padre. al eliminar c Las s e "warning" del <li> padre del campo actual y cualquier «apan
Queremos proporcionar un mensaje algo diferente si el campQ es uno de los cam- c l as s» "error-message" > dentro del mismo c l s cada vez que el campo pierde el í
pos de texto condicionales only required when its corresponding checkbox is checked foco, antes de pasar por las comprobaciones de validación:
(sólo obligatorio cuando su casilla de verificación correspondiente está seleccionada). $ (document) .ready(function() {
Concatenaremos un mensaje modificador al mensaje de error estándar. Para hacer esto, $('form :input') .blur(function() (
podemos anidar una sentencia i f más que comprueba la clase condi t ional solamente $(this).parents('li:first').removaClass('warning')
.find('span.error-message'}.remove()¡
después de que se hayan cumplido las dos primeras condiciones if:
if ($(this) .hasClass('required'» (
$Idocument) .ready(function() { var $listltem = $(this) .parents('li:first');
$('form :input') .blur(function() ( if (this.value == ") {
if ($(this) .hasClass('required'» ( var errorMessage = 'This is a required field'¡
var $listltem = $(this) .parentsl'li:first'); if ($ Ithis) .hasClass ( 'conditional '» (
}
if (this.value 1= II &&
Personal Inlo
!/.+@.+\.[a-zA-Z){2.4}$/.test(this.value)) {
Flrat Nama [MMk m m - '--~. var errorMessage = 'Please use proper e-mail format'
i + 1"(e.g. joe@example.com) Ii
$(I<span></span>l)
.addClass('error-message')
•. byE •••• 11
c--_·_·~-=---~
~ Thl818 • requlred 1IeId. wlien lis mlatad cho<:IcboX Is checkad .text(errorMessage)
.appendTo($listltem);
•. byPhono [ 1~ T11I810. requlred fIeId, wIien lis mlatad cho<:IcboXIs checked $listltem.addClass('warning') ;
I!!l by Fax
// código continúa . . .
}) ;
Figura 8.7, Script de validación en funcionamiento.
El código realiza las siguientes tareas:
Pero, espere. Queremos eliminar la clase warning del elemento < 1i > Ysus elemen- • Comprobaciones para el id del campo de correo electrónico; si la comprobación
tos e span class="error-message"> cuando el usuario quita la selección de una tiene éxito:
casilla de verificación también. Podemos hacer esto al visitar nuestro código de casilla
de verificación anterior una vez más y hacer que active blur en el campo de texto co- • Establece una variable para el elemento de lista padre.
rrespondiente cuando su casilla de verificación no está seleccionada: • Comprueba el estado oculto del campo de correo electrónico. Si está oculto
if (this.checked) {
(que sucede cuando su casilla de verificación correspondiente no está selec-
$thislnput.show() .addClass('required'); cionada), su valor se establece en una cadena vacía. Esto permite que nuestra
$thisFlag.show() ; eliminación del mensaje de error anterior funcione correctamente para los
$ (this) .parent('label') .addClass('req-label'); campos de correo electrónico también.
el se {
$thislnput.hide() .removeClass('required').blur(); • Comprueba que el valor del campo no es una cadena vacía y que el valor del
$thisFlag.hide() ;
campo no coincide con la expresión regular. Si las dos comprobaciones tienen
$(this) .parent('label') .removeClass('req-label');
éxito, el script:
• Crea un mensaje de error.
Ahora cuando una casilla de verificación no está seleccionada, los estilos de aviso
relacionados, y los mensajes de error están fuera de la vista y de la mente. • Inserta el mensaje en « spar; c La s s e "error-message" >.
• Anexa el elemento e span c l as s» "error-message" >y suscontenidos
Formatos obligatorios al elemento de lista padre.
Existe un tipo más de validación a implementar en nuestro formulario de contacto: • Añade la clase warning al elemento de lista padre.
formatos de entrada de datos correctos. Algunas veces puede ser de utilidad propor- Ahora echemos un vistazo a la expresión regular en solitario:
cionar un aviso si el texto se incorpora en un campo de forma incorrecta (en lugar de
simplemente dejado en blanco). Los principales candidatos para este tipo de aviso son !/ .+@.+\. [a-zA-Z) {2.4}$/ .test(this.value)
EIiI 8. Formularios con funciones Aprende jQuery 1.3 &iI
Aunque esta expresión regular es similar a la que hemos creado anteriormente en el $ (document) .ready(function() (
$('form') .submit(function() (
capítulo, utiliza el método. test () en lugar del método. replace (), ya que solamente $('#submit-message') ,remove();
necesitamos que devuelva true o falseo Como antes, la expresión regular va entre las $(' :input.required') .trigger('blur');
dos barras inclinadas. Luego se comprueba contra una cadena que se sitúa dentro de los var nÚmwarnings :1:1 $ (1 .warning,~, this) ,length¡
paréntesis de . test (), en este caso el valor del campo del correo electrónico. if (numWarnings) {
$ ('<div></div>')
En esta expresión regular, buscamos un grupo de uno o más caracteres no de nueva .attr({
línea (.+), seguido por un símbolo e, y luego seguido por otro grupo de uno o más carac- lid': 'submit-message',
teres no de nueva línea. Hasta el momento, una cadena como lucia@example pasaría 'clasa': .warning ,
la prueba, como lo harían millones de otras permutaciones, aunque no es una dirección })
.append('Please correct errara with I +
de correo electrónico válida. numWarnings + fields')
I
Podemos hacer que la comprobación sea más precisa al buscar un carácter . seguido .insertBefore('#send');
de dos a cuatro letras entre a y z al final de la cadena. Eso es exactamente lo que hace el return falss¡
}
resto de la expresión regular. Primero busca un carácter entre a y z o A Y Z ( [a - zA - Z 1 ).
}) ;
Luego dice que una letra en ese rango puede aparecer de dos a cuatro veces solamente }) ;
({ 2,4}). Por último, insiste que esas dos a cuatro letras aparecen al final de la cadena: $.
Ahora una cadena como 1ucia@example . com devolvería true, mientras que 1ucia@ Además de proporcionar una petición genérica para solucionar errores, el mensaje
example,01ucia@example.2fnolucia@example.exampleoluci~-example. indica el número de campos que se tienen que solucionar, como se ve en la figura 8.8.
com, devolvería falseo Pero queremos que nos devuelva true (y el mensaje de error,
etc., creado) solamente si el formato de dirección de correo electrónico adecuado no se
incorpora. Ésa es la razón por la que precedemos la expresión de comprobación con el l~axredenonM 3 ~d. I
signo de exclamación (no operador):
!/ .+@.+\. [a-zA-Z] (2,4}$/.test(this.value) Figura 8.8. Información para el usuario.
Sin embargo, podemos hacer algo mejor que simplemente mostrar el número de erro-
Una última comprobación res, podemos listar los nombres de los campos que contienen errores:
El código de validación está ahora casi completo para el formulario de contacto.
$ (document) .ready(function() (
Podemos validar los campos del formulario una vez más cuando el usuario trata de en- $('form') .submit(function() (
viarlo, esta vez todo a la vez. Al utilizar el manejador de evento . submi t () en el for- $('#submit-message') .remove();
mulario (no el botón Send), activamos blur en todos los campos obligatorios: $(' :input.required') .trigger('blur');
var numWarnings = $(1 .warning', this) .length¡
$ (document) .ready(function() ( if (numWarnings) {
$('form') .submit(function() ( var list = [l;
$('#submit-message') .remove(); $('.warning label').each(function()
$(' :input.required') .trigger('blur'); list.push($(this).text());
}) ; }) ;
». $ (' <div></div>
.attr ((
1)
Observe ahora que hemos colado una línea para eliminar un elemento que todavía 'id': 'submit-message',
'class': 'warning'
no existe: <di v id=" submi t -message" >. Añadiremos este elemento en el siguiente
paso, Simplemente estamos eliminándolo como medio de prevención aquí porque ya ».append('Please correct error s with the following , +
sabemos que necesitaremos hacerlo basándonos en los problemas que hemos encontra- numWarnings + fields:<br />')
1
do al crear múltiples mensajes de error anteriormente en el capítulo. .append('• , + list.join('<br />• '))
.insertBefore{'#send') ¡
Después de activar bl ur, recibimos el número total de clases warning en el formu-
return false¡
lario actual. Si no hay ninguna, crearemos un nuevo <di v id=" submi t -message" > };
y lo insertaremos delante del botón Send donde es más probable que lo vea el usuario. }) ;
También impediremos que el formulario realmente se envíe: }) ;
ltr-'r.',
I~ Ahora tenemos una nueva casilla de verificación con una etiqueta que dice check
Figura 8.9. Unir los elementos de la labia con salto de línea y boliche.
all (seleccionar todas). Pero todavía no hace nada. Necesitamos anexarle el método
. click ():
Ciertamente, el HTML para la lista de campos es de presentación en lugar de semán-
tico. Sin embargo, para una lista efímera, una que se genera por JavaScript como un úl- $ (document) .ready(function()
timo paso y pensada para eliminarla lo más pronto posible, perdonaremos este código $ ('<1i></li>')
.htrnl('<label><input type= checkbox" id=lIdiscover-all />'
rápido en aras de la sencillez y brevedad. +
U
11
<em>check
I all</em></label>')
.prependTo('li.discover :> ul');
}
How dId you di"""" ••. us1 (Check all thaI apply)
}) ;
!!!I Magaztne }) ;
8w_
Dentro de este manejador de evento, primero establecemos la variable $checkboxes,
El Talevlslo" que consta de un objetojQuery que contiene toda casilla de verificación dentro de la lista
ElMoYle
actual. Con la variable establecida, manipular las casillas de verificación se convierte en
una cuestión de seleccionarlas si la casilla de verificación check all está seleccionada, y
13 SchooI
deseleccionarlas si check all no lo está.
!!!IMom Un último toque se puede aplicar a esta característica de casilla de verificación al
13 Blltboartl añadir una clase checkall a la etiqueta de la casilla de verificación check all, y cam-
biar su texto a un-check all (deseleccionar todas) después de que se ha seleccionado por
13 Gramtl
el usuario:
El Detrttus
$ (document) .ready(function() {
El HateMaIl $ ('di></li>')
.htrnl('<label><input type="checkbox lIid="discover-all" />' +
, <ern>check all</ern></label>')
Figura 8.10. Lista de casillas de verificación. .prependTo('li.discover > ul');
$('#discover-a11') .click(function() (
Para redondear nuestras mejoras en el formulario de contacto, ayudaremos al usua- var $checkboxes = $(this) .parents('u1:first')
rio a gestionar esta lista. Un grupo de la casillas de verificación puede ser desalentador, .find(' :checkbox');
Jwr
mi 8. Formularios con funciones Aprende jQuery 1.3 ea
if (this.checked) {
$ (thia) .next O .text (' un-check a11'); El código terminado
$checkboxes.attr('checked', true) I
else ( Aquí está-el código terminado para el formulario de contacto:
$(thia) .next() .text(' check a11'); .,
$checkboxes. attr ('checked', ") I $ (document) .ready(function() (
l. // Mejorar estilo de elementos de formulario.
}) $('legend') .each(function(index) (
.parent('labe1') .addClass('checka11'); $(this) .replaceWith('<h3>' + $(this) .text() + '</h3>') I
}) I
}) I
El grupo de casillas de verificación, junto con el cuadro check all, ahora se parece a var requiredFlag = ' * ';
la figura 8.11. var conditionalFlag = ** i I I
SGratIIII .end O
.filter(' ,conditional')
13 Detritus .next('span') .text(conditionalFlag);
$listltem.addClass('warning') ; if (this.checked) (
$(this) .next() .text(' un-check all');
$checkboxes.attr('checked', true) i
el se (
if (this.id == 'email') ( $(this) .next() .text(' check all');
var $listltem = $(this) .parents('li:first'); $checkboxes.attr('checked', 1') i
if ($ (this) .is(' :hidden')) { };
this .val ue = "¡ })
} .parent('label') .addClass('checkall');
if (this.value && != " }) ;
!/ .+@.+\. [a-zA-Z] {2,4}$/ .test(this.value))
var errorMessage = 'Please use proper e-mail format'
Aunque hemos realizado mejoras significativas en el formulario de contacto, todavía
+ ' (e.g. joe@example.com)';
$('<span></span>')
queda mucho por hacer. La validación, por ejemplo, se presenta de varias formas. Para
.addClass(rerror-message') un plug-in de validación flexible, visite http://plugins . jquery. com/proj ect/
.text(errorMessage) validate/ .
.appendTo($listltem) ;
$listltem.addClass('warning') ;
} Formularios compactos
}l;
Algunos formularios son mucho más sencillos que los formularios de contacto. De
// Validar formulario al enviar.
$('form') .submit(function() (
hecho, muchos sitios incorporan un formulario de un solo campo en cada página: una
$('#submit-message') .remove() ; función de búsqueda para el sitio. Los elementos habituales de un formulario, como eti-
$(' :input.required') .trigger('blur'); quetas de campo, botones de envío y el texto, son incómodos para una parte tan pequeña
=
var numWarnings $(' .warning', this) .length¡
de la página. Podemos utilizar jQuery para ayudar a simplificar el formulario mientras
if (numWarnings) (
var fieldList = [);
mantiene sus funcionalidades, e incluso mejorar su comportamiento para que sea más
$(' .warning label') .each(function() útil que un equivalente de página completa.
fieldList.push($(this) .text());
}l;
$('<div></div>') Texto como marcador de posición para campos
.attr({
'id': 'submit-message',
El elemento e Labe L» para un campo de formulario es un componente esencial de
'class': 'warning'
sitios Web accesibles. Todo campo se debería etiquetar de modo que los lectores de pan-
»
.append('Please correct errors with the following , + talla y otros dispositivos de ayuda puedan identificar qué campo se utiliza, y para qué
numwarnings + ' fields:<br />') propósito. Incluso en la fuente HTML, la etiqueta ayuda a describir el campo:
B!II 8. Formularios con funciones
Aprende jQuery 1.3 ea
c:::form id=lIsearch
l1 action=lIsearch/index.phpll method=lIget >ll
c:::label for=lIsearch-text">search
<input type="text ll
the sitec:::/label>
name="search-text" id=lIsearch-text ll />
II-!he .no '1
</form>
Figura 8.15. No se puede hacer clicen la etiqueta.
Sin estilo, vemos la etiqueta delante del campo, como se muestra en la figura 8.13. Para evitar el primer problema, ri'ecesitamos ocultar el texto de la etiqueta cuando
el campo recibe foco, y mostrarlo de nuevo cuando se pierde foco, siempre y cuando.
i 8e.~h~.tte
I _ J
I no haya texto incorporado por el usuario en el campo. Ocultar el texto de la etiqueta en
foco es sencillo:
Figura 8.13. Etiquetasin estilo. $ (docurnent) .ready(function() (
l' var $search = $('#search') .addClass('overlabel');
Aunque esto no ocupa mucho espacio, en algunos diseños de sitio incluso esta línea var $searchlnput = $search.find('input');
var $searchLabel = $search.find('label');
de texto podría ser demasiado. Podríamos ocultar el texto con CSS, pero luego esto no
proporciona al usuario forma de saber para qué es el campo. En su lugar, utilizaremos $searchlnput
CSS para posicionar la etiqueta sobre el campo, solamente si JavaScript está disponible, .focus(function() (
al añadir una clase al formulario de búsqueda: $searchLabel.hide();
)l
$ (docurnent) .ready(function() ( .blur(function() {
var $search = $('#search') .addClass('overlabel'); if (this.value == ") (
)) ; $searchLabel.show() ;
)
En una sola línea estamos añadiendo una clase al formulario de búsqueda y almace- )) ;
)) ;
nando el selector en una variable de modo que podemos hacerle referencia más adelante.
La hoja de estilo utiliza la clase overlabel para aplicar estilo a la etiqueta: La etiqueta ahora está oculta cuando el usuario escribe texto en el campo, como se
.overlabel (
ve en la figura 8.16.
)
position: relative¡
Ilt<.. I I
.overlabel label ( Figura 8.16. Ocultarla etiqueta cuando se escribe.
position: absolute¡
top: 6px;
left: 3px;
El segundo problema ahora es bastante sencillo de solucionar también. Podemos
color: #999; ocultar el texto de la etiqueta y proporcionar al usuario acceso a la entrada de datos al
cursar: text¡ permitir que un clic en la etiqueta active el evento focus para la entrada de datos:
$ (docurnent) .ready(function() (
var $search = $('#search') .addClass('overlabel');
No solamente la clase añadida posiciona la etiqueta adecuadamente, sino que tam- var $searchlnput = $search.find('input');
bién deshabilita el texto para distinguirlo como un marcador de posición, como muestra var $searchLabel = $search.find('label');
la figura 8.14.
$searchlnput
Ilsesn:Ii Ihe stte II .focus(function() (
$searchLabel.hide();
Figura 8.14. Aplicarestilo a la etiqueta. ))
.blur(function() {
Éste es un buen efecto, pero tiene un par de problemas: if (this.value·== ,,) (
$searchLabel.show() ;
)
1. El texto de la etiqueta oscurece cualquier texto que el usuario incorpora en el
)) ;
campo de texto.
$searchLabel.click(function()
2. Ahora solamente se puede acceder a la entrada de texto al utilizar la tabulación.
$searchlnput.trigger('focuB');
Puesto que la etiqueta cubre la entrada de datos, al usuario se le impide hacer }) ;
clie en él, como se ve en la figura 8.15. )) ;
BliI 8. Formularios con funciones Aprende jQuery 1.3 eDI
Por último, necesitamos gestionar el caso en el que el texto permanece en el campo de La idea básica detrás de la rutina autocompletar es reaccionar a una tecla, y enviar
entrada de datos cuando la página se refresca, similar a lo que teníamos que hacer con una petición AJAX al servidor que contiene los contenidos del campo en la petición. Los
las entradas condicionales en el apartado de validación de formulario anteriormente en resultados contendrán una lista de posibles terminaciones para el campo. El script en-
este capítulo. Si la entrada de datos tiene un valor, la etiqueta se oculta: tonces presenta esta lista como un desplegable por debajo del campo.
$ (document) .ready(function() (
var $search := $('#search') .addClass('overlabel'); En el servidor
var $searchlnput = $search.find('input') i
var $searchLabel = $search.find('label') i Necesitamos algún código del lado del servidor para gestionar peticiones. Aunque
if ($searchlnput.val())
una implementación del mundo real se basará normalmente en una base de datos para
$searchLabel.hide(); / producir una lista de terminaciones posibles, para este ejemplo podemos utilizar un
sencillo script PHP con los resultados incorporados:
$searchlnput <?php
.focus(function() ( if (strlen($_REQUESTC'search-text']) < 1) {
$searchLabel.hide() ; print '[]';
}) exit¡
.blur(function() {
if (this.value == ") ( $terms = array(
$searchLabel.show() ; " access
I I,
} action'
I I
}) ; $possibilities = array();
}) ; foreach ($terms as $term) {
if (strpos($term, strtolower($_REQUEST['search-text']))
Una ventaja de utilizar la etiqueta en lugar de insertar un valor predeterminado direc- === O) {
$possibilities [] = rr r 11. str_replace (11 , u I 11 \ \ 111, $term)
tamente en la entrada de texto es que esta técnica se puede adaptar a cualquier campo de
texto sin tener que preocuparse por un conflicto potencial con un script de validación.
ciones sencillas y del mundo real, como un punto de partida para otras más complejas. $('#search-text') .keyup (function ()
Se puede encontrar en http://ui.jquery . com/. $.ajax<{
BllI 8. Formularios conJunciones Aprende jQuery 1.3 eg
'url': '../search/autocomplete.php',
'data ': {' search-text': $ (, #search-text r) . val () },
Id.1 -t
'dataType': 'json',
de,ellct
~
'type': 'GET', deftnltlVe
d•• lgn •.
'success': function(data) o.,
d•• lgnlng
if (data.1ength) {
dOYOlopers
$autocomp1ete.empty(),
development
$.each(data, function(index, term) {
$('<li></li>') .text(term) .appendTo($autocomp1ete) , Figura 8.18. Mecanismo autocompletar incorporado del navegador.
}),
$autocomp1ete.show() ,
oculta cuando no existen resultados para una búsqueda dada. En segundo lugar, pode-
}
mos añadir un manejador rnouseover que resalte el elemento bajo el cursor del ratón. }) ;
1m 8. Formularios con funciones Aprende jQuery 1.3 mil
.insertAfter('#search-text') ; }
var selectedltem = null; else if
(event.keyCode == 38 &&
selectedltem !== null)
var setSelectedltem = function(item) // Usuario ha pulsado Flecha arriba.
selectedltem = item; setSelectedltem(selectedltem - 1);
if (selectedltem === null) event.preventDefault() i
$autocomplete.hide() ; }
return¡ else if(event.keyCode == 40 &&
selectedltem !== null) (
// Usuario ha pulsado Flecha abajo.
if (selectedltem < O) setSelectedltem(selectedltem + 1);
selectedltem = o; event.preventDefault() ;
}
if (selectedltem >= $autocomplete.find('li') .length) (
selectedltem = $autocomplete.find('li') .length - 1; else if (event.keyCode == 27 && selectedltem !== null) (
} // Usuario ha pulsado tecla Ese.
$autocomplete.find('li') .removeClass('selected') setSelectedltem(null) ;
.eq(selectedltem) .addClass('selected'); }
$autocomplete.show() ; }) .keypress(function(event) (
i. if (event.keyCode == 13 && selectedltem !== null) (
t.mI 8. Formularios con funciones
Aprende jQuery 1.3 mi
/1 Usuario ha pulsado tecla Intro. </tr>
populateSearchField() ; ctr plass="shipping">
event.preventDefault() ;
<td class="itemtl>Shippingc/td>
} <ftd class=lIquantity">S</td>
}) .blur(function(event) (
'ctd class="price">$2 pe r, Lt.ernc y t.d s
setTimeout (function () (
<td class="cost">$lO.OOc/td>
setSelectedltem(null) ; </tr>
}, 250);
<tr class="total">
}) ;
<td class="item">Total</td>
j);
<td class="quantity"></td>
ctd class="price"></td>
<td class="cost">$172.13</td>
</tr>
Trabajar con datos de formulario numéricos <tr class="actions">
<td></td>
ctd>
Ahora hemos examinado varias características de formulario que se aplican a entradas <input type="button" name="recalculate"
textuales del usuario. Sin embargo, a menudo nuestros formularios son numéricos en value=IIRecalculate11 id="recalculatell />
contenido. Existen varias mejoras de formulario que podemos realizar cuando estamos </td>
<td></td>
tratando con números como valores de formulario. ctd>
En nuestro sitio de librería, un principal candidato para un formulario numérico es <input type=lIsubmit" name="submit"
el carrito de la compra. Necesitamos permitir que el usuario actualice cantidades de ar- value="Place Order" id="submit" />
tículos que se compran, y también necesitamos presentar datos numéricos de vuelta al </td>
</tr>
usuario para precios y totales. </tfoot>
<tbody>
<tr>
Estructura de tabla de carro de la compra <td class="item">
Building Telephony Systems With Asterisk
</td>
El HTML para el carro de la compra describirá una de las estructuras de tabla más
<td class="quantityll>
relacionadas que hemos visto hasta el momento: <input type="text" name="quantity-2" value="l"
id="quantity-211
maxlength="3" />
cform action=Ucheckout.php" methoct="postll>
</td>
ctable id=lIcart">
<td class="price">$26.99c/td>
cthead>
<td class="cost">$26.99</td>
ctr>
</tr>
cth class=llitem11>Iteme/th>
<tr>
cth class=lIquantityl1>Quantity</th>
<td class="item">
cth class="price'I>Pricec/th>
Smarty PHP Template Prograrnming and Applications
cth class="cost >Totalc/th>
11
</td>
</tr>
<td class=lIquantity">
</thead>
cinput type=tltext" name="quantity-l" value="2"
ctfoot>
id=lIquantity-l" maxlength="3" />
ctr class=l1subtotalll>
</td>
ctd class="itemll>Subtotalc/td>
<td class="price">$35.99</td>
ctd class="quantityll></td>
<td class="cost">$71.98</td>
ctd class=llprice"></td>
</tr>
ctd class="cost">$152.9Sc/td>
<tr>
</tr>
<td class="item">
ctr class=lItaxl1>
Creating your MySQL Database
ctd class=llitem1'>Taxc/td>
</td>-
ctd class=l1quantity"></td>
<td class="quantity">
ctd class="price">6%c/td>
<input type="text" name="quantity_3" value="1"
ctd class="cost">$9.18c/td>
id="quantity-3" maxlength="3" />
r" p"
Esta tabla presenta otro elemento pocas veces visto, -ct f oot;». Como -;:thead>, este Shlpplng S2perttem
elemento agrupa un conjunto de filas de tabla. Observe que aunque el elemento viene Tocal
delante del cuerpo de la tabla, se presenta detrás del cuerpo cuando se muestra la pági-
na, como se ve en la figura 8.20.
€Ut'ildlliñ9 @iü.~
Cálculos numéricos
6%
15 $2 por Item
Ahora, pasaremos a cierta manipulación de los números que el usuario incorporará
en el formulario de carro de la compra. Tenemos un botón Recalculate (Volver a calcu-
(_0Iifi~ ~K«i0ñIé~
lar) en el formulario, que hará que el formulario se envíe al servidor, donde se pueden
calcular nuevos totales y el formulario se puede presentar de nuevo al usuario. Esto re- Figura 8.22. Actualizar la cantidad total automáticamente.
quiere un recorrido completo que no es necesario, sin embargo; todo el trabajo se puede
realizar en el lado del navegador utilizando jQuery.
El cálculo más sencillo en este formulario es para la celda en la fila Shipping (EnVÍO) Analizar y aplicar formato a moneda
que muestra la cantidad total de elementos solicitados. Cuando el usuario modifica una
Ahora, podemos pasar a los totales en la columna derecha. El coste total de cada fila
cantidad en una de las filas, queremos añadir todos los valores incorporados para pro-
se debería calcular al multiplicar la cantidad facilitada por el precio de ese elemento.
ducir un nuevo total y mostrar este total en la celda:
Puesto que estamos llevando a cabo varias tareas para cada fila, podemos empezar por
Var $quantities = $('td.quantity input'); volver a estructurar los cálculos de cantidad un poco para que se basen en fila en lugar
$quantities.change(Eunction() ( de en campos:
var totalQuantity = O;
$quantities.each(Eunction() ( $('#cart tbody tr') .each(Eunction() (
var quantity = parselnt(this.value); var quantity = parselnt($('td.quantity input', this) .val());
totalQuantity += quantity; totalQuantity += quantity;
}) ; }) ;
$('tr.shipping td.quantity') .text(String(totalQuantity));
}) ; Esto produce el mismo resultado que antes, pero nosotros ahora tenemos un lugar
adecuado para insertar nuestro cálculo de coste total para cada fila:
Tenemos varias opciones respecto al evento a seguir para esta operación de volver
a calcular. Observaremos el evento keypress, y activaremos el volver a calcular con $('td.quantity input') .change (Eunction ()
var totalQuantity = O;
cada pulsación del teclado. Podríamos también observar el evento blur, que se activa
$('#cart tbody tr') .each(function() (
cada vez que el usuario abandona el campo. Aquí, podemos ser algo más conservadores var price = parseFloat($('td.price', this) .text()
con el uso de CPU, sin embargo, y solamente llevar a cabo nuestros cálculos cuando se .replace(r¡A\d.J*/, "));
activa el evento change. De esta forma, volvemos a calcular los totales solamente si el price = isNaN{price) ? O : price¡
var quantity =
usuario abandona el campo con un valor diferente de lo que tenía antes.
parselnt ($ ('td.quantity input', this) .val());
La cantidad total se calcula utilizando un sencillo bucle . each ( ) . La propiedad var cost = quantity * price¡
. value de un campo informará de la representación de cadena del valor del campo, $('td.cost', this).text('$' + cost);
de modo que utilizamos la función parselnt () incorporada para convertir esto en un totalQuantity += quantity;
j);
entero para nuestro cálculo. Esta práctica puede evitar situaciones extrañas en las que la
$('tr.shipping td.quantity') .text(String(totalQuantity));
suma se interpreta como concatenación de cadena, ya que las dos operaciones utilizan el }) ;
EifI 8. Formularios con funciones Aprende jQuery 1.3 mi
Vamos a buscar el precio de cada elemento de la tabla utilizando la misma técnica Como vemos aquí, el total que debería ser $1079.70 se muestra como $1079.7. Incluso
que necesitamos cuando ordenábamos tablas por precio anteriormente. La expresión re- peor, los límites de precisión de JavaScript algunas veces pueden conducir a errores de
gular primero quita los símbolos de moneda de delante del valor, y la cadena resultante redondeo. Estos pueden hacer que los cálculos aparezcan completamente rotos.
f
luego se envía a parseFloa t ( ) , que interpreta el valor como un número en coma flo-
tante. Puesto que realizaremos cálculos con el resultado, necesitamos comprobar que se
ha encontrado un número, y establecer el precio en O de lo contrario. Por último, mul-
tiplicamos el coste por la cantidad, y luego situamos el resultado en la columna total
con un $ precediéndolo. Ahora podemos ver nuestros cálculos totales en acción como
muestra la figura 8.23.
Tax 6%
Shlpplng 52 $2 perttem
Total
@éiJCüritet @'t_g¡¡j~f~
SUbtlllal $152.95
Tal< 6% $9.18
Tax 6%
Shlpplng 43 $2 per Item $10.00
Shlpplng 52 S2peritem
Total $1T2.13
Total
t1<iiüiilCü_~ ~~ @t'8Iilü~
Figura 8.24. Trabajar con decimales. Figura 8.26. Mejorar los valores monetarios con decimales.
mi 8. Formularios con funciones Aprende jQuery 1.3 ED
Redondear valores
Después de una larga serie de operaciones aritméticas, el redondeo de números en coma Para calcular impuestos, necesitamos dividir la cifra facilitada entre 100 y luego
flotante podria causar suficiente error acumulado que incluso . toFixed () no pueda multiplicar taxRate por el subtotal. Como el impuesto siempre se redondea, debemos
enmascararlo. Laforma más segura de gestionar manipulaciones de moneda en grandes asegurarnos de que el valor correcto se utiliza tanto para la visualización como para cál-
aplicaciones es almacenar y manipular todos los valores en céntimos, como enteros; los culos posteriores. La función Math. ee il () de JavaScript puede redondear un número
puntos decimales se pueden añadir para visualización solamente. hacia arriba hasta el entero más próximo, pero puesto que estamos tratando con dólares
y céntimos tenemos que ser un poco más complicados:
var taxRate = parseFloat($('tr.tax td.price') .text(» j 100;
var tax = Math.ceil(totalCost * taxRate * 100) j 100;
Otros cálculos $('tr.tax td.cost') .text('$' + tax.toFixed(2»;
totalCost += taxi
El resto de los cálculos en la página siguen un patrón similar. Para el subtotal, pode- El impuesto se multiplica por 100 primero de modo que se convierte en un valor en
mos sumar nuestros totales para cada fila según se calculan, y mostrar el resultado uti- • céntimos, no dólares. Esto luego se puede redondear de forma segura por Math . ee i 1 ( )
lizando el mismo formato de moneda que antes, como se ve en la figura 8.27. y luego dividirse por 100para convertirlo de nuevo en dólares. Por último, . toFixed ()
$('td.quantity input') .change(function() se invoca como antes para producir el resultado correcto.
var totalQuantity = Di "
var totalCost a O;
$('#cart tbody tr') .each(function() {
var price = parseFloat($('td.price', this) .text()
.replace(jA(A\d.*/, "»;
price = isNaN(price) ? O : price;
var quantity =
parselnt($('td.quantity input', this) .val(»;
var cost = quantity * price¡
$('td.cost', this) .text('$' + cost.toFixed(2»; 6%
totalQuantity += quantity; Shlpplng $2 por ltBm
totalCost += cost;
T_
j);
$ ('tr.shipping td.quantity') .text (String(totalQuantit y»; ~ttiKaJa¡liiO, ~~l
$('tr.subtotal td.cost') .text('$' +
totalCost.toFixed(2»; Figura 8.28. Calcular el impuesto.
j);
Toques finales
El cálculo de envío es más sencillo que el impuesto ya que no implica ningún re-
dondeo en nuestro ejemplo. El transporte se multiplica por el número de artículos para
determinar el total:
$('tr.shipping td.quantity') .text(String(totalQuantity»;
var shippingRate = parseFloat($('tr.shipping td.price')
.text().replace(r(A\d.l*j, "»;
var shipping = totalQuantity * shippingRate;
$('tr.shipping td.cost') .text('$' + shipping.toFixed(2»;
6% totalCost += shipping;
23 $2 perttem
Hemos estado siguiendo el importe total a medida que hemos ido avanzando, por
€,Rii:II~ @'i!<i!é<d~ lo que todo lo que nos queda por hacer para esta última celda es formatear totalCost
de forma apropiada:
Figura 8.27. Gestionar totales.
$('tr.total td.cost') .text('$' + totalCost.toFixed(2»;
mi 8. Formularios con funciones Aprende jQuery 1.3 ••
Ahora, hemos replicado por completo cualquier cálculo del lado del servidor que ocu- .insertAfter($('td:nth-Child(2)', this»
.append($deleteButton) ;
rriría, por lo que podemos ocultar el botón Recalculate, como muestra la figura 8.29. ) ;
$ ('<td>&ñbsp;</td>')
$('#recalculate') .hide();
.insertAfter ('#cart tfoot t,f:'>nth-child(2) ,) ;
Necesitamos crear celdas vacías en las filas cabecera y pie de página como marcadores
de posición de modo que las columnas de la tabla se alineen correctamente. Los botones
se crean y añaden en las filas del cuerpo solamente, como muestra la figura 8.30.
Tax 6%
Shlpplng 7 $2 perltem
Total
@~~~
Figura 8.29. Eliminar el botón Recalculate. .'
~-~
Shipplng $2 perltem
Total
Este cambio una vez más se hace eco de nuestro principio de mejora progresiva: en
primer lugar, asegúrese de que la página funciona correctamente sin JavaScript. Luego,
utilice jQuery para llevar a cabo la misma tarea de forma más elegante cuando sea po- Figura 8.30. Permitir eliminar elementos.
sible.
Ahora necesitamos hacer que los botones hagan algo. Podemos cambiar la definición
del botón para añadir un manejador click:
Eliminar elementos
$deleteButton ~ $('<irng />') .attr({
width '16
Si los comprobadores en nuestro sitio cambian de parecer acerca de los elementos I 1: I I
height
I 1: 1161,
que han añadido a sus carros, pueden cambiar el campo Quantity (Cantidad) para esos 'src': /images/cross.png',
l ..
elementos a O.Podemos proporcionar un comportamiento más alentador, sin embargo, 'alt': 'remove from cart',
al añadir botones Delete (Suprimir) explícitos para cada elemento. El efecto actual del 'title': 'remove from cart',
botón puede ser el mismo que cambiar el campo Quantity, pero el feedback visual puede 'class': 'clickable'
}) .click(functionn {
reforzar el hecho de que el artículo no se comprará. .; ,.
$(this).parents('tr') .find('td.quantity input')
En primer lugar, necesitamos añadir los nuevos botones. Puesto que no funcionará .val (O);
sin JavaScript, no los pondremos en el HTML. En su lugar, dejaremos que jQuery los »;
añada a cada fila:
El manejador encuentra el campo quanti ty en la misma fila que el botón, y esta-
$('<th> </th>') blece el valor en 0, como muestra la figura 8.31. Ahora, el campo se actualiza, pero los
.insertAfter('#cart thead th:nth-child(2) ');
cálculos no están en sincronía.
$('#cart tbody tr') .each(function() {
$deleteButton ~ $('<irng />') .attr({ Necesitamos activar el cálculo como si el usuario hubiera cambiado manualmente
'width': '16', el valor del campo:
I height ': I16 ' I
}I.click(function() {
$(thisl .parents('tr'l .find('td.quantity input'l
.val(O) .trigger('change');
}I ;
6% __ "_1
Tax $2pe<ltem •....
Shlpplng
Total
~
Figura 8.33. Ocultar la fila en la tabla.
Tax 6%
Cuando la fila se oculta,el campo sigue presente en el formulario. Esto significa que
Shlpplng $2perHem
YoIal
se enviará con el resto del formulario, y el elemento se eliminará en el lado del servidor
{I!I-.w-., en ese momento.
Nuestro diseño de filas de líneas alternas se ha visto alterado al eliminar esta fila.
Figura 8.31. Añadir un manejador al balón. .: Para corregir esto, movemos nuestro código de filas alternas existentes a una función
de modo que podemos invocarla de nuevo más adelante. Al mismo tiempo, necesita-
Ahora los totales se actualizan cuando se hace clic en el botón. mos modificar el código para asegurarnos de que nuestra selección de fila alterna ignora
cualquier fila oculta.
Desafortunadamente, incluso si filtramos todas las filas ocultas, seguimos sin poder
utilizar el selector : nth-child (even), porque aplicará la clase al t a todas las filas vi-
sibles que son hijo par de su padre. Veamos lo que sucede cuando aplicamos el siguiente
código modificado a cuatro filas de tabla cuando la segunda está oculta:
$('#cart tbody tr'l .removeClass('alt')
.filter(' ,visible,nth-child(even) ') .addClass('alt');
Ahora el feedback visual. Ocultaremos la fila en la que se ha hecho clic, de modo que Son visibles tres filas; sólo la cuarta tiene aplicada la clase al t. Lo que necesitamos
el elemento se elimina claramente de la cesta, como muestra la figura 8.33. es la expresión selector : visible: odd, ya que elegirá cada dos filas después de elimi-
$deleteButton = $('<img />') .attr({ nar las ocultas de la selección (y justifica el cambio de un selector de índice uno a otro
'width': '16', de índice cero). Como se vio en el capítulo2, utilizar: odd o : even podría producir re-
'height': '16', sultados inesperados si tuviéramos más de un elemento -c t body». pero en este caso,
'sre': ' ../images/cross.png',
'alt': tremove from cart' I
estamos en buena forma. Con el cambio de selector en su lugar, nuestra nueva función
'title': 'remove from cart' I
se parece a esto:
'class': 'clickable'
)) .click(function() ( var stripe = function() {
$(this) .parents('tr') .find('td.quantity input') $('#cart tbody tr') .removeClass('alt')
.val(O) .trigger('change') .filter(' ,visible,odd') .addClass('alt');
.end () .hide () ;
};
)); stripe() ;
&llI 8. Formularios con funciones Aprende jQuery 1.3 mi
Ahora podemos invocar esta función de nuevo después de eliminar una fila: Pero con JavaScript disponible, y con el potencial de jQuery a nuestra disposición,
$deleteButton = $('<img 1>'1 .attr({
podemos convertir este pequeño vínculo en un formulario completo. Realizaremos esto
I width 1: r 16 I f al solicitar el formulario desde una página PHP. Típicamente los datos que completan
I height ": 116 I ,
el formulario se almacenarán en una.base de datos de algún tipo, pero para los fines de
'sre': /images/cross.png',
l ••
esta demostración, mantendremos algunos datos estáticos en una tabla PHP.
lalt': Iremove from cart',
'title': 'remove from cart',
Para recuperar el formulario y hacer que aparezca dentro del cuadro Shipping to
'class': 'clickable' (Enviar a), utilizamos el método $ . get () dentro del manejador de evento. el iek () :
}1.click(function(1 (
$ (document) .ready(function() (
$(this) .parents('tr'l .find('td.quantity input'l
.val(O) .trigger('change') $('#shipping-name') .click(function() (
.end () .hide () ; $.get('shipping.php', function(data)
stripe (); $('#shipping-name') .remove();
}) ; $ (data) .hide() .appendTo('#shipping') .slideDown();
}) ;
return false¡
La fila eliminada ahora ha desaparecido, como muestra la figura 8.34.
}) ;
»;
Al comprobar la ausencia de la variable de servidor $_SERVER l ' HTTP_X_
REQUESTED _WITH 1 antes de imprimir la mayor parte de la página, la página PHP
I
clicy en su lugar anexamos el formulario y sus datos desde shipping .php. Luego aña-
dimos return false de modo que el evento predeterminado para el vínculo en el que
~ií&-~
se hace clic (cargar la página indicada en el atributo href) no ocurre. Ahora el cuadro
Shipping to es un formulario editable, como muestra la figura 8.36.
Figura 8.34. Eliminar la fila.
Shipplng lo:
Esto completa otra mejora utilizando jQuery que es completamente transparente
para el código en el servidor. Por lo que se refiere al servidor, el usuario simplemente Flrstname: ~ __ ==:::J
ha escrito un Oen el campo de entrada, pero para el usuario se trata de una operación Las! name: ISchoon I
de eliminación diferente de cambiar una cantidad. Addmss:§ooeo Drl"" I
CIIy: ~~. =::J
Stale: l? I
Editar información de envío ZlP: §C J
GttI·message: !:jOY thEtB8 grs8,t. books-r You' 11 leva
La página del carro de la compra tiene también un formulario para información de the cliff-han,9'eral
Tiene sentido que se elimine el formulario en este punto y que el cuadro Shipping El código terminado
to regrese a su estado original. Podemos conseguir esto en la rellamada del método
$ . post () que acabamos de utilizar: Tomados juntos, el código para la página de carrito de la compra son meramente 80
$ (document) .ready(function() {
.' líneas, bastante pequeño considerando la funcionalidad que realiza, pero especialmente
$('#shipping form') .submit(function() (
cuando tenemos en cuenta el estilo dinámico que ha adquirido el código para legibilidad
var postData = $(this) .serialize(); óptima. Muchas de las líneas en jQuery se podrían haber combinado, si hubiéramos es-
$.post('shipping.php', postData, function(data) tado particularmente preocupados con el número de líneas, debido a la posibilidad de
$ ('#shipping form') .remove(), encadenar de jQuery. En cualquier caso, aquí tiene el código terminado para la página
$ (data) .appendTo('#shipping'),
de carrito de compra, que concluye este capítulo sobre formularios:
}) ;
eliminación del formulario en una función denominada saveShipping () . Luego po- event.which> 57))
event.preventDefault();
demos vincular la función saveShipping () en la rellamada de $ . get (), después de
que se haya creado el formulario. De igual forma, podemos vincular la función edi tS-
hi pping () tanto cuando el DOM está listo y cuando el vínculo Edit shipping se vuelve }) .change(function()
a crear en la rellamada de s . post () : var totalQuantity = O;
var totalCost = O;
$ (document) .ready(function() ( $('#cart tbody tr') .each(function() (
var editShipping = function() { var price = parseFloat($(' .price', this)
$.get('shipping.php', function(data) .text () .replace (r ['\d.] * /, "»;
$('#shipping-name') .remove(); price = isNaN(price) ? O , price;
$ (data) .hide() .appendTo('#shipping') .slideDown(); var quantity =
$('#shipping form') .submit(saveShipping), parselnt($(' .quantity input', this) .val(), 10);
}) ; var cost = quantity * price¡
return false¡ $ (,.cost " this). text (,$' + cost. toFixed (2» ;
i. totalQuantity += quantity;
~"!n'
&DI 8. Formularios con funciones Aprende jQuery 1.3 IJD
totalCost += cost¡ }) ;
}); return. false i
};
$(' .subtotal .cost') .text('$' + totalCost.toFixed(2)); $('#shipping-name') .click(editShipping);
var taxRate = parseFloat($(' .tax .price') .text()) / 100; }) ; - -',
var tax = Math.ceil(totalCost * taxRate * 100) / 100;
$('.tax .cost').text('$' + tax.toFixed(2));
totalCost += taxi
$(' .shipping .quantity') .text(String(totalQuantity));
var shippingRate = parseFloat($('.shipping .price')
Resumen
.text() .replace(r¡'\d.]*/, "));
var shipping = totalQuantity * shippingRate; En este capítulo hemos investigado formas de mejorar la apariencia y comportamíento
$(' .shipping .cost') .text('$' + shipping.toFixed(2));
de elementos de formulario HTML comunes. Hemos aprendido cómo mejorar el estilo
totalCost += shipping;
$(' .total .cost') .text('$' + totalCost.toFixed(2)); de formularios, ocultar y mostrar campos condicionalmente basándose en otros valo-
}) ; res de campo, y validar contenidos de campos antes del envío y durante la entrada de
datos. Hemos tratado características como autocompletar AJAX para campos de texto,
$('<th> </th>')
permitiendo incorporar sólo caracteres específicos en un campo, y llevar a cabo cálculos
.insertAfter('#cart thead th:nth-child(2) ');
$('#cart tbody tr') .each(function() {
sobre valores numéricos en campos. También hemos aprendido a enviar formularios
$deleteButton = $('<img />') .attr({ utilizando AJAX en lugar de refrescar una página.
'width 1: '16', El elemento formulario es a menudo el pegamento que mantiene un sitio interactivo
'height': '16',
junto. Con jQuery, podemos fácilmente mejorar la experiencia del usuario al completar
'src /images/cross.png'¡
l: ' ••
c La s s ":
I clickable I I
}) .click (function () {
$(this) .parents('tr') .find('td.quantity input')
.val (O) .trigger ('change' )
.end ().hide ();
stripe ();
});
$ (' c t.do e y t.d> ")
.insertAfter($('td:nth-child(2)', this))
.append($deleteButton) ;
});
$ (,<td> </td>' )
.insertAfter('#cart tfoot td:nth-child(2) ');
}) ;
$ (document) .ready(function() {
var editShipping = function() {
$.get('shipping.php', function(data)
$('#shipping-name') .remove();
$(data) .hide() .appendTo('#shipping') .slideDown();
$('#shipping form') .9ubmit(saveShipping);
}) ;
return
false¡
};
var saveShipping = function() {
var postData = $(this) .serialize();
$.post('shipping.php', postData, function(data)
$('#shipping form') .remove();
$(data) .appendTo('#shipping');
$('#shipping-name') .click(editShipping);
____________________________ 1
, /
.'
9 vista con incluso más estilo. Estos tipos de animaciones se conocen también como carru-
seles o rotativos. Lo que tienen en común es una habilidad para girar rápidamente entre
múltiples datos en una forma llamativa e impresionante. En este capítulo exploraremos
estas animaciones avanzadas, combinándolas con técnicas AJAX y sutilezas CSSpara real-
mente causar impresión. Pasaremos por dos grandes ejemplos en este capítulo: un título
Rotativos rotativo y un carrusel de imágenes. Estos ejemplos nos permitirán aprender cómo:
•
•
Animar la posición de un elemento.
Analizar documentos XML.
• Preparar efectos de estilo avanzados utilizando opacidad parcial.
• Recuperar información de dominios diferentes.
• Crear un elemento de interfaz de usuario para desplazamiento horizontal.
• Componer capas para etiquetas y superposiciones.
Titular rotativo
Para nuestro primer ejemplo de rotativo, tomaremos un feed de noticias y desplaza-
remos los titulares, junto con extracto del artículo, a la vista uno de cada vez. Las histo-
rias fluirán hasta quedarse a la vista, se detendrán para leerse, y luego se desplazarán
mi 9. Rotativos
e
, Aprende jQuery 1.3 mi
fuera de la vista como si fueran una cinta infinita de información dando vueltas conti-
núamente por la página. Observe ql!-eaquí la altura de los elementos de noticias individuales (representados
por la clase headl ine) y su contenedor es de 2 OOpx.Igualmente, puesto que los elemen-
tos headl Lrie están posicionados de forma absoluta relativos a #news - feed, podemos
Configurar la página alinear la parte superior de los elementos de noticias con la parte inferior de su conte-
nedor. De esta forma, cuando establecemos la propiedad overflow de #news-feed
En su nivel más básico, esta característica no es muy dificil de implementar. Pero como en hidden,los titulares no se muestran inicialmente. Establecer la posi tion de los ti- .
pronto veremos, hacer que esté lista para producción requiere un poco de sutileza. tulares en absolute es necesario por otra razón también: para que cualquier elemento
Empezamos, como siempre, con un bloque de HTML. Situaremos el feed de noticias tenga su ubicación animada en la página, debe tener posición absolute o relati ve,
en la barra lateral de la página:
en lugar del posicionamiento static predeterminado.
<:h3>Recent News</h3> Ahora que tenemos el HTML y CSS en su lugar, podemos aplicar los elementos de
<:div id="news-feedU> noticias desde un feed RSS. Para empezar, situaremos el código en un método. each ( ) ,
<a href="news/index,htmlll>News
</div>
Releases</a> que actuará como una sentencia i f Ycontendrá el código dentro de un espacio de nom-
bre privado:
$ (document) .ready(function() (
Hasta el momento, el área de contenido de nuestro feed de noticias contiene solamente
$('#news-feed') .each(function()
un solo vínculo a la página de noticias principal, como se ve en la figu5a 9.1. var $container = $(this);
$container.empty() ;
}) ;
Recent N_s
)) ;
NowsRe! •• S8~
éxito. El contenido del feed se pasa a este manejador como una estructura XML. Luego
RecentNew8
podemos utilizar el motor selector de jQuery para trabajar con estos datos.
IQuory. M!croto!!. and Nolda
$ (document) .ready(function() ( touery UI18n:2
$('#news-feed') .each(function() JOotry Confarenca 2008 Agenda
var $container = $(this); O.al!! lo JayaScr!pt Rock Sta,,1
$container.empty(); IQuery Sita Red•• !an • II!t
$.get('news/feed.xml'. function(data) { Communltv ,Speakl
$ ('rss item'. data) .each(functionO { IQuery.comSII!! Red•• ¡go
II Trabajar con los titulares aquí. Raglatratlan Optn tgr !QueN
}) ; ConferaDa, 2008
}) ; IQuerI UI1.5.2
/'
}) ;
}) ;
Figura 9.3. Todavía no se ha aplicado la clase headline.
}) ; $ (' <div></div>')
}) ; .append($head1ine. $publication. $summary)
}) ; .appendTo($container) ;
});
método . append ( ) . Observe aquí que estamos utilizando una nueva característica del IQl!lry. Mlcro!oll
9/2812008 and NOIIIa
método; si se proporciona más de un argumento, todos ellos se anexan en secuencia,
como muestra la figura 9.4. Wa llaVe twa pleces of fanlas1lc, albeH
Berendlpltous, news today: BoIh
Mlcroson and NoIda arelaklng 1Ile
majar slap 01 ado¡lUng jQuery as par!
Recent Newa o/1heIr oIIIoIai appllcatlon
daVelopment plalfonn.
Figura 9.4. Anexar los elementos en secuencia. var headlineCount = $('div.headline'l .length,
var pause¡
Como podemos ver, el título, fecha, vínculo y resumen de cada noticia ahora están en
No hay necesidad todavía de asignar a pause un valor en este momento; se esta-
su lugar. Todo lo que queda es añadir la clase headl ine con. addClass ( headl ine
I I )
blecerá cada vez que ocurre la rotación. Sin embargo, siempre debemos declarar varia-
(que ocultaremos a la vista debido al CSS definido anteriormente), y estamos listos para bles locales utilizando var para evitar el riesgo de colisiones con variables globales del
continuar con nuestra animación.
mismo nombre.
También deberíamos actualizar el valor oldHeadl ine de modo que podamos fácil- segundos después de que se haya recuperado el feed RSS.Ahora tenemos un rotativo
mente manipular el titular que se está moviendo fuera de la vista. de titulares totalmente operativo.
var headlineRotate = function() { $ (document) .rrady(function() (
currentHeadline = (oldHeadline + 1) % headlineCount¡ $('#news-feed') .each(function()
// Animar las posiciones de titular aquí. var $container = $(this);
oldHeadline = currentHeadline¡ $container.empty() ;
}; $.get('news/feed.xml', function(data) {
$('rss item', data) .each(function() {
Ahora tenemos que llenar el vacío con el código que mueve los titulares. En primer var $link = $(Ica></a>')
.attr('href', $('link', this).text())
lugar, añadiremos una animación que mueve el titular antiguo fuera de la vista. Luego, .text($('title', this) .text());
insertaremos otra animación que pasa el nuevo titular a la vista. var $headline = $('<h4></h4>') .append($link) ;
}) ;
después de un período especificado. En este caso, estamos haciendo que headl ineRo- }l;
tate () se lance de nuevo en cinco segundos (5000milisegundos). }) ;
Ahora tenemos un ciclo de actividad; una vez que se completa una animación, la
siguiente está lista para activarse. Queda invocar la función la primera vez; haremos A mitad de la animación, podemos ver un titular cortado en la parte superior, y el si-
esto con otra llamada a setTimeout (), haciendo que la primera transición ocurra 5 guiente que aparece a la vista cortado en la parte inferior, como muestra la figura 9.6.
Ea 9. Rotativos Aprende jQuery 1.3 BiI
Rac:ent News
la variable en true y luego en la rellamada del segundo método. animate (), lo esta-
IlaYo .-c"'V<VQ'QIU ."tu lItO'v.lUDuaaa
blecemos en falseo
15growln¡¡ 11'IOI9stabIe every day. The
full changaln¡¡ Is avallable hera WYO" var headlineRqtate = function()
want lo Ilnd out n (...]
if (Irotat'elnProgress) {
rotatelnProgress = true;
lQuary Ull.S.l currentHeadline = (oldHeadline + 1)
6/2712008 % headlineCount¡
Soon IIfIer the mleass 01jQUOI)' UI $('div.headline') .eq(oldHeadline) .animate(
1.5, we were gettfng many useful {top, -hiddenPosition}, 'slow', function()
feedback and fssues entered In our
$(this) .CSS(ltop', hiddenPosition) i
bug!md<er. Todey, we're happy lo
}) ;
$('div.headline') .eq(currentHeadline) .animate(
Figura 9.6. Titulares parcialmente cortados.
[Eop , o). 'slow', function () (
rotatelnProgress = false¡
pause = setTimeout(headlineRotate, 5000);
Detenerse al pasar por encima )) ;
oldHeadline = currentHeadline;
Aunque el rotativo de titulares está operativo completamente, existe un significativo
};
problema de usabilidad que deberíamos abordar: un titular podría desplazarse fuera de
la vista antes de que un usuario pueda hacer clic en uno de sus vínculos. Esto obliga al Rac:entNewa
usuario a esperar hasta que el rotativo ha pasado en ciclopor todo el conjunto de titulares _ on :sun!ISY. :septelt1llW 26,
de nuevo antes de tener una segunda oportunidad. Podemos reducir la posibilidad de IA¡tlllMW.a,se1!two tracks of
~ (beginner and
este problema al hacer que el rotativo se detenga cuando el cursar del ratón del usuario
pasa por encima de cualquier parte dentro del titular.
$container.hover(function()
-...atI~_".
~ced
if (! rotatelnProgress) {
función en una sentencia if. Inmediatamente dentro de este condicional, establecemos
rotatelnProgress = true¡
BB 9. Rotativos Aprende jQuery 1.3 mil
pausa •. falsa; Dentro del archivo f eed. php, recuperamos el contenido de nuestro feed de noticias
currentHeadline = (oldHeadline + 1)
% headlineCount;
del sitio remoto, luego imprimimos el contenido como el resultado del script. '
$('div.headline') .eq(oldHeadline) .animate(
<?php !
{top: -hiddenPosition}. 'slow', function()
header ('Con t errt v'I'ype : text/xml'); ",
$(this) .css('top', hiddenPosition);
print file_get_contents('http://jquery.com/blog/feed');
}) ;
?>
$('div.headline') .eq(currentHeadline) .animate(
{top: O}, 'slow', function() (
rotatelnProgress = false¡
Observe aquí que necesitamos establecer explícitamente el tipo de contenido de la
if (Ipause) { página en text/xml de modo que jQuerypuede ir a buscarloy analizarlo como si fuera
pause = setTimeout(headlineRotate, 5000); un documento XML normal, estático.
} /
});
oldHeadline = currentHeadline¡
};
if (Ipause) Algunos proveedores de hospedaje Web pueden no permitir el uso de la función PHP
pause = setTimeout(headlineRotate, 5000); file_get_contents () para ir a buscar archivos remotos debido a problemas de
} seguridad. En estos casos, soluciones alternativas, como utilizar la librería eURL,pueden
$container.hover(function()
clearTimeout(pause)
pausa = falsa;
; .' estar disponibles. Más información sobre esta librería se puede encontrar en ht t P : / /
wiki.dreamhost.com/CURL.
}, funct ion () {
if (1 pausa) {
pause = setTimeout(headlineRotate, 250);
}
}) ; Añadir un indicador de carga
Por fin, nuestro rotativo de titulares puede soportar todo tipo de intentos del usua- Recuperar un archivo remoto como éste podría llevar algo de tiempo, dependiendo
rio para desbaratarle. de un número de factores, por lo que deberíamos informar al usuario de que la carga
está en progreso.
Para hacer esto, añadiremos una imagen de indicador de carga a la página antes de
Recuperar un feed de un dominio diferente , lanzar nuestra petición AJAX.
var $loadinglndicator = $('<img/>')
El feed de noticias que hemos estado utilizando para nuestro ejemplo es un archivo .attr({
local, pero podríamos querer recuperar un feed de otro sitio. Como.:hemos visto en un ca- 'sre': 'images/loading.gif',
'alt': Loading. Please wait.
pítulo anterior, las peticiones AJAXno se pueden, como regla, realizar a un sitio diferente I I
que el que alberga la página que se visualiza. Allí, tratamos el formato de datos JSONP ».addClass('news-wait')
como un método para eludir esta limitación. Aquí, sin embargo, asumiremos que no po- .appendTo($container) ;
demos modificar la fuente de datos, por lo que necesitamos una solución diferente.
Para permitir que AJAX vaya a buscar ese archivo, utilizaremos código del lado del Luego, como primera línea de la rellamada de éxito de nuestra función $ . get ( ) ,
servidor como un proxy para la petición, para que JavaScript crea que el archivo XML podemos eliminar la imagen de la página con un sencillo comando:
está en nuestro servidor aunque reside en uno diferente. Escribiremos un pequeño script $loadinglndicator.remove() ;
PHP para extraer el contenido del feed de noticias a nuestro servidor, y pasar esos datos
al script jQuery solicitante. Este script, que denominaremos feed. php, se puede invocar Ahora, cuando la página se carga por primera vez, si hay un retraso al recuperar el
de la misma forma que se fue a buscar feed. xml previamente: contenido del titular, veremos una imagen de carga en lugar de un área vacía, como
muestra la figura 9.8.
$.get('news/feed.php', function(data) (
// ... Esta imagen es un GIF animado, y en un navegador Web girará para significar que
}) ; se está llevando a cabo cierta actividad.
mi 9. Rotativos Aprende jQuery 1.3 &111
$ (document) .ready(function() (
RacentNews $('#news-f~ed') .each(function()
var $container = $(this);
$contai~er.empty() ;
Antes de dejar nuestro ejemplo de titular rotativo, démosle un toque final, al hacer var fadeHeight = $container.height() / 4;
que el texto del titular parezca que aparece progresivamente desde la parte inferior de for (var yPos = O; yPos < fadeHeight; yPos '+= 2) (
$('<div></div>') .css({
su contenedor. El efecto será un degradado, apareciendo como si el texto fuera opaco en
opacity: yPos / fadeHeight.
la parte superior del efecto y transparente en la parte inferior. top: $container.height() - fadeHeight + yPos
Un solo elemento de texto no puede tener múltiples opacidades simultáneamene, }) .addClass('fade-slice') .appendTo($container);
sin embargo. Para simular esto, cubriremos el área de efecto con una serie de elemen- }
}) ;
tos, cada uno de los cuáles tiene una opacidad diferente. Estos cortes serán elementos j);
<di v » con algunas propiedades de estilo en común, que podemos declarar en nuestra
hoja de estilo: Estos cálculos pueden ser difíciles de visualizar, por lo que situaremos los números
.fade-slice (
en una tabla. Los valores de opacidad suben incrementalmente de transparente a opaco,
position: absolute; ya que los valores superiores empiezan en la parte superior del área de degradado (150)
width: 20ern¡ y crecen a la altura del contenedor:
height: 2px;
background: #efd;
z-index: 3¡
Todos tienen las mismas propiedades width y background - color que su elemento o O 150
contenedor, <di v id= "news- feed" >. Esto engañará el ojo del usuario al pensar que el 2 0.04 152
texto se está desvaneciendo, en lugar de cubrirse por otro elemento.
Ahora podemos crear los elementos ed i.v c Las s« "fade-slice" >. Para estar se- 4 0.08 154
guros de que tenemos el número correcto, primero determinaremos una altura en píxe- 6 0.12 156
les para toda el área de efecto. En este caso, estamos eligiendo 25 por ciento de la altura
8 0.16 158
<di v Ld» " news - f eed" >. Utilizaremos un bucle f or para iterar por la altura del área,
creando un nuevo elemento de corte para cada segmento de 2 píxeles del degradado:
ti
I
&DI 9. Rotativos Aprende jQuery 1.3 &D
var $loadinglndicator = $('<irng/>')
.attr 1{
's"rc': images/loading. gif t ,
I
})
42 0.84 192 .addClass('news-wait')
.appendTo($container) ;
44 0.88 194
0.92 196 $.get('news/feed.php', function(data)
46
$loadingIndicator.rernove() ;
48 0.96 198 $ ( 'rss itern', data). each (function () {
var $link = $('<a></a>')
('
.attr('href', $('link', this) .textll)
Recuerde que puesto la posición superior del último <di v clas s = " f ade - s 1 i ce " > .text($('title', this).textll);
es 198,su altura de 2 píxeles claramente superpondrá los dos píxeles inferiores del <div » var $headline = $('<h4></h4>') .append($link);
contenedor de 200 píxeles de alto. Con nuestro código en su lugar, el texto en el área de var pubDate = new Date($('pubDate', this) .text());
titular de la página ahora se funde desde transparente a opaco, a medida que solapa la var pubMonth = pubDate.getMonth() + 1;
parte inferior del contenedor, como muestra la figura 9.9. var pubDay = pubDate.getDate();
var pubYear = pubDate.getFullYear();
var $publication = $('<div></div>')
.addClass('publication-date')
RecentNewa
.text(pubMonth + '1' + pubDay + ,/, + pubYear);
•••••••••
' ••••••••
n ••••••••••••.•
" •••••••••.••••••••••.•
""' •••••••••
elearTimeout(pause) ;
alt="Deep Inside osCommerce: The Cookbook" />
pause = false;
cspan class="price">$44.99c/span>
}. funet ion () (
c/a>
if (!pause) (
<a href="images/covers/large/1847190979.jpg"
pause = setTimeout(headlineRotate. 250) ; title="Learn OpenOffice.org Spreadsheet Macro
}
Programming: OOoBasic and Calc automation">
});
<img sre="images/covers/medium/1847190979.jpg"
}) ;
}) ;
width="120" height="148"
». alt="Learn
Programming:
OpenOffice.org
OOoBasic
Spreadsheet
and Calc
Macro
automation" />
cspan class="price">$3s.99c/span>
Un carrusel de imágenes
.' c/a>
<a href="images/eovers/large/1847190987.jpg"
title="Mierosoft AJAX C# Essentials: Building
Responsive ASP.NET 2.0 Applications">
Como otro ejemplo de desplazar contenido por la página, implementaremos una cimg src=lIimages/covera/medium/1847190987.jpg"
height='t148It
galería de imágenes para la página principal del sitio de librería. La galería presentará width="120"
alt="Microsoft AJAX C# Essentials: Building
algunos libros destacados para la venta, con vínculos a la portada ampliada para cada Responsive ASP.NET 2.0 Applieations" />
uno. A diferencia del ejemplo anterior, donde los titulares se movían según una planifi- capan class="price">$3l.99c/span>
cación establecida, aquí utilizaremos jQuery para desplazar las imágenes por la pantalla c/a>
conseguiremos aquí, estos plug-ins pueden producir efectos de deplazamiento de alta <img src="images/eovers/medium/1847192386.jpg"
calidad con muy poco código. width="120" height="148"
alt="Building Websites with Joomla! 1.5 Beta 1" />
capan class="price">$40.49c/span>
c/a>
Como siempre, empezamos diseñando el HTML y CSS de modo que los usuarios Cada imagen se encuentra contenida dentro de una etiqueta de ancla, apuntando a
sin JavaScript disponible reciban una representación llamativa y funcional de la infor- la versión más grande de la portada. También tenemos precios para cada portada; se
mación: ocultarán por ahora, y utilizaremos JavaScript para mostrados más adelante en un mo-
<:div id=Ufeatured-bookslI>
mento apropiado.
<:div class=lIcoversll> Para ahorrar espacio en la página principal, queremos mostrar solamente tres porta-
<a href=" images/eovers/large/184 7190871. jpg" das a la vez. Sin JavaScript, podemos realizar esto al establecer la propiedad overf low
title="Community Server Quickly"> del contenedor en scroll, y ajustar la anchura de forma apropiada:
,,,.--
}
} ) .f ind (,.covers a'). css ({
#featured-books .CQvers
/ "f Loa t t : 'none',
position: relative¡
'position': 'absolute',
width, 840px;
,left " 1000
z-index: 1;
));
}
#featured-books a
f Loa t : left;
var $covers = $('#featured-books .covers a');
margin: lOpx¡
$covers.eq(O) .css('left', O);
height, 146px;
$covers.eq(l) .css('left', spacing);
}
#featured-books .price
$covers.eq(2) .css('left', spacing * 2);
}) ;
display: none;
Figura 9.10. Desplazarse por las imágenes con barra de desplazamiento estándar.
Figura 9.11. Sólo tres portadas sin desplazamiento.
$covers.unbind('click') i
// Imagen izquierda;
// desplazarse a la derecha (para ver imágenes a la izquierda).
$covers.eq(O)
.css (,left " O)
.click(function(event) (
$covers.eq(2) .css('left', 1000); Figura 9.12. Reorganizar las portadas.
$covers.eq($covers.length - 1)
.prependTo('#featured-books .covers'); Hacer die en la imagen #2 lleva a cabo el proceso a la inversa. Esta vez, es #0 el que
setUpCovers() ;
se oculta a la vista, y luego se mueve al final de la cola. Esto cambia #1 al puesto #0, #2
event.preventDefault() j
al #1, y #3 al #2.
}) ; Existen un par de detalles que tenemos que tener en cuenta para evitar anomalías de
interacción de usuario:
// Imagen derecha;
// desplazarse hacia la izquierda (para ver las imágenes a:la derecha).
1. Necesitamos invocar .preventDe f aul t () dentro de nuestro manejador el ick,
$covers.eq(2)
.css('left', spacing * 2) ya que nosotros hemos convertido todas las portadas en vínculos a la versión más
.click(function(event) ( grande. Sin esta llamada, el vínculo se seguirá y nunca veremos nuestro efecto
$covers.eq(O) .css('left', 1000); de movimiento.
$covers. eq (O)
.appendTo('#featured-books .cavers'); 2. Necesitamos quitar el vínculo de todos los manejadores click al principio de
setUpCovers(); la función setUpCovers (), o podremos acabar con múltiples manejadores
vinculados a la misma imagen a medida que el carrusel rota.
event.preventDefault() ;
}) ;
// Imagen central.
$covers.eq(l) Añadir animación deslizable
.css('left', spacing);
};
Puede ser difícil entender lo que ha sucedido cuando se ha hecho die en una ima-
setUpCovers() ; gen; puesto que las portadas se mueven instantáneamente, es posible que haya pareci-
}) ; do que simplemente se han cambiado en lugar de movido. Para mitigar este problema,
mi 9. Rotativos Aprende jQuery 1.3 lIiII
podemos añadir una animación que hace que las portadas se deslicen en lugar de sim- de portada, debemos aplazar la llamada hasta que la animación se completa, por lo que
plemente aparecer en sus nuevas posiciones. Esto requiere una revisión de la función situamos la llamada en la rellamada de la animación.
setUpCovers ( ) : Un clic en.la imagen más a la derecha lleva a cabo un conjunto similar de animacio-
nes, pero al contrario. Esta vez, es la.imagen más a la izquierda la que se mueva fuera
var setUpCovers = function() {
var $covers = $ ('#featured-books .covers a') i
de la vista, y se debe mover al final de la cola antes de activar setUpCovers () cuando
$covers.unbind(lclick i
1) se completa la animación. La nueva imagen, más a la derecha, por el contrario, se debe'
/1 Imagen izquierda; desplazarse a la derecha (para ver imágenes a la izquierda). posicionar (spacing * 3) antes de que su animación pueda empezar.
$covers. eq (O)
.css('left', O)
.click(function(event) (
$covers.eq(O) .animate({'left': spacing}, 'fast');
$covers.eq(l) .animate({'left': spacing * 2}I fast
I I )i
$covers.eq(2) .animate({'left': spacing * 3}I fast r ) i
I
$covers.eq($covers.length - 1)
.css('left
t, -spacing)
·animate ({ ,left ': O}, 'fast', function () (
$(this) .prependTo('#featured-books .covers');
setUpCovers() ;
Figura 9.13. Hacer que una nueva imagen aparezca a la vista.
}) ;
event.preventDefault() ; .'
}) ;
// Imagen derecha; desplazarse a la izquierda (para ver imágenes a la derecha). Mostrar íconos de acción
$covers.eq(2)
.css('left', spacing * 2) Nuestro carrusel de imágenes ahora rota sin problemas, pero no hemos proporcionado
.click(function(event)' (
ninguna indicación al usuario de que haciendo clicen las portadas hará que se desplacen.
$covers.eq(O)
·animate ({ ,left': -spacing), 'fast', function () Podemos ayudar al usuario al mostrar los iconos apropiados cuando el ratón pasa por
$(this) .appendTo('#featured-books .covers'); encima de las imágenes, como muestran las imágenes 9.14 a 9.16. En este caso, situare-
setUpCovers() ; mos los iconos sobre las imágenes existentes. Al utilizar la propiedad opacity, pode-
}) ;
mos continuar para ver la portada por debajo cuando se muestra el icono. Utilizaremos
$covers. eq (1) .animate ({ 'left': O). 'fast');
$covers.eq(2) .animate({ 'left': spacing}, 'fast'); sencillos iconos monocromos para que la portada no sea demasiado obscura.
$covers. eq (3)
.css(11eft', spacing * 3)
·animate ({ 1eft
1 1:spacing * 2}
I I fast ') i
event.preventDefault();
}) ;
~
// Imagen central. Figura 9.14. Icono para desplazarse a la izquierda.
$covers. eq (1)
I
.css('left', spacing);
};
Cuando se hace die en la imagen izquierda, podemos mover las tres imágenes vi-
sibles a la derecha en el ancho de una imagen (reutilizando la variable spacing que Figura 9.15. Icono para ampliar portada.
hemos definido anteriormente). Esta parte es sencilla, pero nosotros también tenemos
que hacer que la nueva imagen se desplace para situarse a la vista. Para hacer esto, toma-
mos la imagen del final de la cola, y primero establecemos que su posición de pantalla
esté fuera de la pantalla en el lado izquierdo (- spacing). Luego, lo pasamos a la vista
junto con los otros elementos, como muestra la figura 9.13.
Aunque la animación se ocupa del movimiento inicial, todavía necesitamos cambiar el
Il
Figura 9.16. Icono para desplazarse a la derecha.
orden de la portada al invocar setUpCovers () de nuevo. Sino lo hacemos, el siguiente Necesitaremos tres íconos, uno para las portadas izquierda y derecha, que el usuario
clic no funcionará correctamente. Puesto que setUpCovers () cambia las posiciones decidirá hacia qué lado desplazarse, y uno para la portada del centro, en el que el usua-
_________________________________ 1
mi 9. Rotativos Aprende jQuery 1.3 lIiD
rio puede hacer clic para una versión ampliada. Podemos crear elementos HTML que Ahora, todo lo que tenemos que hacer en nuestros manejadores hover es situar las
hagan referencia a los íconos y almacenarlos en variables para uso posterior: imágenes en la ubícación DOM correcta y mostrarlas.
!
var $leftRollover = $('<img/>') var setUpCovers = function() {
.attr('src·, 'images/left.gif') var $covers = $('#featured-books .CQvers a )¡l
.addClass('control')
.css('opacity', 0.6) $covers.unbind('click mouseenter mouseleave')¡
. ess ( 'display I I none 1) i
I
// Imagen izquierda; desplazarse a la derecha (para ver imágenes a la izquierda).
var $rightRollover = $('<img/>') $covers.eq(O)
.attr('src', 'images/right.gif') .css('left', O)
.addClass('control') .click(function(event) (
.,.
.css('opacity',0.6) $covers.eq(O) .animate({'left': spacing}, 'fast');
.css('display', 'none')¡ $covers. eq (1) .animate ({ 'left': spacing * 2), 'fast');
var $enlargeRollover = $('<irng/>') $covers. eq (2) .animate ({ 'left': spacing * 3), 'fast');
.attr('src', 'images/enlarge.gif') $covers.eq($covers.length - 1)
.addClass('control') .css( 'left'·, -spacing)
.css('opacity', 0.6) .animate ({ 'left ': O), 'fast', function () (
.c ss ('display', "norie t L, $(this) .prependTo('#featured-books .covers');
setUpCovers() ;
Puede haber notado una gran cantidad de repetición aquí. Para miniqUzar este códi- }) ;
event.preventDefault() ;
go adicional, podemos situar esto en una función que podemos invocar para cada icono
}) .hover(function() {
que se necesita crear: $leftRo11over.appendTo(this) .show();
}, func tion () {
function createControl(src) $leftRollover.hide();
return $ (, <img/> 1) }) ;
,attr( "s r c! are) •
// Imagen derecha; desplazarse a la izquierda (para ver imágenes a la derecha) .
.addClass('control') $covers.eq(2)
.css('opacity',O.6) .css(11eft', spacing * 2)
.css('display', "norie t L: .click(function(event) (
$covers.eq(O)
.animate({'left': -spacing}, 'fast', function()
var $leftRollover = createControl('images/left.gif'); $(this) .appendTo('#featured-books .covers');
var $rightRollover = createControl('images/right.gif'); setUpCovers();
var $enlargeRollover = createControl('irnagesJenlarge.gif'); }) ;
Igual que hicimos anteriormente con el iek, desvinculamos los manejadores mouse- Aplicaremos un conjunto de reglas de estilo a esta nueva das e que son similares a
enter y mouseleave al principio de setUpcovers () de modo que los comportamien- las que hemos-visto antes: '
tos de pasar por encima no se acumulen. Aquí, utilizamos otra característica del método irng.enlarged ~
. unb ind ( ) :manejadores para múltiples tipos de evento se pueden desvincular a la vez position: 'absc Lut e ,
al separar los nombres de tipos de evento con espacios. z-index: Si
cursar: pointer¡
¿Por qué mouseenter y mouseleave? Cuando invocamos el método. hover (),
internamente jQuery traduce esto en dos vinculaciones de evento separadas. La primera
función que proporcionamos se vincula como un manejador para el evento mouseenter, Este posicionamiento absoluto permitirá que la portada flote sobre las otras imágenes
y la segunda se vincula a mouseleave. Por lo tanto, para eliminar los manejadores vin- que hemos posicionado, porque el z - index es mayor que los que ya hemos utilizado.
culados utilizando. hover (), necesitamos desvincular mouseenter y mouseleave. f Ahora necesitamos posicionar la imagen ampliada cuando se hace clic en la imagen del
Ahora cuando el cursor del ratón está sobre una portada, la imagen rollover apropiada centro en el carrusel:
está superpuesta sobre la parte superior de la portada, como muestra la figura 9.17. /1 Imagen central; ampliar portada.
$eovers.eq(l)
.css('left', spacing)
.eliek(funetion(event) (
$enlargedCover.attr('sre'. $(this) .attr('href'))
.ess({
'left', ($('body') .width() - 360) / 2,
" 'top' , 100,
'width', 360,
'height', 444
}) .show ();
event.preventDefault() ;
Figura 9.17. Efectodel cursorsobre una portada. })
.hover(function() (
$enlargeRollover.appendTo(this) .show();
}, funetion() (
Ampliar imagen $enlargeRollover.hide();
}l;
Ahora, nuestra galería de imágenes es totalmente funcional, con un carrusel que per-
Podemos aprovechamos de los vínculos ya presentes en la fuente HTML para saber
mite al usuario navegar hasta la imagen deseada. Un clic en la imagen central lleva a
dónde reside el archivo de imagen de la portada más grande en el servidor. Tomamos
una vista ampliada de la portada en cuestión. Pero, podemos hacer mucho más con esta
esto del atributo href del vínculo, y lo establecemos como el atributo sre de la imagen
funcionalidad de ampliación de imagen.
En lugar de llevar al usuario a una URL diferente cuando se hace die en la imagen de portada ampliada.
Ahora, debemos posicionar la imagen. top, width, y height se han incorporado en
del centro, podemos superponer la portada del libro ampliado sobre la propia página.
el código de momento, pero left requiere un poco de cálculo. Queremos que la imagen
ampliada se centre en la página, pero no podemos saber por adelantado cuál es la coor-
denada apropiada para conseguir este posicionamiento. Podemos encontrar la marca a
mitad de camino en la página al medir el width del elemento «body» y dividirlo entre
Una serie de variacionessobre eltema de mostrar informaciónsuperpuesta en la página
dos. La mitad de nuestra imagen ampliada estará en ambos lados de este punto, por lo
se encuentran disponiblescomoplug-insjQuery.Algunosde los más popularesincluyen
que la coordenada izquierda de la imagen será ($ ( body I width () - 360) / 2, ya
I ) •
FancyBox,ShadowBox,Thickbox,SimpleModal,yjqModal.
que 360 es el width de la portada ampliada. La portada está posicionada ahora de forma
apropiada, centrada horizontalmente en la página, como muestra la figura 9.18.
Esta imagen de portada ampliada requerirá un nuevo elemento de imagen, que po-
demos crear al mismo tiempo que se instancian las imágenes de pasar por encima:
Ocultar la portada ampliada
var $enlargedCover ~ $('<irng/>')
Necesitamos un mecanismo para descartar la portada una vez que se ha ampliado .
.addClass('enlarged')
.hide() La forma más sencilla de hacer esto es hacer que un evento eliek en la portada haga
.appendTo('body') ; que desaparezca paulatinamente:
!'~"';'
lIlII 9. Rotativos
Aprende jQuery 1.3 lIiiI
// Imagen central; ampliar portada.
$eovers.eq(l) de nuevo para cada ampliación. Si no hacemos nada para desvincular el manejador, se
.css('left', spacing} apilarán con el'tiempo. Utilizar. one () se asegura de que los manejadores se eliminan
.eliek(funetion(event) ( una vez utilizados.
$enlargedCover.attr('src', $(this) .attr('href'))
.ess ({
'left': ($('body') .width() - 360) /2, Mostrar un botón cerrar
'top' : 100,
'width': 360, Este comportamiento es suficiente para eliminar la portada grande, pero no estamos
'height': 444
}) proporcionando ninguna indicación al usuario de que hacer clic en la portada hará que
.show() desaparezca. Podemos proporcionar esta ayuda al etiquetar la imagen ampliada con un
.one('click', function() { ,; botón Cerrar. Crear el botón es similar a definir los otros elementos de instancia única
$enlargedCover.fadeOut(); que hemos utilizado, los elementos que se garantizan que aparecen solamente una vez,
»; y podemos invocar la función de utilidad que hemos creado anteriormente:
event.preventDefault() ;
}) var $closeButton = createControl('images/close.gif')
.hover(function() ( .addClass('enlarged-control')
$enlargeRollover.appendTo(this) .show(); .appendTo('body') ;
}, function () (
Cuando se hace clic en la portada central, y se muestra la portada ampliada, necesi-
}) ;
$enlargeRollover.hide() ;
.' tamos posicionar y mostrar el botón:
$closeButton.css({
'left': ($('body') .width() - 360) / 2,
Learning JQuery "t.op ' : 100
}) .show() ;
!!Ol>lII_l1ollIIcI
1 Las coordenadas del botón Cerrar son idénticas a las de la portada ampliada, por lo que
YourCUt
• Bulcllng TeIephony
.~!~~L_ .... sus esquinas superior-izquierda están alineadas, como se muestra en la figura 9.19.
--
Systems Wlth Ast8l1sk
<S.We1
_.""
• Smarty fltip Templete
• Cr9atI~yourMySQI..
I!!-~.
Rocont_
__
.__.J
...
Utilizamos el método. one () para vincular este manejador el iek, que elude un
par de problemas potenciales. Con un . bind () regular del manejador, el usuario po-
dría hacer clic en la imagen de nuevo a medida que se desvanece. Esto podría hacer
que el manejador se activara de nuevo. También, puesto que estamos reutilizando el
mismo elemento de imagen cada vez que se amplía la portada, la vinculación ocurrirá Figura 9.19. Botón Cerrar.
lI!IlI 9. Rotativos
lI!IiI
,, Aprende jQuery 1.3
Ya tenemos un comportamiento vinculado a la imagen que la oculta cuando se hace contenido textual en lugar de una imagen. Una vez más, creamos un elemento de ins-
clic en la imagen. Típicamente en esta situación podríamos basamos en el burbujeo de tancia única al principio de nuestro código JavaScript:
evento para hacer que un clic en el botón Cerrar cause el mismo efecto. Sin embargo, en I
este caso, el botón Cerrar no es un elemento descendiente de la portada, a pesar de las var $priceBaage = $('<div/,')
apariencias. Hemos posicionado de forma absoluta el botón Cerrar sobre la portada, lo .addClass('enlarged-price')
.css('opacity',0.6)
que significa que clics en el botón no se pasan a la imagen ampliada. En su lugar, debe- . ess (' display'I"norie ")
mos gestionar clics en el botón Cerrar: .appendTo('body') ,
$enlargedCover.click();
}) ; Ahora podemos mostrar la etiqueta cuando se amplía la portada:
event.preventDefault(),
) $priceBadge.css({
,right " ($ ("body ') .width () - 360) / 2,
.hover(function() (
'top' , 100
$enlargeRollover.appendTo(this) .show(),
) .text (price) .show ();
}, function() (
$enlargeRollover hide(),
». Esto ajustará el precio en la esquina superior derecha de la imagen ampliada, como
muestra la figura 9.20. Una vez que situamos un $prieeBadge. hide () ; dentro del
Cuando mostramos el botón Cerrar, vinculamos un manejador de evento el i ck para manejador eliek de la portada, hemos terminado.
ello. Todo lo que este manejador necesita hacer, sin embargo, es activar el manejador
el i ck que ya hemos vinculado a la portada ampliada. Necesitamos modificar ese mane-
jador, sin embargo, y ocultar el botón Cerrar allí. Mientras estamos en ello, desvinculamos Animar la ampliación de la portada
el manejador c Li ck para impedir que los manejadores se acumulen en el tiempo. Cuando el usuario hace clic en la portada del centro, la versión ampliada aparece
actualmente en el centro de la página. Para mejorar esto, podemos utilizar las posibi-
Más diversión con el etiquetado lidades de animación incorporadas de jQuery para pasar suavemente entre la vista en
miniatura de la portada y la versión a tamaño completo.
Puesto que tenemos los precios para los libros disponibles en la fuente HTML, po- Para hacer esto, necesitamos saber las coordenadas iniciales de la animación; por
demos mostrar esto como información adicional cuando se amplía la portada del libro. ejemplo, la posición de la portada central en la página. Calcular esta posición requiere
Esta vez aplicaremos la técnica que acabamos de desarrollar para el botón Cerrar para algo de DOM transversal utilizando JavaScript sencillo, pero jQuery nos proporciona
lIl!I 9. Rotativos Aprende jQuery 1.3 IDI
un método abreviado. El método. offset () devuelve un objeto que contiene las co- $closeButton.unbind('click') .hide();
$prkeBadge.hide() ;
ordenadas 1eft y top de un elemento relativo a la página. Luego podemos insertar el
$enlargedCover.fadeOut() ;
width y height de la imagen en este objeto, y tener la información de posición conte- }l; r
nida en un paquete. $closeButton
.css ({
var startPos = $(this) .offset();
'1eft': endPoB.left,
startPos.width = $(this) .width();
'top' : endPos.top
startPos.height = $(this) .height(); })
·show ()
.click(function()
,.. $enlargedCover.click() ;
}) ;
$priceBadge
·css ({
'right': endPoB.left,
'top' : endPos.top
})
.text(price)
·show() ;
}) ;
"
Observe que el botón Cerrar y la etiqueta de precio no se pueden situar hasta que la
animación se completa, por lo que hemos movido su código a una rellamada del método
.an imat.e () . También hemos aprovechado esta oportunidad para simplificar las lla-
madas . css () para estos elementos al reutilizar la información de posición que hemos
calculado para la portada ampliada. Ahora tenemos una transición suavizada de portada
pequeña a grande, como muestra la serie de imágenes en las figuras 9.21 a 9.24:
Ahora podemos utilizar estos dos objetos como mapas de atributos CSS, que se pue-
den pasar a métodos como. css () y . animate () .
,1,OOI1sectetur adlplslclng all~ sed do alusmod tempor Incidid un! ut la
It enlm ad mlnlm vooNim, quls nostnJd exercttatlon uAamoo taborts nlsl
$enlargedcover.attr('src', $(this) .attr('href'))
;1.Oul. aute lrure dolor In rsprehendom In voIuptale veI~ ess a cIIlum ó
.caa(atartPoa) r slnt oocaecal cupldalBl non proldanl, sunt In culpa qul ofllcla daserunt
.show ()
,animate(endPoB, 'normal', function() .met, con •• ctotur adlplslclng all~ sed do alusmod lampor Incldldun! ut la
$enlargedCover entm ad mlnlm venlam, quls nostrud exercttaUon ulJamco laborts nlsi
,/
$closeButton
.css({
'left': endPos.left,
'top' : endPos.top
met, consectetur adlplslclng ell1,sed do elusmod tempor InoIdldunt ut la }l
t enlm ad mlnlm venlam, quls nostrud exercllatlon uUamco !abono nlsl utl .show()
.click(function()
Figura 9.23. Transición. Paso 3.
mi 9. Rotativos Aprende jQuery 1.3 &11
$enlargedCover.click() ; los titulares de noticias, deberíamos proporcionar una indicación al usuario de que está
}) ;
ocurriendo cierta actividad al mostrar un indicador de carga entre tando.
$priceBadge
.css ((
El indicador será otra imagen de instancia única que se mostrará cuando sea apro-
I right ,: endPos.left, piado: r
'tap' : endPos.top var $waitThrobber = $('<irng/>')
}) .attr('src', timages/wait.gif')
.text(price) .addClass('control')
.show (); .css('z-index', 4)
}) ; .hide o .
}; Para esta imagen, estamos utilizando un GIF animado, porque el movimiento reforzará
if ($enlargedCover[O] .complete)
". al usuario de que la actividad se está llevando a cabo, como muestran la figura 9.25.
performAnimation();
}
else {
$enlargedCover.bind(lload', performAnimation);
• • •
Hay dos casos que tenemos que considerar: la imagen está disponible casi instantá-
neamente (quizá debido a guardado en caché), o necesita tiempo para cargarse. En la
primera situación, el atributo complete de la imagen será true, por lo que podemos Paso 1 Paso 2 Paso 3 Paso 4
invocar nuestra función performAnimation () inmediatamente. En el segundo caso, Figura 9.25. Indicadorde carga progresivo.
tenemos que esperar a que se complete la carga de la imagen antes de llamar a perfor-
mAnimation (). Éste es un caso extraño en el que el evento load DOM estándar es de Solamente llevará dos líneas situar nuestro indicador de carga en su lugar, ahora
más utilidad para nosotros que el evento ready personalizado de jQuery. Puesto que" que tenemos el elemento definido. Al principio de nuestro manejador de click para
load se activa en una ventana, imagen, o marco cuando todos sus contenidos se han la imagen central, antes de empezar a hacer cualquier trabajo, necesitamos mostrar el
cargado por completo, podemos observar el evento para aseguramos de que la imagen
indicador:
se muestra correctamente. Solamente entonces se ejecuta el manejador, y se lleva a cabo
la animación. $waitThrobber.appendTo(this) .show();
Estamos utilizando la sintaxis. bind ( , Load ' ) en lugar del método abreviado .load () $waitThrobber.hide() ;
aquí por claridad ya que . load () es también un método AJAX; las dos sintaxis son
Esto es todo lo que se necesita para marcar la portada que se amplía con el indicador
intercambiables.
de carga. La animación aprece superpuesta en la esquina superior izquierda de la por-
tada, como muestra la figura 9.26.
Internet Explorer y Firefox tienen diferentes interpretaciones de lo que hacer si la
,. ""O . lile w.w··' (.~.o>
imagen está ya en la caché del navegador. En este caso, Firefox enviará inmediatamente
el evento load a }avaScript, pero Internet Explorer nunca enviará el evento porque no
ha ocurrido ninguna carga. Nuestra comprobación del atributo complete compensa
esta diferencia en implementaciones.
([) .
"'\iii
I
Añadir un indicador de carga r
!'
tre.
,
~?
Pero ahora, podemos tener una situación extraña en conexiones de red lentas cuan-
tK1;IN?,\ ,;; . ., -.. M;~". 4w~Y.•.L;
do una imagen tarda unos cuantos momentos en cargarse. Nuestra página parece no
hacer nada mientras esta descarga está en progreso. Como hicimos cuando cargamos Figura 9.26. Indicadorde carga.
mil 9. Rotativos Aprende jQuery 1.3 lIfI
BIlI 9. Rotativos
/
r
}) ;
$closeButton
.css ({
'left': endPos.left,
'top' : endPos.top
».show ()
.click(function() (
$enlargedCover.click() ;
j);
$priceBadge
.css ({
1 right ,: endPos.left,
/
Itop' : endPos.top
j)
.text(price)
.show () ;
}) ,
},
if ($enlargedCover[O] .complete)
performAnimation() ,
}
else (
$enlargedCover.bind('load', performAnimation);
}
event.preventDefault() ,
j)
.hover(function() (
$enlargeRollover appendTo(this) .show(),
l, function() (
$enlargeRollover.hide() ,
}) ,
},
setUpCovers() ;
}) ,
Resumen
En este capítulo, hemos examinado los elementos de página que cambian en el tiem-
po, por sí mismos o en respuesta a una intervención del usuario. Estos rotativos pueden
diferenciar una presencia Web moderna de sitios diseñados de forma tradicional. Hemos
tratado la presentación de un feed XML de información en una página, al igual que rotar
elementos en y fuera de la vista en intervalos de tiempo. Además de mostrar un con-
junto de imágenes en una galería al estilo de un carrusel por la que se puede desplazar,
también hemos tratado ampliar una imagen para un primer plano con una animación
suavizada y presentar controles de interfaz de usuario de una forma discreta.
Estas técnicas se 'pueden combinar de muchas formas para dar vida a páginas que
de otra forma serían aburridas, a la vez que mejorar simultáneamente la usabilidad de
nuestras aplicaciones basadas en Web. Las animaciones y efectos que serían tediosos de
conseguir de otra forma se podrían realizar sin esfuerzo gracias al potencial de jQuery.
,,
'~. ~
'd13""
\.":.:1
.....,'0 ..\
~...-.,
'C'
..."...\. _. '" •..
.~.-.:
º-l
~~" ,., .~ '.. \./ ~<>
~ .'0, ~
\. ,6 ..' . /.-"'" ._." //@
/<....,
f~~'
¡.-....
. @'
• e .~" '{:-." \ __ @ 1
• , 15"
-
~f.. ' ,.'¡',,"'
- " .
<-, "\
O \ ~,--
(@J
"
~
A lo largo de este libro, hemos examinado muchas de las formas en que se puede uti-
lizar la librería jQuery para realizar una amplia variedad de tareas. Aún así, un aspecto
Utilizar plug-ins
lectores de ayuda a widgets de interfaz de usuario a gran escala. Ya hemos tratado el
potencial de plug-íns y creado uno sencillo en un capítulo anterior. En este capítulo, exa-
minamos cómo encontrar plug-ins desarrollados por otros e incorporarlos en nuestras
páginas Web. Exploraremos el popular plug-ín Form y la librería oficial de plug-ins de
interfaz de usuario jQuery, y luego listaremos y describiremos brevemente una serie de
otros plug-ins populares "recomendados por el autor".
utilizamos los métodos que el plug-in crea o amplía. A menudo, podemos añadir una }
sola línea dentro del método $ (documen t) . ready () de nuestro archivo personaliza- }) ;
».
do para invocar cierta acción:
La opción target indica el elemento, en este caso, un elemento con Ld» " log", que
$ (document) .ready(function()
$ (/#myID/) .someP1ugin();
se actualizará por la respuesta del servidor.
}) ; La opción beforeSubmit lleva a cabo tareas antes de enviar el formulario. Aquí,
hace referencia a la función validateForm () . Si la función devuelve false, el for-
Muchos plug-ins tienen flexibilidad incorporada también, proporcionando una serie mulario no se enviará.
de parámetros opcionales que podemos establecer para modificar su comportamiento. La opción success lleva a cabo tareas después de que el formulario se envía con éxito.
Podemos personalizar su operación tanto como se necesite, típicamente al incluir un En este ejemplo simplemente proporciona un mensaje de alerta para permitir al usuario
mapa como el argumento del método: saber que el formulario se ha enviado. Otras opciones disponibles con. aj axForm () y
$ (document) .ready(function()
el similar. aj axSubmi t () incluyen:
$ (/#myID/) .someP1ugin ({
• url: La URL al que se enviarán los datos de formulario, si es diferente del atributo
send: true¡
message: /This p1ugin is greati/
action del formulario.
}) ;
});
• type: El método utilizado para enviar el formulario, GETo POST.El predetermi-
nado es el atributo method del formulario, o si no se facilita ninguno, GET.
La sintaxis de los plug-ins jQuery es, típicamente, bastante similar a la sintaxis de los • dataType: El tipo de datos esperado de respuesta del servidor. Los valores
métodos dentro del propio jQuery. Ahora que hemos visto cómo incluir un plug-in en posibles son null, xml, script, o j son. El valor predeterminado es null (una
una página Web, echemos un vistazo a algunos de los más populares. respuesta HTML).
mil 10. Utilizar plug-ins Aprende jQuery 1.3 IPD
• resetForm: Booleano;predeterminado es falseo Si se establece en true, todos Cuando se requiere menos personalización, se puede pasar a losmétodos . a j axForm ()
los valores del campo del formulario se restablecerán en sus predeterminados y . a j axSubmi t () una función en lugar de un mapa de opciones. Puesto que la 'fun-
cuando el envío tiene éxito. ción se trata como el manejador de éxito, podemos obtener el texto de la respuesta del
servidor, de es'taforma:
• clearForm: Booleano;predeterminado es falseo Si se establece en true, todos
los valores del campo del formulario se borrarán cuando el envío tiene éxito. $ (document) .ready(function() (
$ (#myForm) .ajaxForm (function (responseText)
El plug-in Form proporciona una serie de otros métodos para ayudar a gestionar alert(responseText) i
formularios y sus datos. Para un análisis más detallado de estos métodos, así como más j):
j);
demos y ejemplos, visite http://www.malsup.com/jquery/form/.
,1
Consejos y trucos
la librería de plug-ins jQuery UI
El método. aj axForm () es a menudo más apropiado que el método . aj axSub-
mit ( ) , a expensas de poca flexibilidad. Cuando queremos que el plug-in gestione todas Aunque el plug-in Form realiza una cosa, y lo realiza bien, jQuery UI realiza una
las vinculaciones de evento por nosotros, así como invocar el método. aj axSubmi t ( ) amplia variedad de cosas (y las hace bien). De hecho, jQuery UI no es tanto un plug-in,
por nosotros en el momento apropiado, deberíamos utilizar. aj axFOl;m() . Cuando sino una biblioteca completa de plug-ins.
deseamos control más detallado sobre la gestión del evento submi t, ~e recomienda, Dirigido por Paul Bakaus, el equipo jQuery DI ha creado una serie de componentes
. ajaxSubmit (). básicos de interacción y widgets completos para ayudar a que la experiencia Web sea
Tanto. aj axForm () como. aj axSubmi t () por defecto pasan a utilizar los valo- más como la de una aplicación de escritorio. Los componentes de interacción incluyen
res action y method en el código del formulario. Siempre y cuando utilicemos código métodos para arrastrar, soltar, ordenar y cambiar elementos de tamaño. El conjunto ac-
apropiado para el formulario, el plug-in funcionará exactamente como esperamos sin tual de widgets incluye un menú con efecto acordeón, un selector de fecha, un cuadro
ninguna necesidad de modificación. Como beneficio añadido, automáticamente ad- de diálogo, un deslizador, y pestañas, con algunos más en desarrollo. Además, jQuery
quirimos las ventajas de mejora progresiva; el formulario es totalmente funcional sin UI proporciona un amplio conjunto de efectos avanzados para complementar las ani-
JavaScript habilitado. . maciones principales jQuery.
Normalmente cuando se envía un formulario, si el elemento utilizado para enviar el Puesto que la librería UI completa es demasiado extensa para tratarla adecuadamente
formulario tiene un nombre, sus atributos name y val ue se envían junto con el resto de en este capítulo, limitaremos nuestra exploración a efectos UI, el componente de inte-
los datos de formulario. El método. aj axForm () es proactivo en este sentido, añadien- racción Sortable (para ordenar lista de elementos), y el widget Dialog (para cuadro de
do manejadores click a todos los elementos <input t ype» " submit" > de modo que diálogo). Las descargas, documentación y demos de todos los módulos jQuery se en-
sabe cuál envió el formulario. Elmétodo . a j axSubmi t ( ) r por otro lado, es reactivo y no cuentran disponibles en http://ui.jquery . com/.
tiene forma de determinar esta información. No captura el elemento que envía. La misma
distinción se aplica a elementos <input t.ype« " image" > también: .-aj axForm () los
gestiona, mientras que . a j axSubmi t () los ignora. Efectos
A menos que se suba un archivo como parte del envío del formulario, los métodos
. a j axForm () y . a j axSubmi t () pasan su argumento opt ions al método $ . a j ax ( ) El módulo Efectos de jQuery DI viene con un archivo y un conjunto de archivos de
que es parte de jQuery. Por lo tanto, cualquier opción válida para $ . aj ax () se puede efectos individuales. El archivo principal proporciona animaciones para colores y clases,
pasar por medio del plug-in Form. Con esta característica en mente, podemos hacer que así como aceleración y desaceleración avanzada.
las respuestas de nuestro formulario AJAXsean aún más robustas, de esta forma:
$ (document) .ready(function()
$ (#myForm) .ajaxForm({
Animaciones de color
timeout: 2000,
error: function (xml, status, e) ( Con el archivo de efectos principal referenciado en el documento, el método. anima-
alert(e.message) : te () se amplía para aceptar propiedades de estilo adicionales, como borderTopColor,
} backgroundColor, y color. Por ejemplo, ahora podemos cambiar gradualmente un
}) :
}):
elemento de texto negro sobre fondo blanco a texto blanco sobre fondo negro:
• ',lO, ,)"",
,,'\"
u
Demostraciones del conjunto completo de funciones se encuentran disponibles en
http://gsgd.co.uk/sandbox/jquery/easing/.
Efectos adicionales
to jQuery VI. Esto se puede realizar al añadir un argumento o añadir una opción a un En la mitad de la animación, se parece a lo que muestra la figura 10.3.Y al final de la
mapa de opciones, dependiendo de la sintaxis que se utiliza. Por ejemplo, especificar la animación, el cuadrado se oculta.
función easelnQuart para nuestra animación de color anterior se puede realizar con
un argumento adicional:
Componentes de interacción
$ (document) .ready(function()
$ (/#mydiv/) .animate({ Entre los componentes de interacción de jQuery VI está Sortable (para ordenar lista
color, I#fff/.
backgroundColor: 1#0001
de elementos), que puede transformar casi cualquier grupo de elementos en una lista de
}, Islow/, leaselnQuart/); arrastrar y soltar. En la figura 10.4, tenemos una lista no ordenada con algunos estilos
}) ; CSS aplicados a cada elemento.
,rr-r,o·i"!
••••
••••
••••
••••
Figura 10.3. Animación en progreso.
Las dos primeras opciones, opaci ty y cursor, se explican por sí mismas. La terce-
El HIML es bastante sencillo: ra, axis, limita el movimiento del elemento a un eje determinado (en este caso, el eje y)
<ul id="sort-container"> mientras que se ordena, como muestra la figura 10.6.
<li>John<!li>
<li>Paul</li>
<li>George<!li>
c::li>Ringoc::/li>
c::li>Petec::/li>
c Ld sSt.uc y Ld >
<fuI>
Ahora, para que la lista se pueda ordenar, simplemente escribimos el siguiente có-
digo:
$ (document) .ready(function() (
$ (!#sort-container!) .sortable();
}) ;
Como es evidente por el color de fondo más claro del elemento ordenado, también
nos hemos aprovechado de una clase que se le aplica automáticamente, ui - sortable- MyDlalog ,
Widgets
Además de los componentes de construcción, jQuery DI incluye un conjunto de ro-
bustos widgets de interfaz de usuario que funcionan como los elementos que estamos Figura 10.8. Texto en un cuadro de diálogo.
acostumbrados a ver en aplicaciones de escritorio. El widget Dialog, por ejemplo, utiliza
los componentes de arrastrar y cambiar de tamaño para producir un cuadro de diálogo,
de modo que no tenemos que crear el nuestro.
My Dialog
Como con otros widgets de interfaz de usuario, Dialog acepta un gran número de
opciones. Su método denominado. dialog () también puede tomar argumentos de
cadena que alteran lo que hace el cuadro de diálogo. En su nivel más básico, el méto-
do . dialog () convierte un elemento existente en un cuadro de diálogo y lo muestra, ,f.
junto con los contenidos del elemento. Por ejemplo, podemos empezar con una sencilla
estructura <di v », Figura 10.9. Cuadro de diálogo con estilo.
párrafos cuando se hace die. Después de hacer clic en el botón de añadir mensaje tres ~ !&C=
veces, el cuadro de diálogo con estas opciones se parece a la figura 10.10. Oatepick.er
;~~~:;~~
Su No Tu w. Th Fr $a
Inserted message
[nsertecf message
Inserted message
~r~~~~~~~~
Icrem Ipsum dolor sit: .met, consedlllur odipisidng elit. sed do
eiu.mod tempof' Inddldunt ut labore et dolore magna afiqua. ut P1ogreosbar
enim ad mlntm venlam, qull nostrvd exercltation ull.mea
..~;;-~~']~ laborla ni" ut allqulp ex ee c:ommodo COt1sequat.
••• J
• ,a
Figura 10.10. Modificar la apariencia del cuadro de diálogo. .' H;ghtightl Em>'
Las otras opciones para configurar la visualización y comportamiento de los cua- Framewol'i< loon8 (content color praview)
dros de diálogo se puede encontrar en http://does.j query. com/UI/Dialog/ éé1l~J ITli @:,1] E~~ ¡;;;¡~r<:l!K:~~~ ~~: [ A AJe"1 Sample ut-stetilH!lTor styIe. I
dialog#options.
Figura 10.11. Herramienta ThemeRoller.
ThemeRoller
Una reciente incorporación a la librería jQuery VI es ThemeRoller, un motor temático
interactivo basado en Web para widgets VI (véase figura 10.11). ThemeRoller facilita la
creación de elementos altamente personalizables, de aspecto profesional. Como hemos
indicado, el cuadro de diálogo que acabamos de crear tiene aplicado el tema predeter-
minado; este tema se mostrará desde ThemeRoller si no se proporcionan parámetros
personalizados. ,.
Generar un conjunto completamente diferente de estilos es cuestión de visitar ht tp : / /
ui . j query . com/ themeroller /, modificar las varias opciones según se desee, y pulsar
Figura 10.12. Mejorar nuestro cuadro de diálogo anterior.
el botón Download This Theme (Descargar esta temática). Un archivo. z ip de hojas de
estilo e imágenes luego se puede situar en el directorio apropiado. Por ejemplo, al elegir
algunos colores y texturas diferentes, podemos cambiar en unos minutos nuestro cuadro Formularios
de diálogo anterior para que se parezca a lo que muestra la figura 10.12.
Hemos investigado algunas formas de manipular formularios en un capítulo anterior.
Estos plug-ins pueden realizar tareas relacionadas con facilidad.
Otros plug-ins recomendados
Autocomplete
Además de los plug-ins descritos en este capítulo, los plug-ins listados más adelan-
te se recomiendan por los autores no sólo por su popularidad, sino también por tener http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete
código sólido. http://plugins.jquery.com/projeet/autoeompletex
mil 10. Utilizar plug-ins Aprende jQuery 1.3 lSiI
Escrito por el desarrollador jQuery [orn Zaefferer, el plug-in Autocomplete propor- El plug-in Jeditable (véase figura 10.15) convierte elementos no de formulario en
ciona una lista de coincidencias posibles a medida que el usuario escribe en una entrada entrada de datos editable cuando un usuario lleva a cabo alguna acción, como Un clie
de texto, como muestra la figura 10.13. o doble dic. El contenido cambiado se envía automátieamente para almacenarse en el
servidor. '
""-
Single City (local):
Month (local):
o Ed~pl dolor si! amel, oonsectetuer adipiscing eli!, sed diam nonummy nibh
magt'la aliquam eral volulp
E-Mail (local):
Single Bird (remate): Danvllle
Hidden input e dolor sit amel, oonsectetusr adipiscing elil, sed diam
'\~gna aliquam erat volulp
Single City (contaína): De Graff /'
Deflanee .¡;
e Lorem ipsum dolor sil amel, oonsecteluer adipiscing slil, sed diam nonumm
dolars mágna aliquam eral volulp
C!ur policy
Figura 10.16. Plug-in Masked input.
Figura 10.14. Plug-in Validation.
Tablas
jeditable
En un capítulo anterior, hemos tratado técnicas para organizar y mejorar datos ta-
http://www.appelsiini.net/projects/jeditable bulares. Muchos desarrolladores de plug-in han agrupado rutinas para ayudamos en
http://plugins. jquery. com/proj ect/j edi table estas tareas.
~F'i
Tablesorter Flexigrid
http://tablesorter.com/ http://code.google.com/p/flexigrid/
http://plugins.jquery.com/project/tablesorter http://ptugins . jquery. com/project/flexigrid
'~
El plug-in Tablesorter (véase la figura 10.17)puede convertir cualquier tabla con ele- Como jqGrid, Flexigrid es un plug-in de cuadrícula con todas las características.
mentos < t.he ad» y < t body» en una tabla que se puede ordenar sin refrescar la página. Algunos de éstas incluyen soporte JSON,paginación, búsqueda rápida, mostrar y ocul-
Lascaracterísticas especialesincluyen ordenar por múltiples columnas, analizadores para tar y cambiar el tamaño de columnas, y ordenar filas (véase la figura 10.19).
ordenar muchos formatos diferentes (por ejemplo, fecha, hora, moneda, URL), ordena-
ción secundaria "oculta",y ampliabilidad por medio de un sistema de widgets.
/
Liiot tIam'c';"-~-'[_[~¡;¡;;'-"'''~-~'''I:--''''i<;['~I!DIIIi;<;:t
....
-.!ih~~ )1:
..
1lI~;"-,"-~",_..-
/;:.r.l~.;~~~'1>~...¿I~~w~~,:..'~1.'\'(
A..- .•••.
~: •.•. ..•
~~._
"..,:,;.;'' '«..:..;6..''"' .•.,•••,~
Bruce Evans 22 $13.19 11% -100.9 Jan 18,20079:12NA
~ i*~'t-$~'~i' 46 . "$1~a·t9 ,,:/:< : '~.7," ,¡!;1;~~.f\
:ttt~ii~;{§.¡¡'~lil!2:i¡ól
9.=12.A!.t ..... . .,'tf~.i<.:
Clarlt Kanl 18 $15.89 44% ·26 Jan 12,200311:14AM
:Hcibd"""··::,,,.;t· as
~~~*¡K'¡'~\!í Ii' ,...... ~:11Í1.21l92,5'1~1~:'&~:-%M'O'¡'¡;i~;
_ P••••r 28 $9.99 20.9% -12.1 JuI6. 20068:14M\'"
jqGrid Imágenes
http://www.trirand.com/blog/ La manipulación de imagen es una tarea que a menudo requiere procesado intensivo
http://plugins.jquery.com/project/jqGrids del lado del servidor. Sin embargo, algunos autores de plug-in han desarrollado formas
Un control ]avaScript compatible con AJAX,jqGrid (véase la figura 10.18)permite a de realizar gestión sencilla de imagen en JavaScript utilizando jQuery como vehículo.
los desarrolladores representar dinámicamente y manipular datos tabulares en la Web.
Proporciona opciones para edición en línea, edición de celda, navegación de pagínador, Icrop
selección de múltiples elementos, sub-cuadrículas y cuadrículas en árbol. El plug-in
viene con una amplia documentación en http://www.secondpersonplural.ca/ http://deepliquid.com/content/Jcrop.html
jqgriddocs/. http://plugins.jquery.com/project/Jcrop
8 OJqG~d Examples Selec' Theme: oo'¡j¡¡,'rbli •••••••••• a [crop (véase la figura 10.20)ofrece una forma rápida y sencilla de añadir recorte de
re &. loadlng Data
m I Manlpulatlng
imagen a aplicaciones Web. Las características incluyen bloquear relación de aspecto,
8 a Advanced tamaño mínimo y máximo, vínculos de interacción y estilo personalizado.
CJ Multl Selea
c:aMaster Detall
~~rr4
t::JCrld as Subgrid
(?aReslzlng
Magnify
CY:fi1r.~*··'··
Q;jSearch BI9 Sets
lB a+New stnce beta 3.0 http://www.jnathanson.com/index.cfm?page=pages/jquery/magnify/
I!l a+ Row Edlting magnify
m 1+ Data Mapplng
m 1+lntegratlons
ti! rUve Data Manlpulatlon
1- i '!"
1:4
t:~::" .
lltem"4 ..
.."..~[:~...:¡~Z:~~.".·f}~~b~O ..
. 'LOO .........•..• !2OÓ.Oó •.......•.íioo.rió .-~
http://plugins.jquery.com/project/magnify
ID ,a+New In verslon 3.1
fB a+New In verslon 3.2
te a+New In verstcn 3.3 ~!;,~:r~~~~!~~:~~~~~~;~:~~=='"== Cuando se proporciona con una imagen proporcionalmente pequeña y grande, el
plug-in Magnify (véase la figura 10.21)generará una "lupa" como las que se utilizan co-
Figura 10.18. Pluq-in jqGrid. múnmente para el detalle del producto e imágenes de primer plano.
Ea 10. Utilizar plug-ins Aprende jQuery 1.3 mi
"
Thickbox
http://jquery.com/demo/thickbox/
Thickbox es un versátil plug-in lightbox que puede mostrar un sola imagen, múltiples
imágenes, contenido en línea, contenido e f r ame», o contenido servido por medio de
í
FancyBox
http://fancy.klade.lv/
Este clon de Lightbox enfatiza el estilo, con su apariencia al estilo Mac y un elegante
efecto de sombra. Además de mostrar automáticamente imágenes escaladas, el plug-in
FancyBox (véase la figura 10.22) puede mostrar contenido en línea o e í.f r ame». Figura 10.23. Plug-in Thickbox.
T" BIII
l!IiI 10. Utilizar plug-ins Aprende jQuery 1.3
BlockUI Flot
I
http://malsup.com/jquery/block/ http://cqde.google.com/p/flot/
http://plugins.jquery.com/project/blockUI http://plugins . j query. com/proj ect/flot
El plug-ín BlockUI simula comportamiento síncrono, sin bloquear el navegador.
I
El plug-in Flot utiliza el elemento « c anve s > para producir gráficos de conjuntos de
Cuando se activa, impedirá la interacción de usuario con la página (o parte de la página) I datos y opcionalmente modificar esos gráficos basándose en la interacción de usuario
I
hasta que se desactive (véase la figura 10.24). (véase la figura 10.26).Con la inclusión del script de traducción Excanvas, Flot puede
mostrar gráficos en Internet Explorer también, porque las instrucciones Canvas se con-
('vierten al formato VML propietario de Internet Explorer .
20
_ _ ._-'- I -+--'
I
___
l'
1---
.___
~.
.•
11I1
íJriited Stales
UnHed Kingdom
11I1
Denmark
• No
_rway
~ Russla
¡
15
Figura 10.24. Plug-in BlockUI.
jqModal
http://dev.iceburg.net/jquery/jqModal/
http://plugins.jquery.com/project/jqModal sL=:;r:~1< i=b:~ ~t
El plug-in jqModal es una solución ligera de cuadro de diálogo modal que también
es potente y flexible.Con un énfasis en ampliabilidad, deja mucha de la interacción a los 01 I I ! I ! ! I
desarrolladores Web que lo implementan (véase la figura 10.25). 1990 1992 1994 1996 1996 2000 2002 2004
Gráficas
Eventos
Como con la manipulación de imagen, las gráficas han sido tradicionalmente una
actividad del lado del servidor que requería gran cantidad de procesado. Los progra- Como hemos visto una y otra vez, jQuery proporciona una gran cantidad de herra-
madores creativos han desarrollado variasformas de crear gráficas en el navegador, y mientas para interceptar y reaccionar a eventos de usuario como clics del ratón y pul-
han agrupado estas técnicas en los plug-íns que se muestran aquí. saciones de teclado. Sin embargo, muchas opciones se ponen disponibles en la librería
&El 10. Utilizar plug-ins Aprende jQuery 1.3 I!D
principal aunque siempre habrá más técnicas avanzadas que explorar. Estos plug-ins
facilitan la implementación de algunos escenarios de evento menos comunes. Resumen
En este capítulo hemos examinadoformas en las que podemos incorporar plug-ins
de terceros en nuestras páginas Web. Hemos examinado el plug-in Form y jQuery DI
Mouse speed......1l..- y hemos listado algunos otros. En el siguiente capítulo, nos aprovecharemos de la ar-
quitectura plug-ín de jQuery para desarrollar algunos tipos diferentes de plug-ins por
Inllne ~Ilnegr$phs ~
nuestra cuenta.
Bar Cllar1S 111_.1. negativa values: .-'-'-
CompO$iI8 bar~
Discreta 111111111111111,11
Discreta with thrashold 1!IIIIUI
hoverlntent
http://cherne.net/brian/resources/jquery.hoverlntent.html
http://plugins.jquery.com/project/hoverlntent
El plug-in hoverIntent proporciona un solo método para ocup~r ellugar del método
. hover () cuando es importante impedir la activación accidental de animaciones cuando
el usuario mueve el ratón sobre o fuera de un elemento. Intenta determinar la intención
del usuario al monitorizar el cambio en velocidad del movimiento del ratón del usuario.
Este plug-in es especialmente efectivo cuando se utiliza con navegación desplegable.
live query
http://github.com/brandonaaron/livequery/
http://plugins.jquery.com/project/livequery
Como el método . 1i ve () incorporado de jQuery, el plug-in Live Query anexa di-
nárnicamente y mantiene vinculaciones de evento a elementos en el DOM, con indepen-
dencia de cuando se creen los elementos. El plug-in proporciona una implementación
alternativa que puede ser preferible en algunas situaciones.
r
/
"
11 • Desarrollar lejos. Cuando escribimos código que se podría reutilizar por otros, o incluso nosotros mis-
mos, lo queremos poder empaquetar como un nuevo plug-in. Afortunadamente, el pro-
ceso de desarrollo de un plug-in es más complejo que escribir el código que lo utiliza.
En este capítulo, tratamos cómo crear muchos tipos diferentes de plug-ins, desde
el más sencillo al más complejo. Empezaremos con plug-ins que simplemente ponen
plug-ins disponibles nuevas funciones globales, y pasaremos a tratar métodos del objeto jQuery
de varios tipos. También trataremos de ampliar el motor de selector jQuery con nuevas
expresiones, y concluiremos con algunos consejos sobre distribuir un plug-in para que
lo utilicen otros desarrolladores.
Si nuestro plug-in necesita proporcionar más de una función global, podríamos de- Con esta técnica (y un nombre de plug-in suficientemente único), estamos totalmente
clararlas independientemente: .' protegidos de colisiones de espacio de nombres en nuestras funciones globales.
jQuery.functionOne = function() (
alert(/This is a test. This is only a test./); ¿Qué sentido tiene?
i.
jQuery.functionTwo = function(param) (
alert(/The parameter is ,,/+ param + /"./); Ahora tenemos los fundamentos básicos del desarrollo de plug-in entre nuestros
}; trucos. Después de guardar nuestras funciones en un archivo denominado j query .
rnyplugin. j s, podemos incluir este script y utilizar las funciones desde otros scripts
Ahora, ambos métodos están definidos, por lo que podemos invocarlos en el modo en la página. Pero ¿en qué es esto diferente de cualquier otro archivo JavaScript que
normal: pudiéramos crear e incluir? Ya hemos tratado los beneficios del espacio de nombre de
$.functionOne();
situar nuestro código dentro del objeto j Query. Sin embargo, existe otra ventaja a escri-
$.functionTwo(/test/) ; bir nuestra biblioteca de función como una extensión jQuery: puesto que sabemos que
jQuery estará incluido, las funciones pueden utilizar jQuery.
También podemos emplear otra sintaxis al definir nuestras funciones, utilizando la
función $ . extend ():
jQuery. extend ((
functionOne: function() (
alert(/This is a test. This is only a test./);
Aunque se incluirá jQuery, no deberíamos asumir que el método abreviado $ está
r. disponible. Recuerde que el método $ . noCon fl i e t () puede abandonar el control de
functionTwo: function(param) ( este método abreviado. Para tener en cuenta esto, nuestros plug-ins siempre deberían
alert(/The parameter is "/ + param + /"./); invocar métodos jQuery utilizando j Query o definir internamente $, como se describe
}
});
más adelante.
return total¡
" La mayoría de la funcionalidad incorporada de jQuery se proporciona por medio
}; de sus métodos de objeto, y aquí es donde los plug-ins también destacan. Siempre que
escribimos una función que actúa sobre parte del DOM, es apropiado crear un método
Observe que aquí, hemos utilizado el método $ . each () para pasar por los valores . de objeto. .
de la tabla. Podríamos ciertamente utilizar un sencillo bucle for () aquí, pero puesto que
Hemos visto que añadir funciones globales requiere ampliar el objeto j Query con
podemos estar seguros de que la librería jQuery se ha cargado antes de nuestro plug-in,
nuevos métodos. Añadir métodos de instancia es similar, pero en su lugar ampliamos
podemos utilizar la sintaxis con la que estábamos cómodos.
el objeto j Query . fn:
Para probar nuestro plug-in, crearemos una sencilla página para mostrar las entra-
das y salidas de la función: jQuery.fn.myMethod = function()
a1ert(/Nothing happens./);
<body>
<p>Array contents:</p>
<ul id="array-contents"></ul>
<p>Array sum:</p>
<div id=lIarray-sum"></div>
</body>
El objeto j Query. fn es un alias de jQuery .prototype, proporcionado por conci-
Ahora escribiremos un pequeño script que anexa los valores de la tabla y la suma de sión.
tabla a los marcadores de posición que hemos creado.
$ (document) .ready(function() (
var myArray = [52, 97, 0.5, -22); Podemos invocar luego este nuevo método desde nuestro código después de utilizar
cualquier expresión selector:
$.each(myArray, function(index, va1ue)
$ (/#array-contents/) .append(/<li>/ + va1ue + /</li>/); $(/div/) .myMethod();
}) ;
Nuestra alerta se muestra cuando invocamos el método. Podríamos también haber
$ (/#array-sum/) .append($.sum(myArray)); escrito una función global, sin embargo, puesto que no hemos utilizado los nodos DOM
}) ;
coincidentes en ningún sentido. Una implementación de método razonable actúa sobre
Un vistazo a nuestra página HTML verifica que nuestro plug-in está funcionando su contexto.
correctamente, como muestra la figura 11.1.
Array contenta:
Contexto del método de objeto
• 52
• O, Dentro de cualquier método plug-in, se establece la palabra clave thi s en el obje-
• 0.5
• .Z¿ to jQuery actual. Por lo tanto, podemos invocar cualquier método jQuery incorporado
Atraysum: sobre this, o extraer sus nodos DOM y trabajar sobre ellos:
jQuery.fn.showA1ert = function() (
a1ert(/you se1ected / + this.1ength + / e1ements./);
Figura 11.1. Plug-in en funcionamiento.
lm1I 11. Desarrollar plug-ins
, Aprende jQuery 1.3 mil
r
Para examinar lo que podemos hacer con contexto de objeto, escribiremos un pe-
Pero algo está mal. Cuando hacemos clie en el botón, se le aplica a todas las filas la
queño plug-in para manipular las clases en los elementos coincidentes. Nuestro nuevo
clase that, como muestra la figura 11.3.
método tomará dos nombres de clase y alternará qué clase se aplica a cada elemento
con cada invocación.
jQuery.fn.swapClass = function(classl, class2) { • Lorom Ipsum dolor sIt ame'
if (this.hasClass (elassl)) ( • Comectetur .dlpJsJcJng 811t
• Sed do elusmod tempor incJdJdunf ufl4bor8
this.removeClass(elassl) .addClass(clasS2);
• MagnllS1lqUII
• ut 8lIim ad mJnIm VfJnJttm
Ahora podemos invocar nuestro método siempre que se haga clic sobre el botón:
$ (doeument) .ready(funetion () ( La palabra clave thi s hace referencia a un objetojQuery dentro del cuerpo del método de
$ (/#swap/) .eliek(funetion() { objeto, pero hace referencia a un elemento DOMdentro de la invocación . each ().
$ (fU/) .s.,apClass (fthis/, /that/);
return false¡
».
». Ahora cuando hacemos clie en el botón, las clases se intercambian sin afectar a los
elementos que no tienen aplicada ninguna clase, como muestra la figura 11.4.
'I-~~
j);
• ut lIfIim ad minlm venJINTI
• Ouis nostrud exerdtation ullamco
• Laborls n(sI ut alIqulp ex ea commodo
• Dula aute lrure dolor
€SwaP"'IBS~
Métodos transversales DOM
Figura 11.4. Alternarestilos. En algunos casos, podemos querer que un método plug-in cambie qué elementos DOM
se hacen referencia por el objeto jQuery. Por ejemplo, suponga que quisiéramos añadir un
método transversal DOM que encontrara los abuelos de los elementos coincidentes:
Encadenar métodos jQuery.fn.grandparent = function()
var grandparents = [];
Además de iteración implícita, los usuarios jQuery deberían poder basarse en encade- this.each(function() (
nar comportamiento. Esto significa que necesitamos devolver un objeto jQuery de todos grandparents.push(this.parentNode.parentNode);
j);
los métodos plug-in, a menos que el método esté claramente pensado para recuperar una
grandparents = jQuery.unique(grandparents);
información diferente. El objeto jQuery devuelto es normalmente el proporcionado como return this.setArray(grandparents);
this. Si utilizamos. each () para pasar por this, podemos devolvep su resultado: };
jQuery.fn.swapClass = function(classl, class2) ( Este método crea una nueva tabla grandparents, completándola al pasar por todos
return this.each(function() { los elementos actualmente referenciados por el objeto jQuery. La propiedad DOM es-
var $element = jQuery(this);
if ($element.hasClass(classl)) (
tándar . parentNode se utiliza para encontrar los elementos abuelo, que se pasan a la
$element.removeClass(classl) .addClass(class2); tabla grandparents. Esta tabla se despoja de sus duplicados con una llamada a $ . uni-
} que ( ) . Luego el método interno jQuery . setArray () cambia el conjunto de elementos
el se if ($element.hasClass(class2)) ( coincidentes en la nueva tabla. Ahora, podemos encontrar y operar sobre el abuelo de
$element.removeClass(class2) .addClass(classl);
}
un elemento con una sola llamada de método. Para comprobar nuestro método, confi-
}) ; guraremos una estructura <di v » profundamente anidada:
};
<div>Deserunt mollit anim id est laborum</div>
<div>Ut enim ad minim veniam
Previamente, cuando invocamos . swapClass () tuvimos que iniciar una nueva
<div>Quis nostrud exercitation
sentencia para hacer otra cosa con los elementos. Con la sentencia ret urn en su lugar, <div>Ullamco laboris nisi
sin embargo, podemos encadenar libremente nuestro método de plug-in con métodos <div>ut aliquip ex ea</div>
incorporados, como muestra la figura 11.5. <div class="targetll>Commodo consequat
<div>Lorem ipsum dolor sit amet</div>
</div>
• l.ot!m losum dolor stt amet </div>
• ConnqteItr 8dlpI4IcJng 81ft
.~
• Sed
• tN
do tlullDOd temPOl' tnddldunt
Commodo connquat
lot&m ipMIm dolor ait amet Este código debería resaltar los elementos abuelo, y luego ocultar los elementos des-
Ou •• utaiNredolor
, tino. Sin embargo, el efecto actual es que los abuelos se ocultan en su lugar, como mues-
In~,..hend.rit r tra la figura 11.8.
Involuptata
\lo" ••••
Deaerunl moml.,im id ..tl8borum
Cillumdolo,..
UI: enim ad minlm wniam
Fugl.t nuJl. ~riatur
Non proident
Exc.pléur sinl OCC8ecat cupidsla.l
_~~cut~·~¡.~_da
Non proidenl
Figura 11.6. Estructura anidada. El objeto jQuery almacenado en $target ha cambiado para hacer referencia al abue-
lo. Para evitar esto, tenemos que hacer que el método sea no destructivo. Esto es posible
Ahora podemos localizar los elementos abuelo de los elementos al utilizar nuestro por la pila interna que jQuery mantiene para cada objeto.
nuevo método:
jQuery. fn.grandparent ~ function()
$ (docurnent) .ready(function() { var grandparents ~ (J;
$(/.target/) .grandparent() .addClass(/highlight/); this.each(function() {
}l; grandparents.push(this.parentNode.parentNode) ;
}) ;
La clase highl ight pone en cursiva los elementos abuelo en la página, como mues- grandparents ~ jQuery.unique(grandparents);
tra la figura 11.7. return this.pushStack(grandparents);
};
Oeterunl mollit &f1im id ut laborum
Conwnodo cvnaequal
Como beneficio secundario, . pushStack () también permite que los métodos
lorem ipaum dotor ail amet . end () y . andSelf () funcionen como nuestro plug-in, de modo que podemos enca-
denar métodos juntos adecuadamente, como muestra la figura 11.10.
..,....-
Oui. aute inn dolor
Involuptale
$ (docurnent) .ready(function () {
$(/.target/) .grandparent() .andSelf() .addClass(/highlight/);
'Vela~
OUumdolore
}) ;
FLllilt.tnutl.~riatur
Nonproidenl
SlJJ'Illn culpa qui ofIicia Los métodos transversales DOM como. children () eran operaciones destructivas
en jQuery 1.0, pero han pasado a no destructivas en 1.1.
Figura 11.7. Localizarelementos abuelo en la estructura.
';'1'
;'
1
--,
Sunlinculpa.~¡~
I ExceplMJr ..,1 occaacat Cllpic!IIIat.
I Podemos crear un trío de métodos abreviados para llevar a cabo esta animación cuan-
do mostramos y ocultamos elementos:
jQuery.fn .slideFadeOut = function() (
Figura 11.9.Objetosdestinoocultos. return this.animate({
height: /hide/,
o."Nnl room. anim id eat r.borurn opacity: /hide/
lit enim ad m¡nim wniam
}) ;
Qu;.noattud~tioIt };
Ullamco labori. niai
VeIit ••••
jQuery.fn.slideFadeToggle = function() (
Ollum doIore
return this.animate({
,FusI"'nulMplJ~
height: /toggle/,
ExoeptMJr ainloceaecet cuptdatat opacity: /toggle/
---~-;-:..;.
-~~;~':::;':=~:;;;
..~-~.~~: }) ;
Non_
};
Slmlln CU'P- quí offioia
return false; })
));
.appendTo(!body!) ;
$ (!#toggle!) .click(function() { }
$(!p!) .slideFadeToggle(!slow!) ; ));
return false; };
}) ;
});
Para cada elemento en el que se invoca este método, realizamos una serie de dones
del elemento, ajustando su opacidad. Estos dones se posicionan de forma absoluta, en
y la animación ocurre según lo esperado, como se muestra en la figura 11.11. distintos desplazamientos desde el elemento original.
lID 11. Desarrollar plug-ins
Aprende jQuery 1.3 mi
Como siempre, comprobaremos nuestro plug-in utilizando algo de HTML sencillo,
como se ve en la figura 11.12. Nuestro nuevo parámetro funciona según lo anticipado, la sombra es mayor, utili-
zando dos veces más trozos que antes, pero la interfaz del método es menos que ideal,
como se ve en la figura 11.14.Estos tres números se confunden fácilmente, y su orden
I The quick brown fox jumps over the lazy dogo I no se puede deducir lógicamente. Seria una mejora etiquetar los parámetros, en bene-
Figura 11.12. Probar nuestro ejemplo. ficio de la persona que escribe la llamada de método, y cualquiera que más tarde desee
leer e interpretarlo.
<body>
<hl>The
<!body>
quick brown fox jumps over the 1azy dog.<!hl>
Por el momento, nuestro plug-in no toma parámetros, de modo que invocar el mé-
I-.••.••
·--~---, I
Figura 11.14. La sombra es mayor.
todo es sencillo, como se muestra en la figura 11.13.
Il\'IA'~~~lt~tl""\ttA 1
Mapas dé parámetro
Figura 11.13. Invocar el método. Hemos-visto muchos ejemplos en la API jQuery de mapas que se proporcionan corno
parámetros de método. Ésta puede ser una forma más amigable de exponer opciones a
$Idocument) .ready(functionl) un usuario de plug-ins que la sencilla lista de parámetros que hemos utilizado. Un mapa
$(!hl!) .shadow();
}) ;
proporciona una etiqueta visual para cada parámetro, y también hace que el orden de
los parámetros sea irrelevante. Además, siempre que podamos simular la API jQuery
en nuestros plug-ins. deberíamos hacerlo para aumentar consistencia y por lo tanto fa-
Parámetros sencillos cilidad de uso .
. jQuery.fn.shadow = function(opts) {
Ahora podemos incorporar algo de flexibilidad en el método plug-in. La operación return this.eachlfunction() {
del método se basa en varios valores numéricos que el usuario podría querer modificar. var $origina1E1ement = jQuery(this);
Podemos convertir éstos en parámetros de modo que se pueden cambiar bajo demanda. fer (var i = o; i < opts.slices; i++)
$origina1E1ement
jQuery.fn.shadow = function(slices, opacity, zIndex) { .c1one()
return this.each(function() { .css({
var $origina1E1ement = jQuery(this); position: !absolute!,
for (var i = o; i < slices; i++) { 1eft: $origina1E1ement.offset() .1eft + i,
$origina1E1ement top: $origina1E1ement.offset() .top + i,
.c1one() margin: o,
.css({ zIndex: opts.zlndex,
position: !abso1ute!, opacity: opts.opacity
1eft: $origina1E1ement.offset() .1eft + i, })
top: $origina1E1ement.offset() .top + i, .appendTol!body!);
margin: o, }
zIndex: zIndex, j) ;
opacity: opacity };
})
.appendTo(!body!); Todo lo que hemos cambiado para permitir nuestra nueva interfaz es la forma en que
}
});
se referencia cada parámetro; en lugar de tener un nombre de variable aparte, se accede
}; a cada valor como una propiedad del argumento opts a la función.
Invocar este método ahora requiere un mapa de valores en lugar de tres números
Ahora, cuando invocamos nuestro método, debemos proporcionar estos tres valores: individuales (véase figura 11.15).
$ (document) .ready (function 1)· {
$ (document) .ready(functionl)
$ I!hl!) .shadow(lO, 0.1, -1);
j); $(!hl!) .shadow({
slices: 5,
'ifr~f¡r~i'
ID 11. Desarrollar plug-ins
Aprende jQuery 1.3 DI
opac t y . 0.25,
í
zIndex: -1
Ilb~cty'~1$¡b..r,o~tQX.tw.n~ q1(el1 ~Q l~ ~-QQ-.I
)) ; Figura 11.16. Uso de valores predeterminados.
»;
Seguimos invocando nuestro método utilizando un mapa, pero ahora podemos espe-
La finalidad de cada parámetro es ahora obvia por un rápido vistazo a la llamada cificar solamente los parámetros que queremos que difieran de sus predeterminados:
de método.
$ (document) .ready(function()
)) ;
opac i ty . 0.05
Aquí, hemos definido un nuevo mapa, denominado defaults, dentro de nuestra return this.each(function() (
definición de método. La función de utilidad $ . extend () nos permite tomar el mapa var $originalElement = jQuery(this) ;
options facilitado como un argumento y utilizarlo para anular los elementos en de- tor (var i = Di i < opts.slices; i++)
var offset ='opts,sliceOffset(i);
faults, dejando los elementos omitidos solos, como muestra la figura 11.16.
mi 11. Desarrollarplug-ins Aprende jQuery 1.3 lID
$originalElement personalizar estos predeterminados podría reducir significativamente la cantidad de
.elone ()
.ess ({
código que se'tiene que escribir .
position: /absolute/, Para hacer que los predeterminados sean personalizables, necesitamos moverlos
left: $originalElement.offset().left fuera de nuestra definición de método y hacia una ubicación que es accesible por códi-
+ offset.x, go de fuera:
top: $originalElement.offset() .top
+ offset.y,
jQuery.fn.shadow = funetion(options) {
margin: O,
var opts = jQuery.extend({},
zIndex: opts.zlndex,
jQuery.fn.shadow.defaults, options);
opaeity: opts.opaeity
}) ,.. return this.eaeh(funetion() (
.appendTo(/body/) ;
var $originalElement = jQuery(this);
}
for (var i = o; i < opts.slices; i++)
}) ;
var offset = opts.slieeOffset(i);
};
$originalElement
.elone()
Cada tramo de sombra tiene un desplazamiento diferente desde el texto original. .ess({
Antes, este desplazamiento ha sido simplemente igual al índice del tramo. Ahora, sin position: /absolute/,
embargo, estamos calculando el desplazamiento utilizando la función s li c;eOf f s e t ( ) , left: $originalElement.offset() .left + offset.x,
topo $originalElement.offset() .top + offset.y,
que es un parámetro que el usuario puede anular. Por lo tanto, por ejemplo, podríamos margin: O,
proporcionar valores negativos para el desplazamiento en ambas dimensiones: zIndex: opts.zlndex,
opaeity: opts.opaeity
$ (doeum ent) .ready(funetion() (
})
$(/hl/) .shadow({
.appendTo(/body/);
slieeOffset: funetion(i) {
}
return {x: -i, y: -2*i}; }) ;
} };
});
}) ;
jQuery.fn.shadow.defaults
elices: 5,
Esto hará que la sombra se sitúe hacia arriba y a la izquierda, como muestra la figura opacity: 0.1,
11.17, en lugar de hacia abajo y a la derecha. zIndex: -1,
sliceOffset: function(i)
return {x: i, y: i};
};
Figura 11,17. Utilizar valores negativos.
Los predeterminados están ahora en el espacio de nombre del plug-in de sombra, y
La rellamada permite modificaciones sencillas en la direcciÓn de la sombra, o posi- se pueden hacer referencia directamente con $ . f n . shadow . de f a u 1t s. Nuestra llama-
cionamiento mucho más sofisticado si el usuario del plug-in proporciona la rellamada da a $ . extend () tenía que cambiar para dar cabida a esto también. Puesto que ahora
apropiada. Si la rellamada no se especifica, entonces el comportamiento predeterminado estamos reutilizando el mismo mapa defaul ts para cada llamada a . shadow (), no
se utiliza una vez más. podemos permitir que $ . extend () lo modifique. En su lugar, proporcionamos un
mapa vacío () como el primer argumento a s , extend (), y es este nuevo objeto el que
se modifica.
Predeterminados personalizables Ahora el código que utiliza nuestro plug-in puede cambiar los predeterminados que
utilizarán todas las siguientes llamadas a . shadow (). Las opciones también se pueden
Podemos mejorar la experiencia de utilizar nuestros plug-ins al proporcionar valores
proporcionar al mismo tiempo que se invoca el método.
predeterminados razonables para nuestros parámetros de método, como hemos visto.
Sin embargo, algunas veces puede ser difícil predecir que será un valor predeterminado $ (doeument) .ready(function() {
razonable. Si un script invocara nuestro plug-ín múltiples veces con un conjunto dife- $.fn.shadow.defaults.sliees = 10;
rente de parámetros que hemos establecido como predeterminados, la posibilidad de $(/hl/) .shadow({
lmI 11. Desarrollarplug-ins Aprende jQuery 1.3 _
sliceOffset: function(i)
return va1ue >= parselnt(parts[3]);
return {x: -i, y: i};
cas~ />/:
}
return va1ue > parselnt(parts[3] I;
}) ;
». }
}) ;
Este script creará una sombra con 10 tramos, porque éste es el nuevo valor prede-
terminado, pero también situará sombra a la izquierda y hacia abajo, debido a la rella- Este código le dice a jQuery que css es una cadena válida que puede seguir a dos
mada sliceOffset que se proporciona junto con la llamada de método, como se ve puntos en una expresión de selector, y que cuando se encuentra, la función dada se debe-
en la figura 11.18. ría invocar para determinar si el elemento se debería incluir en el conjunto resultado.
Se pasan cuatro parámetros a la función que se evalúa aquí:
/
I"~"""-I
Figura 11.18. Utilizar el valor predeterminado en la sombra.
•
•
el ement: El elemento DOM bajo consideración. Es necesario para la mayoría de
selectores.
index: El índice del elemento DOM dentro del conjunto resultado. Esto es de
utilidad para selectores como: eq () y : I t ( ) .
Añadir una expresión de selector • matches: Una tabla que contiene el resultado de la expresión regular que se
ha utilizado para analizar este selector. Típicamente, matches [3] es el único
elemento relevante en la tabla; en un selector de la forma: a (b), el elemento
Las partes incorporadas de jQuery se pueden ampliar también. En lugar de añadir matches [3] contiene b, el texto dentro de los paréntesis.
nuevos métodos, podemos personalizar los existentes. Un deseo común, por ejemplo, es
ampliar las expresiones de selector proporcionadas por jQuery para proporcionar más • set: El conjunto entero de elementos DOM que ha coincidido hasta este punto.
opciones esotéricas. Este parámetro no suele ser necesario.
El tipo más sencillo de expresión de selector a añadir es una pseudo-clase; éstas son Los selectores de pseudo-clase necesitan utilizar la información contenida en estos
las expresiones que empiezan con dos puntos, como: checked o :nth - chi Id ( ) . Para cuatro argumentos para determinar si el elemento pertenece o no al conjunto resultado.
ilustrar el proceso de crear una expresión de selector, crearemos una pseudo-clase de- En este caso, eIement y matches es todo lo que necesitamos.
nominada : css (). Este nuevo selector nos permitirá localizar elementos basándonos En nuestra función selector, primero desglosamos el selector en partes que se pue-
en los valores numéricos de sus atributos CSS. den utilizar con una expresión regular. Queremos un selector como: css (width <
Cuando utilizamos una expresión de selector para encontrar elementos, jQuery 200) para devolver todos los elementos con un width de menos de 200. Por lo tanto
busca instrucciones en un mapa interno denominado expr. Este mapa contiene código necesitamos examinar el texto dentro de los paréntesis para recuperar el nombre de la
JavaScript a ejecutar sobre un elemento, haciendo que el elemento se encuentre conte- propiedad (w í.dt.h), operador de comparación (e), y valor con el que comparar (200).
nido en el conjunto resultado si el código evalúa en true. Podemos añadir nuevas ex- La expresión regular / ( [\ w-] +) \ s * ( [<>=] +) \ s * (\ d-s ) / realiza esta búsqueda, si-
presiones a este mapa utilizando la función $ . extend ( ) . ." tuando estas tres partes de la cadena en la tabla parts para nuestro uso.
jQuery.extend(jQuery.expr[/:/], ( A continuación, necesitamos ir a buscar el valor actual de la propiedad. Podemos
/css/: function(e1ement, index, matches, set) ( utilizar el método. css () de jQuery para devolver el valor de la propiedad que se ha
var parts = /([\W-]+)\s*([<>=l+)\s*(\d+)/ nombrado en el selector. Puesto que esta propiedad se devuelve como una cadena, utiliza-
.exec(matches[3]) ;
mos parseFIoat () para convertirlo en número. Por último, realizamos la comparación.
var va1ue = parseF1oat(jQuery(e1ement) .css (parts [1] 11;
Una sentencia swi tch determina qué tipo de comparación se realiza dependiendo del
switch (parts[2]) ( contenido del selector, y se devuelve el resultado de la comparación (true o false).
case /</: Ahora tenemos una nueva expresión de selector que podemos utilizar en cualquier
return va1ue < parselnt(parts[3]);
lado en nuestro código jQuery. Un sencillo documento HTML puede demostrar esto,
case /<=/:
return va1ue <= parselnt (parts [3] I ;
como se ve en la figura 11.19.
case /=/:
<body>
case /==/:
<div>Deserunt mollit anim id est laborum</div>
return va1ue == parselnt(parts[3]);
<div>Ullamco</div>
case />=/:
<div>Ut enim ad minim veniam laboris</div>
"EJ»'"
lIImI 11. Desarrollar plug-ins Aprende jQuery 1.3 l1mI
<div>Quis nostrud exercitation consequat nisi</div>
<div>ut aliquip</div> su código. Antes de esto, sin embargo, deberíamos aseguramos de que el plug-in está
<div>Cornmodo</div> preparado paré;!el público.
<div>Lorem ipsum dolor sit amet ex ea</div> Existen algunas reglas a seguir al escribir plug-ins para que funcione bien con otro
</body>
código. Ya hemos tratado algunas de ellas, pero las recopilamos aquí por comodidad.
Convenciones de nombrado
1~~~,~g,i~~~~.~~J Todos los archivos de plug-in se deberían nombrar jQuery. myPlugin. j s donde.
h ..~ .." , , myPlugin es el nombre del plug-in. Dentro del archivo, todas las funciones globales se
[~;=j~~~'~~ l deberían agrupar en un objeto denominado j Query .myPl ugin, a menos que haya sola-
~~~~ mente uno, en cuyo caso puede ser una función denominada j Query . myPl ugin ( ) .
Los nombres de método son más flexibles, pero se deberían mantener tan únicos
I~J _ como sea posible. Si solamente se define un método, se debería denominar j Query .
~!~~~~,t~~~.~,~! fn. myPl ugin () . Si se define más de uno, intente prefijar cada nombre de método con
el nombre del plug-in para impedir confusión. Evite nombres cortos y ambiguos como
Figura 11.19. Documento HTML. . load () o . get () que se pueden confundir con métodos definidos en otros plug-
" ins.
Con nuestro nuevo selector, se hace trivial resaltar los elementos más pequeños en
esta lista, como muestra la figura 11.20.
Uso del' alias $
-
[il~~I.f~~J"E~_~·;1
~;-;~~~~~~~:I
Los plug-ins jQuery pueden no asumir que el alias $ se encuentra disponible. En su
lugar, se debe escribir el nombre j Query completo cada vez.
En plug-ins más extensos, muchos desarrolladores encuentran que la ausencia del
--
método abreviado $ hace que el código sea más difícil de leer. Para combatir esto, el
&~~~"¡'~~l¡¡ij método abreviado se puede definir localmente para el ámbito de aplicación del plug-in
al definir y ejecutar una función. La sintaxis para definir y ejecutar una función a la vez
se parece a esto:
II'J,~,~~!N~_~~~*~I (function($)
// Código
(
va aqui
}) (jQuery) ;
Figura 11.20. Utilizar el nuevo selector.
La función toma un solo parámetro, al que pasamos el objeto global jQuery. El pa-
$ (document) .ready(function() (
$ (/div:css(width < 1001/) .addClass(/highlight/);
rámetro se nombra $, por lo que dentro de la función podemos utilizar el alias $ sin
}) ; conflictos.
Interfaces de método
Compartir un plug-in con el mundo
Todos los métodos jQuery se invocan dentro del contexto de un objeto jQuery, de
Una vez completo un plug-in, podemos querer publicado de modo que otros puedan modo que thi s hace referencia a un objeto que puede hacer referencia a uno o más ele-
beneficiarse, y posiblemente mejorar, el código. Podemos hacer esto en el repositorio mentos DOM. Todos los métodos deben comportarse correctamente con independencia
de plug-íns jQuery oficial en http://plugins.jquery . com/. Aquí podemos regis- del número de elementos coincidentes. En general, los métodos deberían invocar thi s .
tramos, y seguir las instrucciones para describir el plug-in y subir un archivo. zip de each () para pasar por los elementos coincidentes, operando sobre cada uno en turno.
mi 11. Desarrollar plug-ins
, /
Estilo de la documentación
La documentación archivada se debería anexar delante de cada definición de
función o método en formato ScriptDoc. Este formato se documenta en ¡Ú:tp: //www.
scriptdoc.org/.
Resumen
En este último capítulo, hemos visto cómo la funcionalidad que proporciona jQuery
no necesita limitar las posibilidades de uso de la librería. Los plug-ins que se encuentran
disponibles amplían el menú de características substancialmente, y podemos fácilmente
crear el nuestro propio que amplía aún más los límites.
Los plug-ins que hemos creado contienen varias características, incluidas funciones
globales que utilizan la librería jQuery, nuevos métodos del objeto jQuery para actuar
sobre elementos DOM, métodos ampliables que se pueden personalizar fácilmente, y
expresiones de selector mejoradas para encontrar elementos DOM en nuevas maneras.
Con estas herramientas a nuestra disposición, podemos configurar jQuery, y nuestro
propio código JavaScript, en la forma que queramos.
f>W
I
I
!
,,
,..
Los siguientes recursos representan un punto de partida para aprender más sobre
jQuery, JavaScript, y desarrollo Web en general, más allá de lo que se trata en este libro.
Apéndice A
Existen demasiados recursos de información de calidad en la Web para que este apéndice
pueda llegar a ser algo parecido a una lista exhaustiva. Además, aunque otras publicacio-
nes impresas también pueden proporcionar información valiosa, no se señalan aquí.
Documentación jQuery
Recursos online Estos recursos ofrecen referencias y detalles sobre la librería jQuery.
Wiki jQuery
La documentación en j query . com está en la forma de un wiki, lo que significa que
el contenido es editable por el público. El sitio incluye la API jQuery, tutoriales, guías
de inicio y mucho más:
http://docs.jquery.com/
API jQuery
Además de la documentación oficial en j query . com, la API está disponible en la
siguiente ubicación:
http://remysharp.com/jquery-api/
mi Apéndice A. Recursos online
1"
Aprende jQuery 1.3 liD
Este navegador de API diseñado por Yehuda Katz, y actualizado por Remy Sharp,
/ Quirksmode
es tanto bonito como apropiado. También proporciona visualización rápida de métodos
El sitio Quirksmode de Peter-Paul Koch es un extraordinario recurso para entender las
para un número de plug-ins jQuery:
diferencias en la forma en que los navegadores implementan varias funciones JavaScript,
http://www.visualjquery.com/ así como muchas propiedades CSS:
http://www.qúirksmode.org/
Visor jQueryAPI Adobe AIR
Remy Sharp ha incluido la API jQuery en una aplicación Adobe AIR para visualiza- JavaScript Toolbox
ción cuando se está desconectado:
JavaScript Too1boxde Matt Kruse ofrece una gran variedad de librerías JavaScript, así
http://remysharp.com/downloads/jquery-api-browser.air.zip como consejo sobre mejores prácticas JavaScript y una colección de recursos JavaScript
examinados en otra parte en la Web:
http://www.javascripttoolbox.com/
Referencia JavaScript
Estos sitios ofrecen referencias y guías a JavaScript como lenguaje en general, en lugar
de jQuery en particular. Compresores de código JavaScript
Centro de desarrollo Mozilla Cuando se dan los últimos toques a un sitio, a menudo es aconsejable comprimir el
código JavaScript. Este proceso reduce el tiempo de descarga para todos los usuarios
Este sitio tiene una exhaustiva referencia JavaScript, una guía para programar con del sitio.
JavaScript, vínculos a herramientas de utilidad, y mucho más:
Pretty printer (
. Position is everything
Esta herramienta embellece JavaScript que se ha comprimido, restaurando los saltos Este sitio incluye un catálogo de errores de navegador en CSS junto con explicacio-
de línea y sangrando donde es posible. Proporciona una serie de opciones para adaptar nes de cómo solucíonarlos:
los resultados:
http://www.positioniseverything.net/
http://www.prettyprinter.de/
Blogs de utilidad
Referencia (X)HTML
Nuevas técnicas y características se están desarrollando e incorporando continuamente
La librería jQuery se encuentra en su mejor momento cuando trabaja con documentos 'o, para cualquier tecnología viva. Mantenerse al tanto de las innovaciones puede ser fácil
semánticos HTML y XHTML, con forma to apropiado. El recurso a continuación propor- al comprobar estas fuentes de noticias de desarrollo Web de vez en cuando.
ciona asistencia con estos lenguajes de marcación.
learning jQuery
Referencia CSS
Karl Swedberg dirige este blog para tutoriales, técnicas y anuncios jQuery. Autores
Los efectos y animaciones que hemos visto una y otra vez se basan en el potencial invitados incluyen miembros del equipo jQuery como Mike Alsup y Brandon Aaron:
de las hojas de estilo en cascada. Para incorporar los detalles visuales que deseamos en
http://www.learningjquery.com/
nuestros sitios, podemos necesitar recurrir a estos recursos CSS para orientación.
La página principal CSS de W3C proporciona vínculos a tutoriales, especificaciones, Este blog actualizado con frecuencia iniciado por Dion Almaer y Ben Galbraith
suites de prueba, y otros recursos: proporciona una tremenda cantidad de noticias y características, y el tutorial sobre
JavaScript:
http://www.w3.org/Style/cSS/
http://ajaxian.com/
lIiI Apéndice A. Recursos online Aprende jQuery 1.3 lIiiI
• Textpattem: http://www.textpattern.com/
• Trac:http://trac.edgewall.org/
• WordPress: http://wordpress .org/
• Z-Blog:http://www.rainbowsoft.org/zblog .
Para una lista completa, visite la página Sites Using jQuery en: http://docs .
jquery.com/Sites_Using_jQuery.
•
.
'
r' ,,
·'
de desarrollo
y bien consideradas.
firebug
La extensión Firebug para Firefox es indispensable para desarrollo jQuery:
http://www.getfirebug.com/
Algunas de las características de Pírebug son:
• Un excelente inspector DOM para encontrar nombres y selectores para partes del
documento.
<,
'r'!
Comprobador de expresiones regulares Pérdidas de memoria en código JavaScript pueden causar problemas de rendimien-
, to y estabilidad para Internet Explorer. Drip ayuda a detectar y aislar estos problemas
Las expresiones regulares para hacer coincidir cadenas en JavaScript son difíciles de de memoria:
diseñar. Esta extensión para Firefox permite experimentación sencilla con expresiones http://Sourceforge.net/projects/ieleak/
regulares utilizando una interfaz para incorporar texto de búsqueda:
Para aprender más sobre las pérdidas de memoria Explorer, consulte el apéndice C.
http://sebastianzartner.ath.cx/new/downloads/RExT/
Aunque tiene una cuota de mercado limitada como navegador de escritorio, Opera
desempeña un papel importante en sistemas incorporados y dispositivos móviles, y sus Charles
posibilidades de uso se deberían considerar cuidadosamente en el desarrollo Web.
Cuando se desarrollan aplicaciones intensivas en AJAX, puede ser de utilidad ver
exactamente qué datos se envían entre el navegador y el servidor. El proxy de depura-
Dragonfly ción Web Charles muestra todo el tráfico HTTP entre dos puntos, incluidas peticiones
Web normales, tráfico HTTPS, y respuestas AJAX:
Aunque todavía en sus primeras etapas, Dragonfly es un prometedor entorno de de-
puración para navegadores Opera en ordenadores y dispositivos móviles. El conjunto http://www.xk72.com/charles/
de características de Dragonfly es similar al de Firebug, incluida depuración JavaScript,
así como inspección y edición CSS y DOM.
Fiddler
Fiddler es otro proxy de depuración HTTP de utilidad con características similares a
Otras herramientas las de Charles. Según su sitio, Fiddler "incluye un potente subsistema de script basado
Aunque las herramientas anteriores se centran en un navegador específico, estas uti- en eventos, y se puede ampliar utilizando cualquier lenguaje .NET":
lidades tienen un ámbito de aplicación más amplio. http://www.fiddlertool.com/fiddler/
NitobiBug
Como Firebug Lite, NitobiBug es una herramienta multiplataforma que trata algunas
de las mismas cosas que el más robusto y refinado Firebug. Su fortaleza se encuentra
"'0 \ ~ \ ,"" .'~,' l' ~
!l3
~ li.I '~ ' /@J ?~ l~,:~
"~
""" \.~ ". r'"'\
~
iWt .~ ';':'~,-'''''~i;- ):!\ 15'. ,/ -@
®\
'~,
'\'4;
, .... ~"'"
®,· ~..
:'.-'::'
fr·· -',
»e» /;.~ . \ ~\ ,f,1),.,/ :")
@)
,
f;.
~
o ,~, @'
L.~ .~..
..-
~
.. ~; __ .
. 7$\'~'
\~
'
'""-
..-¡ .•..."
~_~.
.-,:'
,",
-
~;{"~
'
,~
"i'i',
0°.- .... 1@" 13" '~
O
", "<!I¡, '~ t®@1'~ ® /
~,"~\
.".
~
-, ()
'~
'.
,..-,.
.. _-~
- \!J '" ~
~
'.1
,~
'..
~-._' / i\
~:.,'" ~, ~',o:~' '\
.'
En este libro hemos visto muchos métodos jQuery que toman funciones como pa-
rámetros. Nuestros ejemplos han creado, invocado y pasado funciones una y otra vez.
Aunque podemos hacer esto con solamente un conocimiento somero de la mecánica
Apéndice e
interna JavaScript, a veces los efectos secundarios de nuestras acciones pueden parecer
extraños si no tenemos conocimiento de las características del lenguaje. En este apéndice,
estudiaremos una de las construcciones basadas en funciones más esotéricas denomi-
nada c/osures. Nuestra explicación implicará muchos pequeños ejemplos de código, con
los que queremos mostrar un conjunto de mensajes. En lugar de utilizar un mecanismo
de registro específico de un navegador (como console. Loq() de Firefox), o crear una
JavaScript serie de cuadros de diálogo alert (), utilizaremos un pequeño método de plug-in:
jQuery.fn.print = function(message)
return this.each(function()
$(rcdiv class="result"
{
/>')
,text(String(message))
Closures
.appendTo($(this) .find(' .results'));
}) ;
};
Funciones internas
J avaScript es afortunado de incluirse entre los lenguajes de programación que sopor"
tan declaraciones de funciones internas, Muchos lenguajes de programación tradicio-
nales, como C, recopilan todas las funciones en un único ámbito de nivel superior. Los
lIiIiI Apéndice C. JavaScript Closures Aprende jQuery 1.3 mi
lenguajes con funciones internas, por otro lado, nos permiten reunir pequeñas funciones funciones padre. JavaScript, por otro lado, nos permite pasar funciones como si fueran
de utilidad donde son necesarias, evitando la polución del espacio de nombres. cualquier otro tipo de datos. Esto significa que las funciones internas pueden escapar
Una función interna es simplemente una función que se define dentro de otra fun- de sus captores.
ción. Por ejemplo: La ruta de escape puede resultar en muchas direcciones diferentes. Por ejemplo, su-
ponga que la función se asigna a una variable global:
function outerFn() {
function innerFn() var globalVar;
function outerFn() { ,
$('#example-3 .print('Outer function
1) 1);
Aquí, innerFn () es una función interna, contenida dentro del ámbito de outer- function innerFn() {
Fn (). Esto significa que una llamada a innerFn () es válida dentro de outerFn (), '/ $ ( #example-3 I ) . print ( 'Inner function 1) ;
I
Observe que una llamada a innerFn () desde fuera de outerFn () sigue devolvien-
innerFn() ;
}
do un error. Aunque la función ha escapado por medio de la referencia almacenada en
$ ('#example-2') .print('outerFn(), '); . la variable global, el nombre de la función sigue atrapado dentro del ámbito de aplica-
outerFn() ; ción de outerFn () .
Una referencia de función también puede encontrar su camino fuera de una función
Esto tiene este resultado: padre por medio de un valor de retorno:
outerFn() ,
function outerFn() {
Outer function
$('#example-4') .print('Outer function l
);
Inner function
function innerFn() {
$('#example-4') .print('Inner function');
Esta técnica es especialmente útil para pequeñas funciones de una sola finalidad. Por
ejemplo, los algoritmos que son recursivos, pero tienen un envoltorio API no recursivo, return innerFn;
a veces se expresan mejor con una función interna como elemento de ayuda. }
$('#example-4') .print('var fnRef = outerFn(), ');
var fnRef = outerFn();
La trama se complica cuando las referencias de función entran en juego. Algunos Aquí, no hay variable global modificada dentro de ou t erFn ( ) . En su lugar, ou ter-
lenguajes, como Pascal, permiten el uso de funciones internas para la finalidad de Fn () devuelve una referencia a innerFn () . La llamada a outerFn () resulta en esta
ocultar código solamente; esas funciones están para siempre enterradas dentro de sus referencia, que se almacena y se invoca en turno, activando el mensaje de nuevo:
l1iPI Apéndice C. javaScript Closures Aprende jQuery 1.3 1m
var fnRef = outerFn(): ~nRef();
Outer function fnRef();
fnRef () : var fnRef2 o{,terFn() ;
Inner funetion fnRef2 ();
fnRef2() ;
Elhecho de que las funciones internas se puedan invocar por medio de una referencia
incluso después de que la función está fuera de ámbito, significa que JavaScript necesi- Ahora nuestra función incrementará la variable con cada llamada:
ta mantener disponibles funciones referenciadas siempre y cuando se puedan invocar. globalVar = 1
Cada variable que hace referencia a la función se registra por el tiempo de ejecución globalVar = 2
JavaScript, y una vez que ha desaparecido la última, el recolector de basura JavaScript globalVar = 3
globalVar = 4
viene y libera un poco de memoria. /
Pero ¿qué pasa si la variable es local a la función padre? Puesto que la función in-
Ámbito de aplicación de variables terna hereda el ámbito de aplicación de su padre, también se puede hacer referencia a
esta variable:
Las funciones internas pueden por supuesto tener sus propias variables, que están function outerFn()
restringidas en ámbito de aplicación en la propia función: var outerVar = o;
function innerFn()
function outerFn() ( outerVar++¡
"
function innerFn() $(I#example-7 .print('outerVar
1) r + outerVar) i
var innerVar = Oi
innerVar++i return innerFn¡
$('#example-5 .print('innerVar
1) 1 + innerVar);
v r fnRef = outerFn{);
1
fnRef2 (); Ahora nuestras llamadas de función tienen comportamiento más interesante:
fnRef2() ;
outerVar = 1
Cada vez que se invoca la función, por medio de una referencia u alguna otra cosa, outerVar = 2
se crea una nueva variable innerVar, incrementada, y se muestra: outerVar = 1
outerVar = 2
innerVar :::;
1
innerVar = 1 Obtenemos una mezcla de los dos efectos anteriores. Las llamadas a Lrme r Pn () por
innerVar = 1 medio de cada referencia incrementan outerVar independientemente. Observe que la
innerVar = 1
segunda llamada a outerFn () no vuelve a establecer el valor de outerVar, sino que
Las funciones internas pueden hacer referencia a variables globales en la misma forma crea una nueva instancia de outerVar, vinculada al ámbito de aplicación de la segunda
que cualquier otra función: 'lamada de función. El resultado de esto es que después de las llamadas anteriores, otra
llamada a fnRef () imprimirá el valor 3, y una llamada siguiente a fnRef2 () también
var globalVar = Oi imprimirá 3. Los dos contadores están completamente separados. Cuando una referencia
function outerFn()
a una función interna encuentra su camino fuera del ámbito de aplicación en el que se
function innerFn()
globalVar++;
definió la función, esto crea un closure en esa función. Llamamos a las variables que no
$('#example-6') .print('globalVar , + global Var) ; son ni parámetros ni locales a la función interna variables libres, y el entorno de la llama-
da d' función exterio' las cierra' Esenc'alment', el hecho de'que la func'ón haga'refere'cia a
return innerFn¡
una variable local e' la función ex'erior oto'ga a la variable un aplazamiento. La memoria
var fnRef = outerFn();
no se libera cuando la función se completa, ya que lo sigue necesitando el closure.
li!I Apéndice C. [auabcript Closures Aprende jQuery 1.3 1m
}
function innerFn2() {
Argumentos para $(document).readyO
QuterVar += 2;
$('#example-B') .print(' (2) outerVar = ' + outerVar); Casi todo el código que escribimos utilizando jQuery acaba situándose dentro de una
}
return {r fnl ": innerFnl, I fn2 ": innerFn2} i
función pasada como un argumento a $ (document) . ready ( ) . Realizamos esto para
garantizar que el DOM se ha cargado antes de ejecutarse el código, lo que es normalmen-
te un requisito para código jQuery interesante. Cuando se crea una función y se pasa a
var fnRef = outerFn() i . ready ( ) , una referencia a la función se almacena como parte del objeto global jQuery.
fnRef.fn1() ;
fnRef.fn2() ;
Esta referencia se invoca luego en un momento posterior, cuando el DOM está listo.
fnRef.fn1() ; Normalmente situamos la construcción $ (document) . ready () en el nivel supe-
var fnRef2 = outerFn(); rior de la estructura de código, de modo que esta función no es en realidad parte de un
fnRef2.fn1(); closure. Sin embargo, puesto que nuestro código se escribe normalmente dentro de esta
fnRef2.fn2();
fnRef2.fn1() ;
función, el resto es una función interna:
$ (document) .ready(function()
Devolvemos referencias a ambas funciones, utilizando un mapa para hacerlo. Ambas var readyVar = o;
funciones se invocan por medio de las referencias: function innerFn() (
readyVar++¡
(1) outerVar e 1 $('#example-9') .print('readyVar , + readyVar);
(2) QuterVar = 3 }
(1) outerVar = 4 innerFn() ;
(1) outerVar = 1 innerFn() ;
(2) outerVar = 3 }) ;
(1) outerVar • 4
Esto se parece a muchos de nuestros ejemplos anteriores, excepto que en este caso, la
Las dos funciones internas hacen referencia a la misma variable local, de modo que función exterior es la rellamada pasada a $ (document) . ready () . Puesto que inner-
comparten el mismo entorno de cierre. Cuando innerFnl () incrementa outerVar Fn () se define dentro de ello, y hace referencia a rea?yVar que está en el ámbito de apli-
en 1, esto establece el nuevo valor de partida de outerVar cuando se invoca inner- cación de la función de rellamada, innerFn () y su entorno crean un closure. Podemos
Fn2 ( ) , y viceversa. Sin embargo, una vez más, vemos que cualquier llamada siguiente ver esto al observar que el valor de readyVar persiste entre llamadas a la función:
a outerFn () crea nuevas instancias de estos closures con un nuevo entorno de cierre
con el que coincidir. readyVar = 1
readyVar = :2
Los seguidores de la programación orientada a objetos observarán que hemos crea-
do un nuevo objeto, con las variables libres actuando como variables de instancia, y los El hecho de que la mayor parte del código jQuery esté dentro de un cuerpo de fun-
closures actuando como métodos de instancia. ción es de utilidad, ya que esto puede proteger contra algunas colisiones de espacio de
Las variables son también privadas, ya que no se pueden referenciar directamente nombre. Por ejemplo, es esta característica la que nos permite utilizar j Query . no Con -
fuera del ámbito de aplicación en el que se encuentran, permitiendo verdadera privaci- f 1i ct () para liberar el método abreviado $ para otras librerías, mientras se puede seguir
dad de datos orientados a objetos. definiendo el método abreviado localmente para uso en $ (document) . ready ( ) .
lmI Apéndice C. JavaScript Closures Aprende jQuery 1.3 lliII
Estos ejemplos han utilizado funciones anónimas, como ha sido nuestra costumbre
Manejadores de evento en código jQuery. Esto no cambia nada en la construcción de closures; los closures pue-
La construcción $ (document) . ready () normalmente engloba el resto de nuestro den proceder de funciones con nombre o anónimas. Por ejemplo, podemos escribir una
código, incluida la asignación de manejadores de evento. Puesto que los manejadores función anónima para informar del m.diee de un elemento dentro de un objeto jQuery:
son funciones, se convierten en funciones internas. Puesto que estas funciones internas $ (document) .ready(function() {
se almacenan e invocan más tarde, pueden crear closures. Un sencillo manejador el i ck $('#example-12 a') .each(function(index)
$(this) .click(function() (
puede ilustrar esto: $('#example-12') .print('index = ' + index);
$ (document) .ready(function() return false¡
var counter = o; }) ;
return false¡
$ ('#example-14 a') .each(function(index)
}) ;
$ (this) .click (clickHandler) ;
$('#example-ll
counter--¡
a.subtract') .click(function()
».
}) ;
$('#example-ll') .print('counter = I + counter);
return false¡ Esta versión activará un error JavaScript siempre que se haga clie en un vínculo por-
j);
}) ;
que index no se encuentra en el entorno de cierre de clickHandler () . Sigue siendo
una variable libre, y por lo tanto no definida en este contexto.
Puesto que ambas funciones hacen referencia a la misma variable counter, las ope-
raciones de incremento y decremento de los dos vínculos afectan al mismo valor en lugar
de ser independientes: Peligros de pérdidas de memoria
counter = 1
JavaScript gestiona su memoria utilizando una técnica conocida como recolección
counter 2
lI:l
counter • 1
de basura. Esto contrasta con los lenguajes de bajo nivel como C, que requieren progra-
counter = o madores para reservar explícitamente bloques de memoria y liberarlos cuando ya no
1j,
un sistema de conteo de referencias, que permite al usuario tomar nota de cuántas pie- }
outerVar.fn ~ innerFn¡
zas del programa están utilizando una determinada pieza de memoria de modo que se return innerFnn;
,
puede limpiar cuando ya no se utiliza. JavaScript es un lenguaje de alto nivel, por otro };
lado, y por lo general se ocupa de esta contabilidad por detrás.
Siempre que un nuevo elemento residente en memoria como un objeto o función apa- Aquí, un objeto denominado outerVar se crea y referencia desde dentro de la fun-
rece en código JavaScript, un bloque de memoria se reserva para este elemento. Cuando ción interna innerFn () . Luego, se crea una propiedad de outerVar que apunta a
el objeto se pasa a funciones y se asigna a variables, más piezas de código empiezan a innerFn (), y se devuelve innerFn () . Esto crea un closure en innerFn () que hace
apuntar al objeto. JavaScript mantiene registro de estos punteros, y cuando el último referencia a outerVar, que a su vez hace referencia a innerFn () . Pero el bucle puede
desaparece, la memoria ocupada por el objeto se libera. Considere una cadena de pun- ser más insidioso que esto:
('
teros, como muestra la figura el. function outerFn() (
var outerVar = {};
function innerFn()
outerVar.fn = innerFn¡
Figura C.1. Cadena de punteros.
return innerFn;
};
Aquí el objeto A tiene una propiedad que apunta a B, y B tiene una propiedad que
apunta a e Incluso si el objeto A aquí es el único que es una variable en el ámbito ac- Aquí, hemos cambiado innerFn () de modo que ya no haga referencia a outer-
tual, los tres objetos deben permanecer en memoria debido a los punteros hacia ellos. Varo Sin embargo, esto no rompe el bucle. Aunque outerVar no se hace nunca refe-
Cuando A sale del ámbito de aplicación, sin embargo (como al final de la función en la rencia desde innerFn (), sigue estando en el entorno de cierre de innerFn () . Todas
que se declaró), entonces se puede liberar por el recolector de basura. Ahora B no tiene las variables en el ámbito de outerFn () se les hace referencia implícitamente por in-
nada apuntando hacia él, por lo que se puede liberar, y finalmente C también se puede nerFn () debido al closure. Por lo tanto, los closures facilitan la creación accidental de
liberar. Puede ser más difícil tratar con organizaciones de referencias más complicadas, estos bucles.
como muestra la figura e2.
};
Los closures pueden hacer que se creen bucles de referencia sin darse cuenta. Puesto }) ;
que las funciones son objetos que se deben guardar en memoria, cualquier variable que
tengan en su entorno de cierre también se mantiene en memoria: Cuando se asigna el manejador el ck, esto crea un closure con di ven el entorno de
í
cierre. Pero di v ahora contiene una referencia de vuelta al closure, la propia propiedad
function outerFn() (
onclick. De esta forma, el bucle resultante no se puede liberar por Internet Explorer
var outerVar = {};
function innerFn() incluso cuando nos alejamos de la página.
lIl1!I Apéndice C. ]avaScript Closures
la buena noticia
Ahora, escribamos el mismo código, pero utilizando construcciones jQuery norma-
les:
$ (document) .ready(function()
var $div = $('#foo');
$div.click(function()
alert ('hello' ) ;
}) ;
}) ;
./
Aunque todavía está creado un closure, causando el mismo tipo de bucle que antes,
no tenemos una pérdida de memoria lE de este código. Afortunadamente, jQuery es
consciente del potencial de pérdidas, y libera manualmente todos los manejadores de
evento que asigna. Siempre y cuando sigamos utilizando los métodos de vinculación de
evento jQuery para nuestros manejadores, no tenemos que temer pérdidas.
Esto no significa que estemos totalmente liberados; debemos continuar teniendo cui-
dado cuando llevamos a cabo otras tareas con elementos DOM. Anexar objetos JavaScript
a elementos DOM puede seguir causando pérdidas de memoria en Internet Explorer;
jQuery ayuda a que esta situación sea menos frecuente.
Debido a esto, jQuery nos proporciona otra herramienta para ayudar a evitar estas
pérdidas. En el capítulo 7 vimos que el método. data () nos permite anexar informa-
ción a elementos DOM de la misma forma que lo hacemos con propiedades expando.
Puesto que estos datos no se almacenan directamente como un expando (jQuery utili-
za un mapa interno para almacenar los datos utilizando las identificaciones que crea),
el bucle de referencia nunca se forma y nosotros evitamos el problema de pérdida de
memoria. Siempre que un expando parece un mecanismo de almacenamiento de datos
conveniente, deberíamos considerar si . data () es una alternativa más segura.
Resumen
Los closures JavaScript son una potente característica del lenguaje. A menudo son
bastante útiles al ocultar variables de otro código, de modo que no pisamos nombres
de variables utilizadas en algún otro sitio. Debido a la dependencia frecuente de jQuery
de funciones como argumentos de método, también se pueden crear sin darse cuenta
bastante a menudo. Entenderlo nos permite escribir código más eficiente y conciso, y
con un poco de cuidado y el uso de las precauciones incorporadas de jQuery, podemos
evitar los peligros relacionados con memoria que pueden introducir.
i't'\'&
,,
./
.'
Este apéndice está pensado como una referencia rápida para la API jQuery, inclui-
dos expresiones de selector y métodos, Una explicación más detallada de este tema se
Apéndice O encuentra disponible en la documentación jQuery en, http : / / docs . j query . com,
Expresiones de selector
Referencia rápida La función factory jQuery $ () se utiliza para encontrar elementos en la página con
los que trabajar. Esta función toma una cadena compuesta de sintaxis a modo CSS, de-
nominada una expresión de selector. Las expresiones de selector se tratan en detalle en
el capítulo 2.
a+b Elementos b a continuación de a. :nth-chilq~formula) Elementos que son el hijo n de su elemento padre (base 1). Las
a-b Elementos que son hermanos de a. fórmulas son de la forma an-sb para enteros a y b.
:first El primer elemento en el conjunto resultado. : first-child Elementos que son el primer hijo de su padre.
:even <button>.
Elementos pares en el conjunto resultado (en base O).
:text Elementos <input> con t ype e v t.ext ".
:odd Elementos impares en el conjunto resultado (en base O).
:password Elementos <input> con type~"password".
: eq(index) Un elemento numerado en el conjunto resultado (en base O).
: radio Elementos <input> con type~"radio".
:gt (index) Todos los elementos en el conjunto resultado (mayor que)
después del índice dado (en base O). :checkbox Elementos < input> con type~" checkbox".
:empty. Elementos sin nadas hijo. :file Elementos <input> con type~"file".
:has(a) Elementos que contienen un elemento descendiente que coincide :enabled Elementos de formulario activados.
con a. :disabled Elementos de formulario desactivados.
:parent Elementos que tienen nadas hijo, :checked Casillas de verificación y botones de opción seleccionados.
:hidden Elementos que están ocultos, por medio de CSS o porque son :selected Elementos e opt í.on» seleccionados.
<input type~"hidden" />.
[attr! ~valuel Elementos cuyo atributo attr no es value. Después de crear un objeto jQuery utilizando $ () r podemos alterar el conjunto
[attrA~valuel Elementos cuyo atributo attr empieza con value. de elementos coincidentes con el que estamos trabajando al invocar uno de estos mé-
todos transversales DOM. Los métodos transversales DOM se tratan en detalle en el
[attr$~valuel Elementos cuyo atributo attr termina con value.
capítulo 2.
[attr*~valuel Elementos cuyo atributo attr contiene la subcadena value.
:nth-child(index) Elementos que son el hijo index de su elemento padre (base 1).
:nth-child(even) Elementos que son un hijo par de su elemento padre (base 1).
.filter(selector) Elementos seleccionados que coinciden con el selector facili-
:nth-child(odd) Elementos que son un hijo impar de su elemento padre (base 1). tado.
lI!IlI Apéndice D. Referencia rápida Aprende jQuery 1.3 ID
Métodos de evento
.filter(callback) Elementos seleccionados para los que la función de callback Para reaccionar al comportamiento del usuario, necesitamos registrar nuestros ma-
devuelve true.
nejadores utilizando estos métodos de evento. Observe que muchos eventos DOM sola-
. eq(index) El elemento seleccionado en el índice en base O facilitado . mente aplican a ciertos tipos de elementos; estos detalles no se tratan aquí. Los métodos
de evento se tratan en detalle en el capítulo 3.
·slice (start, [endl ) Elementos seleccionados en el rango facilitado de índices en
base O.
.one (type, [datal, Vincular handler a invocar cuando el tipo de evento dado se
·contents () Nodos hijo (incluidos nodos de texto).
handler) envía al elemento. Elimina la vinculación cuando se invoca el
·children ( [selectorl) Nodos hijo, opcional mente filtrados por un selector-' manejador.
·next ( [selectorl ) El hermano inmediatamente a continuación de cada elemento . unbind ( [typel , Elimina las vinculaciones en el elemento (para un tipo de evento,
seleccionado, opcional mente filtrado por un selector. [handlerl) un manejador determinado, o todas las vinculaciones) .
·nextAll ( [selectorl ) Todos los hermanos que siguen a cada elemento seleccionado, . live (type, handler) Vincular handler a invocar cuando el tipo facilitado de evento
opcionalmente filtrados por un selector. se envía al elemento, utilizando delegación de evento.
·prev ( [selectorl ) El hermano inmediatamente delante de cada elemento selec- .die(type, [handlerl) Elimina las vinculaciones en el elemento previamente vinculado
cionado, opcionalmente filtrado por un selector. con .live ().
·prevAll ( [selectorl ) Todos los hermanos que preceden cada elemento seleccionado, .blur(handler) Vincular handler a invocar cuando el elemento pierde el foco
opcional mente filtrados por un selector. del teclado.
·map(callback) El resultado de la función callback cuando se invoca en cada .keyup(handler) Vincular handler a invocar cuando se suelta una tecla y el
elemento seleccionado. elemento tiene foco del teclado.
1"
.load (handler) Vincular handler a invocar cuando el elemento termina de ·dblclick (~ Activar el evento dblclick.
cargarse.
·error () Activar eJ"evento error.
.mousedown(handler) Vincular handler a invocar cuando el botón del ratón se pulsa
·focus () Activar el evento focus.
dentro del elemento.
. keydown() Activar el evento keydown.
.mouseenter(handler) Vincular handler a invocar cuando el puntero delratón entra
en el elemento. No se ve afectado por burbujeo de evento. ·keypress () Activar el evento keypress.
. mouseleave(handler) Vincular handler a invocar cuando el puntero del ratón abandona l' .keyup () Activar el evento keyup .
el elemento. No afectado por burbujeo de evento.
.select() Activar el evento selecto
.mousemove(handler) Vincular handler a invocar cuando el puntero del ratón se
· submit () Activar el evento submit.
mueve dentro del elemento.
.mouseover(handler) Vincular handler a invocar cuando el puntero del latón entra Métodos de efecto
en el elemento.
.mouseup(handler) Vincular handler a invocar cuando el botón del ratón se suelta Estos métodos de efecto se pueden utilizar para llevar a cabo animaciones en elemen-
en el elemento. tos DOM. Los métodos de efecto se tratan en detalle en el capítulo 4.
.resize(handler) Vincular handler a invocar cuando el elemento cambia de
tamaño.
Métodos AJAX
.outerWidth Obtener el ancho del primer elemento coincidente, incluido Podemos r,ecuperar información del servidor sin requerir un refresco de página al
(includeMargin) relleno, borde y margen opcional.
invocar uno de estos métodos AJAX.Los métodos AJAXse tratan en el capítulo 6.
. append(content) Insertar content al final del interior de cada elemento coinci-
dente.
.appendTo(selector) Insertar los elementos coincidentes al final del interior de los $.ajax(options) Realizar una petición AJPIX utilizando el conjunto proporcionado
elementos coincidentes por selector. de opciones. Se trata de método de bajo nivel que normalmente
.prepend(content) Insertar content al principio del interior de cada elemento se invoca vía otros métodos de conveniencia.
l'
coi ncidente. Realizar una petición AJPIX a url, y situar la respuesta en los
.load (url, [data),
.prependTo(selector) Insertar los elementos coincidentes al principio del interior de [callback) ) elementos coincidentes.
los elementos coincidentes por selector. $ . get (url, [data), Realizar una petición AJPIX a url utilizando el método GET.
.after(content) Insertar content detrás de cada elemento coincidente. [callback) ,
[returnType) )
.insertAfter(sel~ctor) Insertar los elementos coincidentes después de cada uno de los
$. getJSON (url, [data), Realizar una petición AJPIX a url, interpretando la respuesta
elementos coincidentes por selector.
" [callback) ) como una estructura de datos JSON.
. before(content) Insertar content delante de cada elemento coincidente .
$.getScript(url, Realizar una peticiónAJPIX a url, ejecutando la respuesta como
.insertBefore Insertar los elementos coincidentes delante de cada uno de los [callback) ) JavaScripl.
(selector) elementos coincidentes por selector.
$. post (url, [data), Realizar una petición AJPIX a url utilizando el método POST.
.wrap(content) Situar cada uno de Ioselernentos coincidentes dentro de con- [callback) ,
tent. [returnType) )
.wrapAll(content) Situar todos los elementos coincidentes como una sola unidad · aj axComplete (handler) Vincular handler a invocar cuando se completa cualquier
dentro de contento transacción AJPIX.
·wraplnner (content) Situar los contenidos interiores de cada uno de los elementos · aj axError (handler) Vincular handler a invocar cuando cualquier transacción AJPIX
coincidentes dentro de contento se completa con un error.
· replaceWi th (content) Reemplazar los elementos coincidente con contento · aj axSend (handler) Vincular handler a invocar cuando comienza cualquier tran-
sacción AJPIX.
· replaceAll (selector) Reemplazar los elementos coincidente por selector con los
elementos coincidentes. .ajaxStart (handler) Vincular handler a invocar cuando comienza cualquier tran-
sacción AJPIX, y ninguna otra está activa.
· empty () Eliminar los nodos hijo de cada elemento coincidente.
· aj axStop (handler) Vincularhandler a invocar cuando termina cualquier transacción
· remove ( [selector) ) Eliminar los nodos coincidentes (opcionalmente filtrados por AJPIX, y ninguna otra está todavía activa.
selector) del DOM.
·aj axSuccess (handler) Vincular handler a invocar cuando cualquier transacción AJPIX
· clone ( [wi thHandlers)) Realizar una copia de todos los elementos coincidente, opcio- se completa con éxito.
nalmente también copiando manejadores de evento.
$ . aj axSetup (options) Establecer opciones predeterminadas para todas las transac-
·data (key) Obtener el elemento de datos denominado key asociado con ciones AJPIX siguientes.
el primer elemento coincidente.
· serialize () Codificar los valores de un conjunto de controles de formulario
·da ta (key, val ue) Establecer el elemento de datos denominado key asociado con en una cadena de consulta.
cada elemento coincidente en value.
· serializeArray () Codificar los valores de un conjunto de controles de formulario
· removeData (key) Eliminar el elemento de datos denominado key asociado con en una estructura de datos JSON.
cada elemento coincidente.
$ . param (map) Codificar un mapa arbitrario de valores en una cadena de consulta.
,~
In~' ,
m
.
Apéndice D. Referencia rápida
Métodos variados ~
Estos métodos no encajan bien en las categorías anteriores, pero son a menudo de
utilidad cuando se escriben scripts utilizando jQuery. lndice
$.support
$.each(collection,
Devolver un mapa de propiedades indicando si el navegador
soporta varias características y estándares.
$. map(array, callback). Construir una nueva tabla que consta del resultado de invocar
callback en cada elemento.
$. inArray (value, array) Determinar si value está en array. modificar la apariencia de una página
$0, función factory
$ . merge (arrayl, array2) Combinar los contenidos de arrayl y array2. bloques principales Web,30
clase, 42 recuperar información de un servidor
$ . unique (array) Eliminar cualquier elemento DOM duplicado de array.
evitar iteración explícita, 37 sin refrescar una página, 30
$ . isFunct ion (obj ect) Determinar si obj ect es una función.
ID,42 responder a la interacción
$. trim(string) Eliminar el espacio en blanco de los extremos de string. nombre de etiqueta, 42 de un usuario, 30
$ . noConf 1 i ct Devolver $ a su definición pre-jQuery. características, 42 simplificar tareas JavaScript
( [extreme] ) definición, 42 comunes,30
·hasClass (className) Determinar si algún elemento coincidente tiene la clase facili- insertar métodos con, 115 ARAR,137
tada. AJAX
acciones predeterminadas, 77
·is (selector) Determinar si algún elemento coincidente coincide por la expre-
sión de selector facilitada. A archivos XML, 146
cargar datos bajo demanda, 136
·each (callback) Iterar sobre los elementos coincidentes, ejecutando callback
para cada elemento.
acciones, jQuery cargar partes de una página lITML, 165
acceder a elementos datos
.length Obtener el número de elementos coincidentes.
en un documento, 30 añadir RTML, 137
·get () Obtener una tabla de nodos DOM correspondientes a los ele- alterar el contenido cargar un documento XML, 146
mentos coincidentes. de un documento, 30 el método AJAX de bajo nivel, 164
·get (index) Obtener el nodo DOM correspondiente al elemento coincidente animar cambios realizados a un elegir de archivos
en el índice facilitado. documento, 30 JavaScript, 150
· index (element) Obtener el índice del nodo DOM facilitado dentro del conjunto Asynchronous JavaScript y XML JSON,150
de elementos coincidentes. (AJAX),30 elegir de documentos XML, 150
11III fndice alfabético Índice atjabético lIfI
elegir de fragmentos HTML, 150 .fadelru). método, 99 cargar datos bajo demanda,
limitaciones de seguridad, 161 .fade'I'ogglef), método, 99
B añadir HTML, 137
opciones adicionales, 163 posicionar con CSS, 101 cargar un documento XML, 146
recuperar un objeto JavaScript, 140 efectos bajo demanda, .eargar datos,
definición, 136
trabajar con objetos JavaScript, 140 añadir HTML, 137
alternar aparecer y desaparecer funciones jQuery globales, 141
definición, 134 paulatino, 99 cargar un documento XML, 146
trabajar con objetos JavaScript, 140
delegación de evento, 78 definición, 136
animar múltiples propiedades, 100 cargar la página
detener la propagación de evento, 76 funciones jQuery globales, 141
crear, 98 coexistir con otras librerías, 60
elegir formato de datos, 150 trabajar con objetos JavaScript, 140
posicionar con CSS, 101 métodos abreviados para código, 60
eventos, 160 apariencia de tabla, ltajo nivel, método AJAX, 164
múltiples scripts en una página, 58
limitaciones de seguridad, 161 filtrado blogs planificación de la ejecución
utilizar JSONP para datos comportamiento A list apart, 379
de código, 57
remotos, 162 expandir,211 Ajaxian, 377
Cascading Style Sheets (CSS), 6
método ocultar, 211 As days pass by, 379
Charles, herramienta, 387
.ajaxfitartt), 158 DOM scripting, 379
implementar, 211 closures
.ajaxStop, 158 el blog jQuery, 377
interactuar con otro código, 214 argumentos para $(document).
AJAX de bajo nivel, 164 Estándares Web con imaginación, 378
invertir los filtros, 213 readyO,395
métodos, 413 1can't, 378
opciones de filtro, 212 en jQuery, 395
opciones predeterminadas, 164 modificar JavaScript ant, 378 interacciones entre closures, 394
XMLHttpRequest, objeto, 135 John Resig, 378
contraer secciones, 209 manejadores de evento, 396
alterar, eventos, 75 learning jQuery, 377
descripciones emergentes, 204 compartir un plug-in
acciones predeterminadas, 77 Recurso JavaScript de Matt Zinder, 378
expandir secciones, 209 con el mundo, 368
delegación de evento, 78 Robert's talk, 378
resaltar filas, 197 convenciones de nombrado, 369
destinos de los eventos, 76 Snook,378
Aptana, herramienta, 387 estilo de documentación, 370
detener la propagación, 76 Asynchronous HTTP y HTML, 137 interfaces de método, 369
objeto event, 75
ampliar imagen
Asynchronous JavaScript y XML, 134
atributos,
e compresores de código JavaScript, 375
compressor YUI, 375
animar la ampliación de la portada, 309 la función factory $0 revisada, 114 JSMin,375
cálculos numéricos, datos de formulario
añadir un indicador de carga, 314 que no son clase, 112 _ Pretty printer, 376
numéricos
aplazar las animaciones hasta que la manipular, 111 referencia (X)HTML, 376
analizar formato moneda,261
imagen se carga, 313 autocompletar, formularios compactos compuestos
aplicar formato a moneda, 261
mostrar un botón cerrar, 307 autocompletar frente a live efectos, 97
otros cálculos, 264
ocultar la portada ampliada, 305 search,253 eventos
redondear valores, 265
sobre ampliar imagen, 304 completar el campo de búsqueda, 249 .hovert), método, 70
toques finales, 265
animaciones personalizadas definir, 246 .toggleí), método, 70
tratar con decimales, 262
crear eliminar la lista de sugerencias, 253 destacar elementos sobre
características jQuery
.animatet), en el navegador, 247 los que se hace clic, 71
abstraer fallos de navegador, 31
método,98 en el servidor, 247 mostrar características
aprovechar conocimiento de CSS, 31
forma gestionar las teclas del cursor, 251 avanzadas, 70
permitir múltiples acciones, 31
primera,98 insertar sugerencias en el campo, 252 ocultar características
soportar extensiones, 31
segunda, 99 navegación por medio de teclado, 249 avanzadas, 70
trabajar con conjuntos, 31
lIlI Índice alfabético Índice alfabético EDil
configurar la página, carrusel estructura de tabla de carro trabajar con múltiples conjuntos estilo de formulario
de imágenes, 294 de la compra, 256 de elementos, 105 definición, 221
revisar los estilos con JavaScript, 296 documentación jQuery trabajar con un solo conjunto expresión regular, 227
conmutador de estilo, eventos API jQuery, 373 de elementos, 102 leyenda, 228
contexto de manejador de evento, 65 jQuery visual, 374 velocidad manipulación de casilla
eventos de teclado, 84 navegador de la API jQuery, 374 aparecer de forma paulatina, 96 de verificación, 238
habilitar otros botones, 63 visor jQuery API Adobe AIR, 374 aplicar velocidad, 95 evento, métodos, 407
mayor consolidación, 67 Wiki jQuery, 373 desaparecer de forma paulatina, 96 evento, objeto
copiar elementos, 125 Document Object Model, (DOM), 41 elementos .istí) método, utilizar, 79
donar citas, 126 DOM anexar pies de página, 122 .preventDefaultO, método, 77
donar con eventos, 126 definición, 41 donar citas, 126 .stopPropagationO, método, 77
de vuelta al código, 127 ejemplo, 41 donar con eventos, 126 acciones predeterminadas, 77
embellecer las citas, 129 elementos copiar, 125 definición, 75
una desviación CSS, 127 burbujeo de eventos, 74 donar delegación de evento, 78
CSS, 6 desventajas, 74 citas, 126 destinos de los eventos, 76
modificaciones en línea captura de eventos, 73 " con eventos, 126 propiedad evenUarget, 78
.add'Classt), método, 89 jerarquía, 72 de vuelta al código, 127 eventos
.cssf), método, 89 métodos de manipulación, 131 embellecer las citas, 129 abreviados, 68
definición, 89 métodos transversales, 353 una desviación CSS, 127 alterar, 75
aplicar estilo a celdas específicas, 52 embellecer las citas, 129 acciones predeterminadas, 77
definición, 51 insertar nuevos elementos, 115 delegación de evento, 78
D encadenar, 53 marcar contexto, 121 destinos de los eventos, 76
función filter, 52 mover elementos, 118 detener la propagación, 76
datos, AJAX recorrer el DOM, 52 marcar el contexto, 121 objeto event, 75
añadir HTML, 137 ventajas de encadenar, 53 numerar el contexto, 121 compuestos, 70
cargar un documento XML, 146 vincular el contexto, 121 .hovert), método, 70
el método AJAX de bajo nivel, 164 numerar contexto, 121 .togglef), método, 70
elegir de E situar elementos alrededor destacar elementos sobre
archivos JavaScript, 150 de otros, 124 los que se hace clic, 71
archivos JSON, 150 efecto, métodos, 409 vincular contexto, 121 mostrar características avanzadas, 70
documentos XML, 150 efectos .wrapí), método, 124 ocultar características avanzadas, 70
fragmentos HTML, 150 animaciones personalizadas elementos DOM conmutador de estilo,
limitaciones de seguridad, 161 alternar aparecer y desaparecer burbujeo de eventos, 74 contexto de manejador de evento, 65
opciones adicionales, 163 paultino, 99 desventajas, 74 habilitar otros botones, 63
recuperar un objeto JavaScript, 140 animar múltiples propiedades, 100 captura de eventos, 73 mayor consolidación, 67
trabajar con objetos JavaScript, 140 crear, 98 jerarquía, 72 de te dado, 84
datos de formulario numéricos, trabajar posicionar con CSS, 101 en línea, modificaciones CSS de usuario,
con,256 efectos simultáneos, crear, 102 .addClassO, método, 89 hoverIntent,342
cálculos numéricos, 260 en cola, 102 ..csst), método, 89 Live Query, 342
editar información de envío, 270 métodos básicos, 93 definición, 89 efectos secundarios del burbujeo
eliminar elementos, 266 rellamadas, 107 espacio de nombres de evento, 80 de eventos, 74
1m Índice alfabético Índice alfabético ID
eliminar manejador de evento, 80 :radio, 405 implementar,211 manipulación de casilla
espacio de nombres de evento, 80 :reset, 405 interactuar con otro código, 214 de verificación, 238
manejador de evento, 80 :selected, 405 invertir los fi~tros, 213 estilo de formulario mejorado, 222
objeto event, 75 :submit, 405 opciones de filtro, 212 mejorar un formulario básico, 221
sencillos, 61 :text, 405 Firebug Lite, herramienta, 386 validación, 231
vincular eventos, 81 :visible, 404 Firefox, herramientas campos obligatorios, 231
expresiones de selector [attrl=value], 404 barra de herramientas campos, 231
#id,403 [attr$=value], 404 de desarrollador Web, 384 expresión regular, 235
*,403 [attr*=value], 404 comprobador de expresiones formatos de entrada de datos, 234
.class, 403 [attr],404 / regulares, 384 formatos obligatorios, 231
:animated, 404 [attrl\=value],404 Firebug, 383 regla, 231
:button, 405 [attr=value],404 Venkman, 384 tareas de código, 235
:checkbox, 405 a - b, 404 Form, plug-in última comprobación, 231
:checked, 405 a + b, 404 .ajaxf'ormf), método, 323 validación del lado del servidor, 231
:contains(text),404 a> b, 403 .ajaxSubmitf), método, 323 formularios, plug-ins
:disabled, 405 a b, 403 .' definición, 323 Autocomplete, 333
:empty,404 a,b,403 opción Jeditable, 334
:enabled, 405 añadir pseudos-clase, 366 beforeSubmit, 323 Masket input, 335
:eq(index), 404 crear, 366 success, 323 Validation, 334
:even, 404 element, 403 target, 323 función factory $0
:file, 405 parámetros formulario, bloques principales
:first, 404 element, 367 compactos clase, 42
:first-child, 405 index,367 autocompletar, 246 evitar iteración explícita, 37
:gt(index), 404 matches, 367 completar el campo nombre de etiqueta, 42
:has(a),404 set, 367 de búsqueda, 249 características, 42
:header, 404 utilizar, 366 eliminar la lista de sugerencias, 253 definición, 42
:hidden, 404 en el navegador, 247 insertar métodos con, 115
:image, 405 en el servidor, 247 funciones globales,
:input, 405 F gestionar las teclas del cursor, 251 añadir múltiples funciones, 346
:last, 404 insertar sugerencias crear un método de utilidad, 347
:last-child, 405 fadelru), método, 97 en el campo, 252 ventaja, 347
:lt(index),404 fadeOutO, método, 97 navegación por medio funciones internas,
:not(a),404 Fiddler, herramienta, 387 de teclado, 249 definición, 389
:nth-child(even),404 filas, apariencia de la página, 197 sobre autocompletar, 246 el gran escape, 390
:nth-child(formula),405 alternar color de filas, 198 completar el campo de búsqueda, 249
:nth-child(index), 404 avanzado, 200 sobre formularios compactos, 243
:nth-child(odd),404 resaltar filas, 197 definición, 229
G
:odd,404 basado en interacción del usuario, 202 estilo GNU, licencia pública, 32
:only-child, 405 filtrado, apariencia de tabla definición, 221 gráficas, plug-ins
:parent, 404 comportamiento expandir, 211 expresión regular, 227 Flot, 341
:password, 405 comportamiento ocultar, 211 leyenda, 228 Sparklines, 341
lB lndice alfabéiico lndice aifabético ••
T V
tablas validación de formulario, 231
apariencia campos, 231
contraer secciones, 209 obligatorios, 231
descripciones emergentes, 204 expresión regular, 235
expandir secciones, 209 formatos de entrada de datos, 234
resaltar filas, 197 formatos obligatorios, 231
manipulación regla, 231 /
filtrado, 211 tareas de código, 235
modificar apariencia de la tabla, 197 última comprobación, 231
ordenación JavaScript, 173 validación del lado del servidor, 231
ordenar y paginar, 172 velocidad y efectos
paginación del lado del servidor, 186 aparecer de forma paulatina, 96
paginación JavaScript, 190 aplicar velocidad, 95 ,
plug-ins desaparecer de forma paulatina, 96
Flexigrid, 337 Venkman, herramientas Firefox, 384
jqGrid,336
Tablesorter, 336
teclado, eventos de w
.keyCode, propiedad, 84
añadir, 84 W3C,376
keydown,84 Wide Web Consortium, (W3C), 376
keypress, 84
keyup,84
TextMate jQuery, herramienta, 387
x
titular, rotativo
XMLPath, lenguaje (XPath), 32
añadir un indicador de carga, 289
recuperar un feed de un dominio
diferente, 288