Sei sulla pagina 1di 216

PROGRAMACIÓN

TÍTULO DE LA OBRA ORIGINAL:


Learning jQuery 1.3
Aprende jQuery .3
RESPONSABLE EDITORIAL:
Víctor Manuel Ruiz Calderón
Alicia Cózar Concejil

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.

Authorized translation from English language edition published


by Packt Publishing Ltd.
Copyright © 2009 by Packt Publishing

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.

Sobre los revisores


Akash Mehta es des arrollador de aplicaciones Web, escritor técnico y consultor con
sede en Brisbane, Australia. Sus proyectos anteriores incluyen sitios Web, soluciones
de e-learning y sistemas de información. Ha escrito artículos sobre desarrollo Web para
varios editores en medio impreso y online, es conferenciante habitual en conferencias
locales, y contribuye en blogs PHP.
Como estudiante, Akash mantenía aplicaciones Web PHP y creó interfaces de usuario
utilizando el conjunto de herramientas jQuery. Mientras se dedicaba a obtener su titula-
ción en comercio y TI,Akash desarrolló aplicaciones Web en plataformas PHP y Python. "
Después de horas, organizó su grupo de usuarios PHP local.
Akash desarrolla aplicaciones sobre una amplia variedad de librerías de código
abierto. Su conjunto de herramientas incluyen una serie de marcos de trabajo de aplica-
ciones, incluido Zend, CakePHP y Django; marcos de trabajo Javascript como jQuery,
Prototype y Mootools, plataformas como Adobe Flash/Flex, y los motores de base de
datos MySQL y SQLite.
.,
Indice
/ d c nt id

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 ••

Selectores CSS 43 Efectos y velocidad 95


Aplicar estilo a niveles de elementos de lista 44 Aplicar velocidad 96
Selectores de atributo 46 Aparecer y desaparecer de forma paulatina 96
Aplicar estilo a vínculos 46 Efectos compuestos >••................................................................................. 97
Selectores personalizados 48 Crear animaciones personalizadas 98
Aplicar estilo a filas alternas 48 Alternar el aparecer y desaparecer paulatino 99 .
Selectores de formulario 51 Animar múltiples propiedades 100
Métodos transversales DOM 51 Posicionar con CSS 101
Aplicar estilo a celdas específicas 52 Efectos simultáneos frente a "en cola" 102
Encadenar 53 Trabajar con un solo conjunto de elementos 102
Acceder a elementos DOM 54 Trabajar con múltiples conjuntos de elementos 105
Resumen 55 Rellamadas , 107
En pocas palabras 109
3. Eventos 56 Resumen 109
Llevar a cabo tareas al cargar la página 57
Planificación de la ejecución de código 57 5. Manipulación DOM 110
Múltiples scripts en una página ,: 58
Manipular atributos 111
Métodos abreviados para código 60 ' . -
Atributos que no son clase 112
Coexistir con otras librerías 60
La función factory $0 revisada 114
Eventos sencillos 61
Insertar nuevos elementos 115
Un sencillo conmutador de estilo "61 Mover elementos 118
Habilitar los otros botones 63'"
Marcar, numerar y vincular el contexto 121
Contexto de manejador de evento 65
Anexar pies de página 122
Mayor consolidación 67
Situar elementos alrededor de otros 124
Eventos abreviados 68
Copiar elementos 125
Eventos compuestos 70
Clonar con eventos 126
Mostrar y ocultar características avanzadas 70
Clonar citas 126
Destacar elementos sobre los que se hace clic 71
Una desviación CSS 127
El viaje de un evento 72
De vuelta al código 127
Efectos secundarios del burbujeo de eventos 74
Embellecer las citas 129
Alterar el viaje: el objeto event .............................................................•.....w 75
Métodos de manipulación DOM 131
Destinos de los eventos 76
Resumen 132
Detener la propagación de evento 76
Acciones predetermínadas 77 6. AJAX 134
Delegación de evento 78
Eliminar un manejador de evento
Espacio de nombres de evento
80
80 Ca~:~~t~::1~ ~~.~~.~~:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Trabajar con objetos JavaScript 140
i~~
Volver a vincular eventos 81
Simular interacción de usuario 83 Recuperar un objeto JavaScript. 140
Eventos de teclado 84 Funciones jQuery globales 141
Resumen 86 Ejecutar un script 144
Cargar un documento XML 146
4. Efectos 88 Elegir un formato de datos 150
Modificaciones CSS en línea 89 Pasar datos al servidor 151
Métodos básicos ocultar y mostrar 93 Llevar a cabo una petición GET 151
lIfJI Í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 ••

Recuperar el feed 279 Tablas ;.. 335


Configurar el rotativo 282 Tablesorter 336
La función rotar titular 283 jqGrid ¡...........................•.............................................................................................. 336

Detenerse al pasar por encima 286 Flexigrid ,,~ 337


Recuperar un feed de un dominio diferente 288 Imágenes 337
Añadir un indicador de carga 289 Jcrop 337
Efecto degradado 290 Magnify 337
El código terminado 292 jQuery Lightbox y cuadros de diálogo modales 338
Un carrusel de imágenes 294 FancyBox 338
Configurar la página 294 r Thickbox 339
Revisar los estilos con JavaScript.. 296 BlockUI 340
Mover las imágenes cuando se hace clic 297 jqModal 340
Añadir animación deslizable 299 Gráficas : 340
Mostrar iconos de acción 301 Flot 341
Ampliar imagen 304 Sparklines 341
Ocultar la portada ampliada 305 Eventos 341
Mostrar un botón cerrar .........................................................................•: 307 hoverIntent 342
Más diversión con el etiquetado 308 Live query 342
Animar la ampliación de la portada 309 Resumen 343
Aplazar las animaciones hasta que la imagen se carga 313
Añadir un indicador de carga 3r4, ' 11. Desarrollar plug-ins 344
El código terminado 316
Resumen 318 Añadir nuevas funciones globales 345
Añadir múltiples funciones 346
10. Utilizar plug-ins 320 ¿Qué sentido tiene? 347
Crear un método de utilidad 347
Encontrar plug-ins y ayuda 321
Añadir métodos de objeto jQuery 349
Cómo utilizar un plug-in 322
Contexto del método de objeto 349
El plug-in Form 323
Encadenar métodos 352
Consejos y trucos 324
Métodos transversales DOM 353
La librería de plug-ins jQuery UI... 325
Añadir nuevos métodos abreviados 356
Efectos :' 325
Animaciones de color 325 Parámetros de método 359
Animaciones de clase 326 Parámetros sencillos 360
Aceleración y desaceleración avanzada 326 Mapas de parámetro 361
Efectos adicionales 327 Valores de parámetro predeterminados 362
Componentes de interacción 327 Funciones de rellamada 363
Widgets 330 Predeterminados personalizables 364
ThemeRoller 332 Añadir una expresión de selector 366
Otros plug-ins recomendados 332 Compartir un plug-in con el mundo 368
Formularios 333 Convenciones de nombrado 369
Autocomplete 333 Uso del alias $ 369
Validation 334 Interfaces de método 369
Jeditable 334 Estilo de la documentación 370
Masked input. 335 Resumen 370
•• Índice de contenidos Índice de contenidos ••

Apéndice A. Recursos online 372 Venkman 384


Comprobador' de expresiones regulares :.. 384
Documentación jQuery 373 Herramientas para Internet Explorer 384
Wiki jQuery 373 Microsoft Intlrnet Explorer Developej Toolbar 384
API jQuery 373 Microsoft Visual Web Developer 385
Navegador de la API jQuery 374 DebugBar 385 .
jQuery visual 374 Drip 385
Visor jQueryAPI Adobe AIR 374 Herramientas para Safari 385
Referencia JavaScript 374 Menú Develop 385
Centro de desarrollo Mozilla 374 Inspector Web 386
Dev.opera 374 Herramientas para Opera 386
Referencia JScript MSDN 375 Dragonfly 386
Quirksmode 375 Otras herramientas , 386
JavaScript Toolbox 375 - Firebug Lite 386
Compresores de código JavaScript 375 NitobiBug 386
Compresor YUI 375 Paquete TextMate jQuery 387

~~~
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

En estos momentos tenemos más personas en el equipo jQuery dedicadas a gestionar la


comunidad jQuery, escribir documentación o escribir plug-ins que a mantener la base
de código principal. Aunque la salud de la librería es increíblemente importante, la co-
munidad que rodea ese código es la diferencia entre un proyecto mediocre, y uno que ""
cumplirá y superará con creces sus necesidades.
Cómo gestionamos el proyecto, y cómo utilizamos el código, es fundamentalmente
muy diferente de la mayoría de proyectos de código abierto, y de la mayoría de librerías
JavaScript. El proyecto jQuery y la comunidad son increíblemente expertos; sabemos lo
que hace que jQuery sea una experiencia de programación diferente y hacemos todo lo
posible para pasar ese conocimiento a los compañeros usuarios.
La comunicad jQuery no es algo sobre lo que pueda leer para aprender; es algo en lo
que realmente tiene que participar plenamente para conocerlo. Espero que tenga la opor-
tunidad de participar en ello. Anímese a participar en los foros, listas de distribución y
blogs y permítanos guiarle por la experiencia de conocer jQuery mejor.
Para mí, jQuery es mucho más que un bloque de códigó. Es la suma total de expe-
riencias que han sucedido a lo largo de los años para que la librería pudiera ocurrir: los
considerables altibajos, enfrentarse al desarrollo junto con la emoción de verlo crecer y
tener éxito, estando cerca de sus usuarios y miembros del equipo, entendiéndoles y tra- . -
tando de crecer y adaptarse.
Cuando vi que este libro hablada de jQuery y lo trataba como una herramienta uni-
ficada, me sentí sorprendido y emocionado. Ver cómo otros aprenden, entienden y rribl- '
dean jQuery para que se ajuste a ellos es mucho de lo que hace que el proyecto sea tan'
estimulante.
No soy el único que disfruta de jQuery en un nivel que es muy diferente de una rela-
ción normal herramienta-usuario. No sé si puedo resumir a qué se debe, pero lo he visto
una y otra vez, el momento en el que la cara de un usuario se ilumina al darse cuenta de
lo mucho que le puede ayudar jQuery.
Existe un momento específico para un usuario jQuery, cuando se da cuenta que esta
herramienta que había estado utilizando era de hecho mucho más que una sencilla he-
rramienta, y de repente su entendimiento de cómo escribir aplicaciones Web dinámicas
cambia completamente. Es una cosa increíble, y absolutamente p:ü parte favorita del
proyecto jQuery.
Espero que tenga también la oportunidad de experimentar esta sensación.

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

• Los nombres de archivo, URL y código incluido en texto se muestran en un tipo


de letra monoespacial.
• Los menús, submenús, opciones, cuadros de diálogo y demás elementos de la
interfaz de las aplicaciones se muestran en un tipo de letra Arial. ~
• Un bloque de código se establecerá de la siguiente forma:
<html>
<head>
<title>el título</title>
</head>
<body>
<div>
<p>Esto es un párrafo.</p>
<p>Esto es otro párrafo.</p>
<p>Esto es otro párrafo.</p>
</div>
</body>
</html>

Cuando deseamos llamar la atención hacia una parte determinada de un bloque ..


de código, las líneas relevantes o elementos aparecerán en negrita:
$ (document) .ready(function() {
$ (/a [href'=mailto, 1/) .addClass (fmailto/) ; ':;'"'-,

$(/a[href$=.pdfl/) .addClass(/pdflink/);
$ (fa [hrefA=http] [href*=henry] /)
.addClass(/henrylink/),
}) ;

En estos cuadros se incluye información importante directamente relacionada con el


texto adjunto. Los trucos, sugerencias y comentarios afines relacionados con el tema
analizado se reproducen en este formato.

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.

Qué hace jQuery


La librería jQuery proporciona una capa de abstracción de aplicación general para
programación Web común, y por lo tanto es de utilidad en casi cualquier situación de
programación. Su naturaleza extensible significa que casi nunca podemos tratar todos
ElII 1. Empezar a trabajar Aprende jQuery 1.3 EII
los posibles usos y funciones en un único libro, ya que constantemente se están desarro-
llando plug-ins para añadir nuevas posibilidades de uso. Las características principales, Por qué.jQuery funciona bien
sin embargo, abordan las siguientes necesidades:
Con el reciente resurgimiento de iqterés en HTML dinámico viene una proliferación
• Acceder a elementos en un documento: Sin una librería JavaScript, se tienen que de marcos de trabajo JavaScript. Algunos son especializados, centrándose en una o dos
escribir muchas líneas de código para recorrer el árbol DOM (Document Object de las tareas anteriores. Otros intentan catalogar cualquier comportamiento y animación
Model), y localizar partes especiales de la estructura de un documento HTML. posible, y las presentan pre-empaquetadas. Para mantener la amplia gama de caracterís-
Un mecanismo selector robusto y eficiente se ofrece en jQuery para recuperar la ticas vistas anteriormente y seguir siendo compacto, jQuery emplea varias estrategias:
parte exacta del documento que se tiene que inspeccionar o manipular.
• Modificar la apariencia de una página Web: CSS ofrece un método potente de • Aprovechar el conocimiento de CSS: Al basar el mecanismo para localizar ele-
influir en la forma en que se muestra un documento, pero se queda corto cuando mentos de página en selectores CSS,jQuery hereda una forma concisa y legible
los navegadores Web no soportan todos los mismos estándares. Con jQuery, los de expresar la estructura de un documento. La librería jQuery se convierte en un
desarrolladores pueden llenar este vacío, basándose en el soporte de los mismos punto de entrada para diseñadores que desean añadir comportamientos a sus
estándares entre todos los navegadores. Además, jQuery puede cambiar las clases páginas porque un requisito previo para realizar desarrollo Web profesional es
o propiedades de estilo individual aplicadas a una parte del documento incluso conocimiento de sintaxis CSS.
después de que se haya mostrado la página. • Soportar extensiones: Para evitar el aumento de características, jQuery relega
• Alterar el contenido de un documento: No limitado a meros cambios estéticos, usos especiales a plug-ins. El método para crear nuevos plug-ins es sencillo y
jQuery puede modificar el contenido de un documento con algunas teclas. El bien documentado, que ha estimulado el desarrollo de una amplia variedad
texto se puede cambiar, las imágenes se pueden insertar o cambiar, las listas se de módulos de utilidad. Incluso la mayoría de las características en la descarga
pueden reordenar, o toda la estructura del HTML se puede volver a escribir y jQuery básica se realizan internamente por medio de la arquitectura plug-in, y
ampliar, todo ello con una sola API (Application Programming Interface o Interfaz se pueden eliminar si se desea, resultando en una biblioteca incluso menor.
de programación de aplicaciones) fácil de utilizar. • Abstraer los fallos de navegador: Una triste realidad del desarrollo Web es
• Responder a la interacción de un usuario: Incluso los comportamientos más que cada navegador tiene su propio conjunto de desviaciones de los estándares
elaborados y potentes no son de utilidad si no podemos controlar cuándo tienen publicados. Una parte significativa de cualquier aplicación Web se puede relegar
lugar. La librería jQuery ofrece una forma elegante de interceptar una amplia a gestionar características de forma diferente en cada plataforma. Aunque el es-
variedad de eventos, como que el usuario haga clicen un vínculo, sin la necesidad cenario de los navegadores en continuo cambio hace que un código base neutro
de saturar el propio código HTML con manejadores de evento. Al mismo tiempo, para el navegador sea imposible para otras características avanzadas, jQuery
su API de gestión de eventos elimina las inconsistencias del navegador que a añade una capa de abstracción que normaliza las tareas comunes, reduciendo el
menudo molestan a los desarrolladores Web. tamaño del código, y simplificándolo tremendamente.
• Animar cambios realizados a un documento: Para implementar de forma eficiente • Siempre trabajar con conjuntos: Cuando le decimos a jQuery, Encontrar todos los
tales comportamientos interactivos, un diseñador debe también proporcionar elementos con la clase collapsible y ocultarlos, no hay necesidad de pasar en bucle
feedback visual al usuario. La biblioteca jQuery facilita esto al proporcionar una por cada elemento devuelto. En su lugar, métodos como. hide () se diseñan para
tabla de efectos, como desvanecerse, así como un conjunto de herramientas para trabajar automáticamente sobre conjuntos de objetos en lugar de individuales.
diseñar nuevas. Esta técnica, denominada iteración implícita, significa que muchas construcciones
• Recuperar información de un servidor sin refrescar una página: Este patrón de de bucle dejar).de ser necesarias, acortando el código considerablemente.
código se ha conocido como AJAX(Asynchronous JavaScript And XMLo JavaScript • Permitir múltiples acciones en una línea: Para evitar uso excesivo de variables
asíncrono y XML),Yayuda a los diseñadores Web a diseñar un sitio rico en ca- temporales o repetición malgastada, jQuery emplea un patrón de programación
racterísticas. La librería jQuery elimina la complejidad específica de navegador denominado encadenamiento para la mayoría de sus métodos. Esto significa que
de este proceso, permitiendo a los desarrolladores centrarse en la funcionalidad el resultado de la mayoría de operaciones en un objeto es el objeto en sí mismo,
del servidor. listo para la siguiente acción que se le va a aplicar.
• Simplificar tareas JavaScript comunes: Además de todas las características espe- Estas estrategias han mantenido el paquete jQuery reducido, por debajo de 20 KB
cíficas de documento de jQuery, la librería proporciona mejoras a construcciones comprimido, mientras que al mismo tiempo proporciona técnicas para mantener nues-
JavaScript básicas como iteración y manipulación de tabla. tro código personalizado que utiliza la librería compacta, también.
•• 1. Empezar a trabajar Aprende jQuery 1.3 ••

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.

YKCOWRE8BAJ .Aplicar la nueva clase


r~-------------------- _
sevot yhtils en! dn. ,glllirb S8Wr
:eb8w ent ni e\bmlg eoe etyV diO
,sevogOfob ~I erew ysmim tiA El método. addClass (), como la mayoría de métodos jQuery, tiene un nombre
.et>argtuo shtar Rmom ehl dnA
She punled 0W!f Ihi, fOf soma time, bul al 1m. btighl lhought tUud: heJ. ~/hy. it's a loolting-gl8$ book. 01 coursel And i11 "old it up lo a glass, the words will all go auto descriptivo; aplica una clase CSS a la parte de la página que hemos seleccionado.
the rightW8y ageln." Su único parámetro es el.nombre de la clase a añadir. Este método, y su equivalente,
T"is wu the poem thal Allc. ntad. - . removeClass (), nos permitirá fácilmente observar jQuery en acción a medida que
JA88ERWOCKY
'Twas bfillig, and the sllthy laves
exploramos las diferentes expresiones de selector que tenemos disponibles. Por ahora,
Oid gyre and gimble in tne webe: nuestro ejemplo simplemente añade la clase highlight, que nuestra hoja de estilo ha
AII minuy wete the borogoves.
And the mame raths outgrabe. definido como texto en cursiva con un borde.
Figura 1.1. Aplicarestilosal poema.
Observe que no es necesaria iteración para añadir la clase a todas las estrofas del
poema. Como hemos comentado, jQuery utiliza iteración implícita dentro de métodos
como. addClass (), por lo que una sola llamada de función es todo lo que necesita
Utilizaremos jQuery para aplicar un nuevo estilo al texto del poema.
para alterar todas las partes seleccionadas del documento.

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');

La operación fundamental en jQuery es seleccionar una parte del documento. Esto


se realiza con la construcción $ ( ) . Típicamente, toma una cadena como parámetro, que Luego anexaremos la función al evento al modificar la etiqueta HTML -cbody» para
puede contener cualquier expresión de selector CSS.En este caso, deseamos encontrar hacerle referencia:
todas las partes del documento que tienen aplicada la clase poem-stanza, por lo que
<hody onload="highlightPoemStanzas() ¡">
el selector es muy sencillo. Sin embargo, trataremos opciones más sofisticadas a lo largo
Aprende jQuery 1.3 ••
ElII 1. Empezar a trabajar

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.

En jQuery, el signo de dólar $ es simplemente un "alias" para jQuery. Pueden surgir


Para empezar a aprender cómo funciona jQuery con selectores CSS, utilizaremos
conflictos si más de una de estas librerías se utilizan en una página dada porque una
una estructura que aparece en muchos sitios Web, a menudo para navegación: la lista
función $ () es muy común en librerías JavaScript. Podemos evitar tales conflictos al
anidada, sin ordenar.
reemplazar cada instancia de $ con j Query en nuestro código jQuery personalizado.
<ul id=llselected-plays· >
I

<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 ••

<li>Twelfth Night</li> .horizontal {


float, left;
</ul>
list-style: none;
</li>
margin: l?;pX¡
<li>Tragedies
<ul>
} '"
<li><a href="hamlet.pdf">Harnlet</a></li> .sub-level (
background: #CCC¡
<li>Macbeth</li>
<li:>Romeo and Juliet</li>
</ul> La clase horizontal flota el elemento a la izquierda del que le sigue, elimina el
</lb boliche de él si es un elemento de lista, y añade un margen de 10 píxeles en todos los
<li:>Histories lados.
<ul>
<li>Henry IV «a href=lImailto:henryiv@king.co.uk >email</a»
ll En lugar de anexar la clase horizontal directamente en nuestro HTML, lo añadire-
<ul> mos.dinámicamente a los elementos de lista de nivel superior únicamente, Comedies,
<li>Part I</li> Tragedies, Histories, para demostrar el uso de selectores de jQuery:
<li>Part II</li>
</ul> $ (document) .ready(function() {
<li><a href="http://www.shakespeare.co.uk/henryv.htm">Henry V</a></li> $(f#selected-plays > li/) .addClass(/horizontal/);
<li>Richard II</li> }) ;

</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!'

lIIlII 2. Se/ectores Aprende jQuery 1.3 lIiII

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;

Figura 2.3. Listaconfondosombreado.


Luego, añadimos las tres clases, mail t o, pdfl ink, y henryl Lrik, a los vínculos
apropiados utilizando jQuery.
Para añadir una clase para todos los vínculos de correo electrónico, construimos un
Selectores de atributo selector que busca todos los elementos ancla (a) con un atributo href ( [href) que em-
pieza con mail to: (=mail to: 1 ), de la siguiente forma:
Los selectores de atributo son un subconjunto particularmente de utilidad de los se-
$( document) .ready(function() (
lectores CSS.Nos permiten especificar un elemento por una de sus propiedades HTML,
$(/a[hrefA=mailto:l/) .addClass(/mailto/);
como el atributo title de un vínculo o el atributo alt de una imagen. }) ;
Por ejemplo, para seleccionar todas las imágenes que tiene un atributo al t , escribi-
mos lo siguiente: Para añadir una clase para todos los vínculos a archivos PDF, utilizamos el signo
de dólar en lugar del símbolo />. Esto es porque estamos seleccionando vínculos con un
$ (limg [alt1 j)
atributo href que termina con. pdf:
$ (document) .ready(function() {
$ (la [hrefA=mailto, 1/) .addClass (lmail to/) ;
$(/a[href$=.pdfl/) .addClass(/pdflink/);
En versiones anteriores a 1.2, jQuery utilizabala sintaxis XMLPath Language(XPath) }) ;

para sus selectoresde atributo e incluía un conjunto de otros selectoresXPath.Aunque


Los selectores de atributo también se pueden combinar. Podemos, por ejemplo, aña-
estos selectores XPath básicos se han eliminado desde entonces de la librería jQuery
dir una clase henrylink para todos los vínculos con un valor href que empieza con
principal, siguen estando disponibles comoun plug-in: http://plugins . j query.
ht tp Ycontiene henry en cualquier lugar:
com/project/xpath/
$Idocument) .readylfunctionl) (
$(/a[hrefA=mailto:1/) .addClassl/mailto/);
$1/a[href$=.pdf1/) .addClassl/pdflink/);

Aplicar estilo a vínculos $ (/a[hrefA=http1 [href*=henryl/l


.addClassl/henrylink/l;
}l ;
Los selectores de atributo aceptan una sintaxis de comodines inspirada por las ex-
presiones regulares para identificar el valor al principio () o final (s) de una cadena. Con las tres clases aplicadas a los tres tipos de vínculos, deberíamos ver la figura 2.4.
También pueden tomar un asterisco (*) para indicar el 'Valoren una posición arbitraria Observe el icono PDF a la derecha del vínculo Harnlet, el icono de sobre junto al vínculo
dentro de una cadena o un signo de exclamación para indicar un valor negado. emaíl, y el fondo blanco y borde negro alrededor del vínculo Henry V.
Aprende jQuery 1.3 ••
KIlII 2. Selectores

<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- }) ;

cas de tablas, dada la siguiente tabla:


Pero, iespere! ¿Por qué utilizar el selector :odd para las filas pares? Bueno, como
<.table> con el selector : eq ( ) , los selectores : odd y : even utilizan la numeración en base cero
<.tr>
<td>As You Like It</td>
nativa de JavaScript. Por lo tanto, la primera fila cuenta como O (par) y la segunda fila
<td>comedy</td> cuenta como 1 (impar), etc. Con esto en mente, podemos esperar que nuestro sencillo
<td></td> código produzca una tabla que se parece a la figura 2.5:
</tr>
<.tr>
<td>All/s Well that Ends Well</td>
<td>comedy</td>
<td>1601</td>
</tr>
<.tr>
<td>Hamlet</td>
<td>Tragedy</td>
<td>1604</td>
</tr> Figura 2.5. Aplicar estilo en filas alternas.
<.tr>
f
Aprende jQuery 1.3 ••
•• 2. Selectores

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.

Métodos transversales DOM


Figura 2.6. Aplicarselector al código. Los selectores jQuery que hemos explorado hasta el momento nos permiten seleccio-
nar un conjunto de elementos a medida que navegamos por el árbol DOM y filtrar los
Es importante destacar que el selector : contains () es sensible a mayúscula y mi- resultados. Si ésta fuera la única forma de seleccionar elementos, nuestras opciones es-
núscula. Utilizar $ (/td: contains (henry) /) en su lugar, sin la "H" mayúscula, no tarían bastante limitadas (aunque, francamente, las expresiones de selector son robustas
seleccionará ninguna celda. por derecho propio, especialmente cuando se compara con las opciones de programación
Sin duda alguna, existen formas de conseguir las rayas de fila y resaltar texto sin DOM normal). Existen muchas ocasiones cuando seleccionar un elemento padre o ances-
[Query, o cualquier programación del lado del cliente, para el caso. No obstante, jQuery, tro es esencial; aquí es donde los métodos transversales DOM de jQuery entran en juego.
junto con CSS, es una estupenda alternativa para este tipo de estilo en casos donde el Con estos métodos a nuestra disposición, podemos recorrer el DOM con facilidad.
contenido se genera dinámicamente y no tenemos acceso al HTML o al código del lado Algunos de los métodos tienen un equivalente casi idéntico entre las expresiones
del servidor. de selector. Por ejemplo, la línea que primero hemos utilizado para añadir la clase al t,
EJI 2. Selectores ,, Aprende jQuery 1.3 mil
$ (/tr: odd/) . addClass (/ alt/) t, se podría rescribir con el método. filter () As You Llke It Comed y
de la siguiente forma: IAU's Well t/lllt Ends weU Có./ÍledY ,lE,ñ]
Hamlet Tragedy 1604
$ (/trj) .filter(/:oddj) .addClass(/altj);
IE~ S "Ii!! ~g!!¡I'¿~4J
Romeo and Jullet 'Ti'agedy 1595
Sin embargo, en su mayor parte, las dos formas de seleccionar elementos se comple-
mentan entre sí. Igualmente, el método. f i 1ter () en particular tiene un enorme poder I&e~ii IV:el!r.I: h '~'i.:jIt!sto!i:';1596]
Henry V Hlstory 1599
porque puede tomar una función como su argumento.
La función nos permite crear pruebas complejas para determinar si los elementos se Figura 2.7. Celda específico con estilo.
deberían mantener en el conjunto que coincide. Supongamos, por ejemplo, que queremos
añadir una clase a todos los vínculos externos. jQuery no tiene selector para este tipo de El método . next () selecciona solamente el siguiente elemento hermano. Para re-
caso. Sin una función de filtro, estaríamos forzados a pasar en bucle explícitamente por saltar todas las celdas que siguen a la que contiene Henry, podríamos utilizar el método
cada elemento, comprobando cada uno por separado. Con la siguiente función de filtro, . nextAll () en su lugar.
sin embargo, podemos seguir basándonos en la iteración implícita de jQuery y mantener $ (document) .ready(function() {
nuestro código compacto: $ (/td:contains (Henry)/) .nextAll() .addClass(/highlight/);
}) ;
$(/a/) .filter(function() {
return this.hostname && this.hostname != location.hostname¡
)) .addClass(/external/);

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:

Aplicar estilo a celdas específicas $ (document) .ready(function()


$ (/td:contains(Henry)/)
{
.parent() .children() .addClass(/highlight/);
});
Anteriormente hemos añadido una clase resaltada a todas las celdas que contenían
el texto Henry. Por el contrario, para aplicar estilo a la celda junto a cada celda que con- En lugar de atravesar por los elementos hermanos, subimos un nivel en el DOM al
tiene Henry, podemos empezar con el selector que ya hemos escrito, y simplemente en- <t r » con. parent () y luego seleccionamos todas las celdas de la fila con. children ( ) .
cadenar a éste el método next ( ) :
$ (document) .ready(function() { Encadenar
$(/td:contains(Henry)/).next() .addClass(/highlight/);
}) ; Las combinaciones de métodos transversales que acabamos de explorar ilustran la
posibilidad de encadenamiento de jQuery. Es posible con jQuery seleccionar múltiples
La tabla ahora debería parecerse a la figura 2.7. conjuntos de elementos y realizar múltiples cosas con ellos, todo dentro de una sola línea
EII 2. Selectores Aprende jQuery 1.3 .mil
de código. Este encadenamiento no solamente ayuda a mantener el código jQuery con- Para mayqr conveniencia, jQuery proporciona un método abreviado para. get () .
ciso, sino que también puede mejorar el rendimiento de un script cuando la alternativa En lugar de escribir la línea anterior, podemos utilizar corchetes cuadrados a continua-
es volver a especificar un selector. ción del selector:
También es posible dividir una sola línea de código en múltiples líneas para mayor
var myTag = $ (/#my-element/)
'"
[OJ .tagName;
legibilidad. Por ejemplo, una sola secuencia de métodos encadenada se podría escribir
como una línea ... No en vano esta sintaxis se parece a una tabla de elementos DOM; utilizar los cor-
$ (/td:contains(Henry)/) .parent() .find(/td:eq(l)/)
chetes es como retirar el envoltorio jQuery para llegar a la lista de nodo s, mientras que
.addClass(/highlight/) .end() .find(/td:eq(2)/) al incluir el índice (en este caso, O), es como arrancar un elemento DOM .
.addClass(/highlight/) ;

... o como siete líneas ...


$ (/td:contains(Henry)/) // Encontrar todas las celdas que contienen "Henry"
Resumen
.parent() // Seleccionar su padre
.find(/td:eq(l)/) // Encontrar la segunda celda descendiente Con las técnicas que hemos tratado en este capítulo, ahora deberíamos poder aplicar
.addClass(/highlight/) // Añadir la clase "highlight" estilo a los elementos de nivel superior y subnivel en una lista anidada al utilizar se-
.end() // Devolver al padre de la celda que contiene "Henryll
lectores CSS básicos, aplicar diferentes estilos a diferentes estilos de vínculos al utilizar
.find(/td:eq(2)/) // Encontrar la tercera celda descendiente
.addClass (/highlight/); / / Añadir la clase "highlight"
selectores de atributo, añadir bandas a una tabla al utilizar los selectores personaliza-
dos jQuery : odd y : even o el selector CSS avanzado: nth- child () , y resaltar texto
Ciertamente, el DOM transversal en este ejemplo es sinuoso hasta el punto del ab- dentro de ciertas celdas de tabla al encadenar métodos jQuery.
surdo. En realidad no recomendaremos su uso, ya que existen métodos claramente más Hasta el momento, hemos utilizado el evento $ (document) . ready () para añadir
sencillos y más directos a nuestra disposición. El objetivo del ejemplo es simplemente una clase a un conjunto coincidente de elementos. En el siguiente capítulo, explorare-
demostrar la tremenda flexibilidad que nos permite el encadenamiento. mos formas en las que añadir una clase en respuesta a una variedad de eventos inicia-
Encadenar puede ser como decir todas las palabras de un párrafo en un solo respi- dos por el usuario.
ro, acabas rápidamente, pero puede ser difícil para cualquier otra persona entender-
lo. Dividirlo en múltiples líneas y añadir comentarios puede ahorrar más tiempo a la
larga.

Acceder a elementos DOM


Toda expresión de selector y la mayoría de los métodos jQuery devuelven un objeto
jQuery. Esto casi siempre es lo que queremos, debido a la iteración implícita y posibili-
dades de encadenamiento que permite.
Sin embargo, existen muchos puntos en nuestro código cuando necesitamos acceder
a un elemento DOM directamente. Por ejemplo, podemos necesitar poner disponible un
conjunto de elementos resultante a otra librería JavaScript. O bien podríamos necesitar
acceder al nombre de etiqueta de un elemento, que se encuentra disponible como una
propiedad del elemento DOM. Para estas situaciones poco comunes, jQuery proporciona
el método. get ( ) . Para acceder al primer elemento DOM referido por un objeto jQuery,
utilizaríamos . ge t ( O) . Siel elemento DOM se necesita dentro de un bucle, utilizaremos
. get (index) . Por lo tanto, si queremos saber el nombre de etiqueta de un elemento
con id= "my- element", escribiríamos:
var myTag = $ (/#my-element/) .get(O) .tagName;
~

.'

JavaScript tiene varias formas incorporadas de reaccionar a la interacción del usuario


y otros eventos. Para conseguir que una página sea dinámica y responda, necesitamos

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.

Eventos llevar a cabo tareas al cargar la página


Ya hemos visto cómo hacer que jQuery reaccione cuando se carga una página Web.
El manejador de evento $ (document) . ready () se puede utilizar para activar el có-
digo de una función, pero se puede decir mucho más sobre ello.

Planificación de la ejecución de código


En el primer capítulo hemos indicado que $ (document) . ready () era la forma de
jQuery de llevar a cabo tareas que se activaban típicamente por el evento onload in-
corporado de JavaScript. Sin embargo, aunque los dos tienen un efecto similar, activan
acciones en momentos ligeramente diferentes. El evento window . onload se lanza cuan-
do un documento se ha descargado completamente en el navegador. Esto significa que
ElII 3. Eventos Aprende jQuery 1.3 EJI
todo elemento en la página se puede manipular por JavaScript, lo que es de gran ayuda Luego podríamos asignarla dentro de nuestro código HTML:
para escribir código sin tener que preocuparse por el orden en que se carga.
<body on Loa.de vdo St u f f (); 11>

Por otro lado, un manejador registrado utilizando $ (document) . ready () se invo- (

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:

podemos escribir: jQuery(function($) (


/ / Código que utiliza $.
$() .ready(function() ( , });
// Nuestro código aquí ..
}) ;

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

In Prose, Belng a Ghost Story of Chrlstmas


by Charles Olckens . A Chrlstmas Carol

Preface In Prose, Being a Ghost Story of Christmas


by Charles..Dlckens
t HAVE endeevoured In this GhosUy ñtñe book, to ralse the GhoSI ot an Idea, whlchshall not put my raaders out or humour wlth
themselVes, wlth each other, wlth Ole season, or wlth me, May lt haunt lhelr houses pleasantly, and no ona wtsh to lay It.

Thelr lailhful Friend and Servant, Preface


C.D.
I HAVE endeavoured in this Ghostly little book, to raise the Ghost of an Idea, which
December, 1843.
shall no! put my readers out of humour wi!h themselves, with each other, with !he
Stave 1: Marley's Ghost season, or wi!h me. May it haunt their houses pleasantly,and no one wish to lay it.
MARlEY was dead: to begin with, There Is no doubt whatever about tha!. The reglster ot hls buriel was slgned by lhe clergyman, the
clerk, Ihe undertaker, and the chlef moumer. Scrooge slgned It: and Scrooge's name was 9000 upon 'Changa, ror anythlng he chose Their fai!hful Friend and Servan!,
lo put hls hand to. Old Martey was as dead as a door·nall.

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;
}) ;
}) ;

Podríamos realizar esta modificación de clase como hemos hecho anteriormente, al


Esto se combina con una regla CSS para la clase narrow: hacer referencia a cada botón por ID y aplicar o eliminar clases según sea necesario, pero
body.narrow .chapter
width: 400px;
.' en su lugar, exploraremos una solución más elegante y escalable que explota el contexto
en el que se ejecutan los manejadores de evento.
Cuando se activa cualquier manejador de evento, la palabra clave thi s hace referen-
cia al elemento DOM al que estaba anexado el comportamiento. Anteriormente hemos
Ahora, después de hacer clíc en el botón Narrow Column, se aplica su CSS
indicado que la función factory $ () podría tomar un elemento DOM como su argumento;
correspondiente y el texto se muestra de forma diferente, como se puede apreciar en la
ésta es una de las razones clave por la que se encuentra disponible. Al escribir $ (this)
figura 3.3. dentro del manejador de evento, creamos un objeto jQuery correspondiente al elemento,
y puede actuar sobre él como si lo hubiéramos localizado con un selector CSS.
Con esto en mente, podemos escribir:
$(this) .addClass(/selected/);

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 ••

}l; $(/#switcher .button/) .removeClass(/selected/);


$ (/#switcher-large/) .bind(/click/, function() $ (thist·addClass(/selected/) ;
$ (/body/) .removeClass(/narrow/) ; }l;
$ (/body/) .addClass(/large/); }) ;

$(/#switcher .button/) .removeClass(/selected/);


$(this) .addClass(/selected/); '"
Esta optimización aprovecha las tres características jQuery que hemos tratado. En
}l;
}l; primer lugar, iteración implícita es de nuevo de utilidad cuando vinculamos el mismo
manejador el iek a cada botón con una sola llamada a . bind () . En segundo lugar,
Ahora el conmutador de estilo proporciona información apropiada como se muestra poner en cola comportamientos nos permite vincular dos funciones al mismo evento
en la figura 3.4. eliek, sin que el segundo sobrescriba el primero. Por último, estamos utilizando las
posibilidades de uso de encadenamiento de jQuery para reducir la adición y eliminación
de clases a una línea de código cada vez.
S(ylo SWitcher

I Oefawl I LNa:~ I I Colurnn La~. Prtnl I Mayor consolidación


La optimización de código que acabamos de completar es un ejemplo de refacto-
A Christmas Carot rización, es decir, modificar código existente para llevar a cabo la misma tarea en una
In Prose, Being a Ghost Story of Christmas forma más eficiente o elegante. Para explorar más oportunidades de refactorización,
by Charles Olckens echemos un vistazo a los comportamientos que hemos vinculado a cada botón. El pa-
rámetro del método. removeClass () es opcional; cuando se omite, elimina todas las
Preface clases del elemento. Podemos mejorar nuestro código un poco al hacer uso de esto de
la siguiente forma:
I HAVE endeavoured in lhis Ghostly little book, lo raise the Ghost of an Idea, whiéh
shall not put my readers out of humour with themselves, wilh each other, with lhe $ (document) .ready(function() (
sea son, or with me. May it haunt their houses pleasanlly, and no one wish lo lay it, $ (/#switcher-default/) .bind(/click/, function() (
$ (/body/) .removeClass();
Their faithful Friend and Servant, }) ;
$ (/#switcher-narrow/) .bind(/click/, function()
C.D. $ (/body/) .removeClass() .addClass(/narrow/);
}) ;
December, 1843. $ (/#switcher-large/) .bind(/click/, function()
$ (/body/) .removeClass() .addClass(/large/);
Stave 1: Marley's Ghost }) ;

$U#switcher .button/) .bind(/cliCk/, function() {


Figura 3.4. Proporcionar información al usuario del botqn a~tivo. $(/#switcher .button/) .removeClass(/selected/);
$(this) .addClass(/selected/);
Generalizar las sentencias al utilizar el contexto de manejador nos permite ser aún }) ;

}) ;
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.

Mostrar y ocultar características avanzadas


Suponga que quisiéramos poder ocultar nuestro conmutador de estilo cuando no se
necesita. Una forma conveniente de ocultar características avanzadas es hacer que se
plieguen. Permitiremos que un clie en la etiqueta oculte los botones, dejando la etiqueta Figura 3.6. Mostrartodos los botones.
sola. Otro clic en la etiqueta restaurará los botones. Necesitamos otra clase para gestio-
nar los botones ocultos: Una vez más nos basamos en.iteración implícita; esta vez, para ocultar todos los bo-
.hidden { tones de una sola vez sin necesidad de un elemento que lo englobe.
display, none; Para este caso específico,jQuery proporciona otro mecanismo para la acción que es-
tamos llevando a cabo. Podemos utilizar el método. toggleClass () para comprobar
Podríamos implementar esta característica al almacenar el estado actual de los boto- automáticamente la presencia de la clase antes de aplicarla o eliminarla:
nes en una variable, y comprobar su valor cada vez que se hace clie en la etiqueta para $ (document) .ready(function() {
saber si añadir o eliminar la clase hidden en los botones. También podríamos compro- $ (j#switcher h3!) .click (function () {
bar directamente la presencia de la clase en un botón, y utilizar esta información para $(!#switcher .button!) .toggleClass(!hidden!);
}) ;
decidir qué hacer. En su lugar, jQuery proporciona el método. toggle (), que lleva a
}) ;
cabo esta tarea por nosotros.
En este caso, . toggleClass () es probablemente la solución más elegante, pero
. toggle () es una forma más versátil de llevar a cabo dos o más acciones diferentes
en alternancia.
Existen en realidad dos métodos. toggle () definidospor jQuery!Para información
sobre los efectos, consulte: http://docs.jquery.com/Effects/toggle.

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 ;
.'
}) ;

De nuevo utilizamos iteración implícita y contexto de evento para código breve y


sencillo. Ahora cuando pasamos por encima de cualquier botón, vemos nuestra clase Figura 3.8. Modelo de página,
aplicada como se muestra en la figura 3.7.
Para cualquier evento, existen múltiples elementos que podrían ser responsables ló-
gicamente de reaccionar.
Cuando se hace clic en el vínculo en esta página, por ejemplo, el <di v », <span>, y
<a> todos deberían tener la oportunidad de responder al clic, Después de todo, los tres
están bajo el cursor del ratón del usuario al mismo tiempo. El elemento <P>. por otro
lado, no es parte de esta interacción en absoluto.
A Christmas Carol Una estrategia para permitir que múltiples elementos respondan a un clic se denomina
captura de eventos. Con captura de eventos, el evento se pasa primero al elemento más
In Prose, Being a Ghost Story o, Christmas
global, y luego sucesivamente a los más específicos. En nuestro ejemplo, esto significa
by Charles Dlckens
que primero el evento se pasa a «d í.v», luego a c span», y por último a <a>, como se
Pteface
muestra en la figura 3.9.
Figura 3.7. Utilizar efectos rollover.

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).

Figura 3.10. Burbujeo de eventos.


Alterar el viaje: el objeto event
Como cabe esperar, diferentes desarrolladores de navegador originalmente decidieron
diferentes modelos para programación de evento. El estándar DOM que se desarrolló es- Ya hemos visto una situación en la que el burbujeo de eventos puede provocar pro-
pecificaba que se deberían utilizar ambas estrategias: primero el evento se captura desde blemas. Para mostrar un caso en el que. hover () no ayuda a nuestra causa, alteraremos
elementos generales a específicos, y luego el evento burbujea hasta arriba del árbol DOM. el comportamiento colapsado que hemos implementado anteriormente.
Los manejadores de eventos se pueden registrar para cualquier parte del proceso. Suponga que deseamos ampliar el área sobre la que se puede hacer clic que activa el
No todos los navegadores se han actualizado para coincidir con este nuevo estándar, y contraer o expandir del conmutador de estilo. Una forma de hacer esto es mover el ma-
en aquellos que lo soportan la captura debe estar habilitada específicaroente. Para propor- nejador de evento desde la etiqueta, <h3 », a su elemento <di v » contenedor:
cionar consistencia entre navegadores, por lo tanto, jQuery siempre registra manejadores
$ (document) .ready(function() {
de evento para la fase de burbujeo del modelo. Siempre podemos asumir que el elemento $ (/#switcher/) .click(function() {
más específico tendrá la primera oportunidad de responder a cualquier evento. $(/#switcher .button/) .toggleClass(/hidden/);
}) ;
}J;
Efectos secundarios del burbujeo de eventos
Esta alteración hace que se pueda hacer clic sobre todo el área del conmutador de
El burbujeo de eventos puede causar comportamiento no esperado, especialmente estilo para alternar su visibilidad. La desventaja es que hacer clic en un botón también
cuando el elemento erróneo responde a un mouseover o mouseout. Considere un ma- contrae el conmutador de estilo después de que el estilo en el contenido se ha alterado.
nejador de evento mouseou t anexado al <di v » en nuestro ejemplo. Cuando el cursor del Esto se debe al burbujeo de evento; el evento se gestiona primero por los botones, luego
ratón del usuario sale del <di V>, el manejador mouseout se ejecuta según lo previsto. Se pasa al árbol DOM hasta que llega a <di v id= swi tcher 11 », donde nuestro nuevo
11

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 }) ;

Como antes, necesitamos añadir un parámetro a la función que estamos utilizando


Ahora tenemos el objeto event disponible como la variable event dentro de nuestro como el manejador click, de modo que tenemos acceso al objeto event. Luego sim-
manejador. La propiedad event . target puede ser de utilidad al controlar dónde ocurre plemente invocamos event. stopPropagation () para impedir que cualquier otro
un evento. Esta propiedad es una parte de la API DOM, pero no se implementa en todos elemento DOM responda al evento. Ahora nuestro clic se gestiona por los botones, y
los navegadores; jQuery amplía el objeto event según sea necesario para' proporcionar solamente los botones; clics en cualquier otro lado en el conmutador de estilo lo colap-
la propiedad en cada navegador. Con. target, podemos determinar qué elemento en sará o expandirá.
el DOM era el primero en recibir el evento, es decir, en el caso de un evento click, el
elemento sobre el que se hace dic. Recordando que this nos da el elemento DOM que
gestiona el evento, podemos escribir el siguiente código: Acciones predeterminadas
$ (document) .ready(function() { Si nuestro manejador de evento click se registrara sobre un elemento de vínculo
$ (/#switcher/) .click(function(event} {
if (event.target == this) {
«a» en lugar de un «d í.v» genérico, tendríamos otro problema. Cuando un usuario
$(/#switcher .button/) .toggleClass(/hidden/); hace die sobre un vínculo, el navegador carga una nueva página. Este comportamiento
} no es un manejador de evento en el mismo sentido que hemos venido discutiendo; en su
}} ;
lugar, se trata de la acción predeterminada para un die sobre un elemento de vínculo. De
}) ;
forma similar, cuando se pulsa la tecla Intro mientras el usuario edita un formulario, se
Este código se asegura de que el elemento sobre el que se hace clic sea <di v activa el evento submi t en el formulario, pero entonces el envío del formulario ocurre en
id=" swi tcher », no uno de sus subelementos. Ahora hacer clic en los botones no ple-
11
realidad después de esto. Sino se desean estas acciones predeterminadas, invocar. sto-
gará el conmutador de estilo, y hacer clic en el fondo del conmutados-sí, Sin embargo, pPropaga ti on () sobre el evento no ayudará. Estas acciones no ocurren en ningún sitio
hacer clic en la etiqueta, <h3 », ahora no hace nada, porque también es un subelemento. en el flujo normal de la propagación de evento. En su lugar, el método. preventDe-
En lugar de situar esta comprobación aquí, podemos modificar el comportamiento de faul t () servirá para detener el evento antes de que se active la acción predeterminada.
los botones para conseguir nuestros objetivos.

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

Hemos utilizado un nuevo método aquí, denominado. is () . Este método acepta


Delegación de evento las expresiones de selector que hemos investigado en el capítulo anterior, y comprue-
ba el objeto [Query actual contra el selector. Si al menos un elemento en el conjunto se
El burbujeo de eventos no siempre es un obstáculo; a menudo lo podemos utilizar corresponde con el selector, . i s () dfvuelve true. En este caso, $ (event . target) .
para obtener beneficio. Una técnica estupenda que hace uso del burbujeo se denomina i s (/ . but ton/) pregunta si el elemento sobre el que se ha hecho clic tiene asignada
delegación de evento. Con ello, podemos utilizar un manejador de evento sobre un solo una clase but ton. Si es así, continuamos con el código desde antes, con un cambio sig-
elemento para realizar el trabajo de muchos. nificativo: la palabra clave this ahora se refiere a <di v id=" switeher" », de modo
que cada vez que estamos interesados en el botón que se puede hacer clic tenemos que
referirnos a él con event . target.

EnjQuery 1.3, sehan incorporado un nuevo par de métodos, . li ve () y . die ( ) . Estos


métodos llevan a cabo las mismas tareas que. bind () y .unbind () , pero por detrás
utilizan delegación de evento para obtener los beneficios que describiremos en este También podemos comprobar la presencia de una clase en un elemento con un método
apartado. La documentación sobre estos métodos se puede encontrar en: http: / / abreviado, .hasClass () . El método . is () es más flexible, sin embargo, y puede
comprobar cualquier expresión de selector.
docs.jquery.com/Events/live.

En nuestro ejemplo, existen tres elementos ed i v elass="button"


.'
> que tienen
Sin embargo, tenemos un efecto secundario no intencionado en este código. Cuando
se hace clic en un botón ahora, el conmutador se contrae, como hizo antes de que aña-
anexados manejadores el iek. ¿Pero qué pasaría si hubiera muchos? Esto es más común diéramos la llamada a . stopPropagation () . El manejador para alternar la visibili-
que lo que uno pudiera pensar. Considere, por ejemplo, una tabla extensa de información dad del conmutador está ahora vinculado al mismo elemento que el manejador para los
en la que cada fila tiene un elemento interactivo que requiere un manejador eliek. La botones, por lo que detener el burbujeo de evento no impide que se active el alternar la
iteración implícita facilita asignar todos estos manejadores eliek, pero el rendimiento visibilidad. Para evitar este problema, podemos eliminar la llamada. stopPropaga-
puede sufrir debido a la iteración en bucle que se realiza internamente en jQuery, y de- tion () y en su lugar añadir otra comprobación. is () :
bido a la huella de memoria de mantener todos los manejadores. $ (document) .ready(function() (
En su lugar, podemos asignar un solo manejador el iek a un elemento ancestro en $ (/#switcher/) .click(function(event) (
el DOM. Un evento eliek ininterrumpido llegará con el tiempo al ancestro debido al if (!$(event.target) .is(/.button/)) {
burbujeo de evento, y nosotros podemos realizar nuestro trabajo ahí. $(/#switcher .button/) .toggleClass(/hidden/);

A modo de ejemplo, apliquemos esta técnica a nuestro conmutador de estilo (aunque }


}) ;
el número de elementos no requiere el enfoque). Como hemos visto antes, podemos uti- }) ;

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 ••

adelante. Para utilizar espacio de nombres, necesitamos regresar al método no abreviado


de vincular ntanejadores de evento, el propio método . bind ( ) .
La delegación de evento es también de utilidad en otras situaciones que veremos más El primer parámetro que pasamos a . bind () es el nombre del evento JavaScript del
adelante, como cuando se añaden nuevos elementos por métodos de manipulación que queremos estar pendiente. Podemos utilizar una sintaxis especial aquí, sin embargo,
DOM o rutinas AJAX. que nos permite subcategorizar el evento.
$Idocument) .readylfunctionl) (
$I/#switcher/J .bindl/click.collapse/, functionlevent)
if 1!$levent.target) .isl/.button/)) (

Eliminar un manejador de evento $I/#switcher


}
.button/) .toggleClassl/hidden/);

}) ;

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.
}) ;

$I/#switcher-narrow, #switcher-large/) .clicklfunctionl)


$I/#switcher/) .unbindl/click/);
}) ;
}) ; Volver a vincular eventos
Ahora cuando se hace clic en un botón como Narrow Column, el rñanejador eliek
Ahora hacer clic en el botón N arrow Column o Large Print hace que la funcionalidad
en el conmutador de estilo <di v» se elimina, y hacer clic en el fondo de la caja ya no
de contraer el conmutador de estilo se desactive. Sin embargo, queremos que regrese el
lo contrae. Sin embargo, el botón ya no funciona. Está anexado al evento eliek del
comportamiento cuando se pulsa el botón Default. Para hacer esto, necesitaremos vol-
conmutador de estilo <di v » también porque volvimos a escribir el código de gestión
ver a vincular el manejador siempre que se hace clic en Default.
de botón para utilizar delegación de evento. Esto significa que cuando invocamos
En primer lugar, deberíamos dar a nuestra función de manejador un nombre de modo
$ (/#switeher 1) . unbind (/ cliek/), se eliminan ambos comportamientos.
que podamos utilizado más de una vez sin repetimos:
$Idocument) .readylfunction() (
Espacio de nombres de evento var toggleStyleSwitcher = function(event)
if (!$(event.target) .is(/.button/)) (
$I/#switcher .button/) .toggleClass(/hidden/);
Necesitamos que nuestra llamada. unbind () sea más específica, de modo que no
elimine los dos manejadores elick que hemos registrado. Una forma de hacer esto es };
utilizar espacio de nombres de evento. Podemos incorporar información adicional cuan- $I/#switcher/) .bind I/click. collapse/, toggleStyleSwitcher);
do se vincula un evento que nos permita identificar ese determinado manejador más ».
1"

_ 3. Eventos Aprende jQuery 1.3 ••

$ (/#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:

$ (document) .ready(function() ( $ (doéument) .ready(function() (


var toggleStyleSwitcher = function(event) $ (/#switcher/) .one(/click/, toggleStyleSwitcher);
if (!$(event.target) .is(/.button/) { }) ;

$(/#switcher .button/) .toggleClass(/hidden/);


Esto hará que la acción de alternar ocurra solamente una vez.
};

$ (/#switcher/) .bind(/click.collapse/, toggleStyleSwitcher); .'


$ (/#switcher-narrow, #switcher-large/) .click(function()
Simular inleracción de usuario
$ (/#switcher/) .unbind(/click.collapse/);
}) ;
A veces es conveniente ejecutar el código que hemos vinculado a un evento, incluso si
$ (/#switcher-default/) .click (function () ( no se dan las circunstancias normales del evento. Por ejemplo, suponga que quisiéramos
$ (/#switcher/) que nuestro conmutador de estilo empiece en su estado contraído. Podríamos realizar
.bind(/click.collapse/, toggleStyleSwitcher);
}) ;
esto al ocultar botones desde la hoja de estilo, o al invocar el método. hide () desde un
}) ;
manejador $ (document) . ready ( ) . Otra forma sería simular un clic en el conmutador
de estilo de modo que se active el mecanismo para alternar que ya hemos establecido.
Ahora el comportamiento de alternar está vinculado cuando se carga el documento, El método. trigger () nos permite realizar precisamente esto:
desvinculado cuando se hace clic en Narrow Column o Large Print, y vuelto a vincular
$ (document) .ready(function() (
cuando se hace clic en Normal después de esto.
$ (/#switcher/) .trigger(/click/) ;
Hemos evitado un peligro potencial aquí. Recuerde que cuando se vincula un mane- }) ;
jador a un evento en jQuery, los manejadores anteriores siguen en vigor. Esto podría pa-
recer significar que si se hace clic en Normal múltiples veces en sucesión, se vincularían Ahora justo cuando se carga la página se contrae el conmutador, como si se hubiera
muchas copias del manejador togg 1eS t y 1e Swi t che r, provocando un comportamien- hecho clic en él, como se muestra en la figura 3.11. Si estuviéramos ocultando conteni-
to extraño cuando se hizo clic en <di v », De hecho, si hubiéramos utilizado funciones do que quisiéramos que personas sin JavaScript pudieran ver, ésta sería una forma de
anónimas en nuestro ejemplo, éste sería el caso. Pero puesto que asignamos a la función implementar degradación.
un nombre y utilizamos la misma función en todo el código, el comportamiento sólo se
vincula una vez. El método. bind () no anexará un manejador de evento a un elemen-
to si ya se ha anexado. Como otro beneficio a nombrar esta función, ya no necesitamos
utilizar espacios de nombres. El método . unbind () puede tomar una función como
segundo argumento; en este caso, desvincula solamente ese manejador específico. A Chrlstmas Carol

In Prosa, Belng a Ghost Story 01 Chrlstmas


$ (document) .ready(function() (
by Charles Dlckens
var toggleStyleSwitcher = function(event) (
if (!$(event.target) .is(/.button/» { Preface
$(/#switcher .button/) .toggleClass(/hidden/);

};
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

$ (/#switcher/) .click(); case /L/:


}) ; $ (/#switcher-Iarge/) .click () ;
break;
}
Eventos de teclado j);
}) ;

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

if ($(event.target) .is(/.button/)) { • Implementar delegación de evento para reducir el número de manejadores de


if (event.target.id == /switcher-default/) evento vinculados necesarios en una página.
setBodyClass(/default/) ;
} • Invocar .unbind () para eliminar un manejador de evento con el que hemos
if (event.target.id == /switcher-narrow/) terminado. ~
setBodyClass(/narrow/) ;
} • Segregar manejadores de evento relacionados con espacios de nombre de evento
else if (event.target.id == /switcher-large/) (
de modo que se pueda actuar sobre ellos como un grupo.
setBodyClass(/large/) ;
• Hacer que los manejadores de evento vinculados se ejecuten con . trigger () .
}
}) ; • Utilizar manejadores de evento de teclado para reaccionar a pulsar una tecla por
el usuario con. keyup ( ) .
// Invocar setBodyClass() cuando se pulsa una tecla
$ (document) .keyup(function(event) ( Utilizados juntos, podemos utilizar estas posibilidades de uso para crear páginas bas-
switch (String.fromCharCode(event.keyCode))
case /D/,
tante interactivas. En el siguiente capítulo, aprenderemos cómo proporcionar feedback
setBodyClass(/default/) ; visual para el usuario durante estas interacciones.
break;
case /N/,
setBodyClass (/narrow/) ;
break;
case /L/,
setBodyClass(/large/) ;
break;
}
}lo
}) ;

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

.' ."/""",,, ... -~~'.". ~:~>~,


.... '~. -; "~;-'"" .~.'" . "'(j)' , ~. ..''
". .'-"
\'1,.~../. /"'h'~h'·
.'~ .' ..
t·,,·,,·
'-
--." /
/~.........
<, .' (~
"t;
~ n '. ..,
,~~
.. 7\w,; " _ 1
/'~"
<!>
~

"'" . ·tá' !
.~ ~,~?•.f~(~'
·QP\ /'W \
.: :""'>',,,-,
\-~! 1"\
@r.-...., '"
/ : .'~\-(!)".,.:
·F"··
ti~,
''''1F
""

. -, ~, . --", ,~ '\
~...,'
\~, ~it·,/
~~-r.
{, '" ()
o~'~ ®
,_"

\,~ <.!l'\.'~ , ' ,


'I'~.'U""
#"~.
"'(3). -.
/~.', ~ """
,/ ---~.
'. '. '.
l.'~O ..
'b.".,
,~~' .., ''''''''
..'-..
'V' ~'.'.@
x., ,
0...•
/~~'
" ••.'-..\ ,..--''.
"~
~"~ i . A!> \~,~ r--.... ~. 4:> ....
~ ,. ® \ ../~'0-----..,-· .~.'
,.~.. I O- ,.'.; hg,~;.
í'~. ' @) @l)~;/ _-'~<, ~
j
", ._~

.•.---.,¡, . -~. •..• ,.~: '{ . '. "~'/ . -....... --::::' e


.••••••..
,.--~:\ ·.•... tJ\,:.~ '. "(!l.. '. ' -:",'f.5{. ..~,/ ,.::\. '()
~$C"\l\,
'1.:;:;> _\ '
r.) '...
~
>'--".
,,
.
.'
I ~..
.'
;' . .
..
®
r-:.:
'!\,.c;,.. "'r':'~.
I
, • ." /'
~
~

. ~ ","". ". ~" ' ' .1 '.' . .. (@I;.--.,.


fr~l.,~",
- .!~ ] ...
, -::.
I~
.~(':'"':"~~~"-"'I,
~.
---_.-..
'A
--'iW
'.•~
' - --rsp -- .•,
a.:;--..""-
',' . "
>

.'

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,

Modificaciones CSS en línea


Antes de pasar a los ingeniosos efectos jQuery, echemos un rápido vistazo a CSS. En
capítulos anteriores hemos estado modificando la apariencia de un documento al defi-
nir estilos para clases en una hoja de estilo aparte y luego añadir o eliminar estas clases
con jQuery. Típicamente, éste es el proceso preferido para aplicar CSS en HTML porque
respeta el rol de la hoja de estilo al tratar con la presentación de una página. Sin embar-
go, existen momentos en los que necesitamos aplicar estilos que no se han podido fácil-
mente definir en una hoja de estilo. Afortunadamente, jQuery ofrece el método, css ()
para tales ocasiones. Este método actúa como un get y un set, Para obtener el valor de
una propiedad de estilo, simplemente pasamos el nombre de la propiedad como una
E!II 4. Efectos
Aprende jQuery 1.3 lIlJII

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 }) ;

nombres de propiedad si se escriben en notación DOM. •


A continuación, el tamaño de fuente se puede descubrir fácilmente al utilizar el mé-
todo . css (): $ ( di v. speech
I css ( fontSi ze
I ) • I Sin embargo, puesto que el
I ) •

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);
;

this continent a new nation, conceived in liberty, and dedicated


to the proposition that all men are created equal. Aquí seguimos comprobando el valor de this. id Ycambiando el tamaño de fuente
en base a ello, pero si su valor no es 'swi tcher-large' ni ' switcher-small ',por
Figura 4.2. Aumentar el tamaño del texto. defecto se establecerá en el tamaño de fuente inicial.

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 •

€oefaUlt) ~:8igger) €smaller~


l
Fourscore and· seven years ago our fathers brougtíbrorth on thls contlnent a new natíon, concelved in liberty, and dedicated
to the proposltlon that all men are created equal.
developer. mozilla. org/en/CSS/display/ y examinelosejemplosenhttps: / /
read more
developer.mozilla.org/samples/cssref/display.html.
Figura 4.3. Ocultar el segundo párrafo.

Esta característica de . show () y . hide () es especialmente de utilidad cuando se


Luego, cuando el usuario hace dic en el vínculo read more al final del primer párra-
ocultan elementos cuya propiedad display predeterminada se anula en una hoja de / fo, ese vínculo se oculta y se muestra el segundo párrafo:
estilo. Por ejemplo, el elemento <li> tiene la propiedad display:block por defec-
to, pero podríamos querer cambiarla a display: inline para un menú horizontal. $ (document) .ready(function() (
$('p:eq(l) ') .hide();
Afortunadamente, utilizando el método . show () en un elemento oculto como una de
$ ('a.more') .click(furlction()
estas etiquetas <1i >no lo restablecerará a su predeterminado display: block, porque $ ( 'p :eq (1) ,) .show() ;
eso situaría el <1i > en su propia línea. En su lugar, el elemento se restaura a su estado $(this) .hide();
display: inline previo, conservando así el diseño horizontal. Una demostración rápi- return false;
}) ;
da de estos dos métodos se puede establecer al añadir un segundo párrafo y un vínculo }) ;
de leer más (read more) después del primer párrafo en el HTML:
cdiv id="switcher't> Observe el uso del return false para impedir que el vínculo active su acción pre-
cdiv class=111abel">Text Sizec/div> determinada. Ahora el texto se parece a lo que se muestra en la figura 4.4:
ebutton id=ltswitcher-defaultU>Defaultc/button>
ebutton id=l1switcher-large1t>Biggerc/button>
ebutton id=l1switcher-smalltl>Smallerc/button> Text Slze
-,
</div>
cdiv class=rrspeech">
f tDefauftq ~.Rigger'3 tSmaller,)

<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

Aparecer y desaparecer de forma paulatina- $(this) .text('read less');


else (
$firstPara.fadeOut('slow') ;
Aunque los métodos . show () Y . hide () son llamativos, a veces pueden ser $(this) .text('read more');
demasiado buenos. Afortunadamente, jQuery ofrece un par de otras animaciones
precreadas para un efecto más sutil. Por ejemplo, para hacer que todo el párrafo apa- return false¡
rezca al aumentar gradualmente la opacidad, podemos utilizar. fadeln ( , slow' ) en )) ;

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

Animar múltiples propiedades


Con el método. animate (), podemos modificar cualquier combinación de pro-
r
I
derecho mientras se aumenta su altura en 20 píxeles y cambiar el ancho de borde en 5
píxeles. Por 10'tanto, hagamos eso con el cuadro <di v Ld» " swi tcher" >. En la figura
4.7 tiene el asrecto que muestra antes de animado.
,
piedades simultáneamente. Por ejemplo, para crear un efecto deslizar y desvanecer si-
multáneamente cuando alternamos el segundo párrafo, simplemente añadimos el par Abraham Li
propiedad-valor de height al mapa de propiedades de . animate () :
$ (document) .ready(function() {
$ ( 'p: eq (1) ,) .hide () ;
$('a.more') .click(function() Fou,"core 5 brought forth on thls oontlnent a new natíon, ccncelved In llberty, and dedlcated
$ ('p:eq (1) ') .animate ({ to the propositlon that al! men are createc equal.
opacity: 'toggle',
-'
read more
height: 'toggle'
},
I slow'); Figura 4.7. Aspectodel cuadro sin animación.
var $link = $(this);
if ( $ link .text () == "read more" ) { Con un diseño de ancho flexible, necesitamos calcular la distancia que necesita reco-
$link.text('read less'); rrer la caja antes de alinearse a la derecha de la página. Asumiendo que el ancho del pá-
else {
$link.text('read more');
rrafo es 100por cien, podemos restar el ancho de la caja Text Size del ancho del párrafo.
Aunque el método. width () de jQuery será de utilidad para dichos cálculos, no tiene
return false¡ en cuenta el ancho del relleno derecho e izquierdo o el borde derecho e izquierdo. En
}) ;
jQuery versión 1.2.6,también tenemos el método. outerWidth () a nuestra disposición.
}) ;
Esto es lo que utilizaremos aquí para evitar tener que añadir anchos de relleno y bordes.
Además, no solamente tenemos a nuestra disposición las propiedades de estilo utiliza- Para el objetivo de este ejemplo, activaremos la animación al hacer die en la etiqueta Text
das para los métodos del efecto abreviado, sino también otras propiedades como: 1eft, Size, por encima de los botones. Aquí tiene el aspecto que debería tener el código:
t op, fontSize, margin, padding, y borderWidth. Recuerde al script que cambie $ (document) .ready(function() {
el tamaño de texto de los párrafos. Podemos animar el aumentar y disminuir el tamaño $('div.label') .click (function () {
con sólo sustituir el método. animate () por el método. css () : var paraWidth = $('div.speech p') .outerWidth();
var $switcher = $(this) .parent();
$ (document) .ready(function() { var switcherWidth = $switcher.outerWidth();
var $speech = $('div.speech'); $switcher.animate({left: paraWidth - switcherWidth,
var defaultSize = $speech.css('fontSize') i height: '+=20px', borderWidth: 'spx'}, 'slow');
$('#switcher button') .click(function() { }) ;

var num = parseFloat( $speech.css('fontSize'), 10 ); }) ;

switch (this. id) {


case 'switcher-large': Observe que la propiedad height tiene += antes del valor del píxel. Esta expresión,
num *= 1.4; incorporada en jQuery 1.2, indica un valor relativo. Por lo tanto, en lugar de animar la
break;
case 'switcher-small t:
altura a 20 píxeles, la altura se anima a 20 píxeles mayor que la altura actual.
num /= 1. 4; Aunque este código aumenta con éxito la altura del <di v » y ensancha su borde, en
break; el momento la posición 1eft no se puede cambiar. Necesitaremos habilitar cambiar su
default: posición en el CSS.
num = parseFloat(defaultSize, 10);
}
$speech.animate({fontSize: num + "px
'slow') ;
! }.

Posicionar con CSS


}) ;

}) ;
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:

AbrahamLincoln's Gettysburg Address 1. Desvanecer su opacidad a .5 con. fadeTo () .


,.- "-.., 2. Moverlo a la derecha con. animate () .
TextSlze

( (~~
<,
~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({

detrás de otro. 'left': paraWidth - switcherWidth


}, 'slow')
.fadeTo('slow',l.O)
.slideUp ('slow' )
Trabajar con un solo conjunto de elementos .slideDown('slow')¡
}) ;

}) ;
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 )

}, {duration: 'slow', queue: false}) .dequeueJ);


.fadeTo('slow',l.O) })
.slideUp (,slow' ) ,slideDown('slow')¡
.slideDown('slow') ; }) ;

}) ; }) ;

}) ;
" 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:;:.

en la cadena, ocurre inmediatamente con el dic. <div class=111abel >Text


ll Size</div>
<button id=lIswitcher-defaultll>Default</button>
Una forma en la que podemos añadir métodos no de efecto a la cola es utilizar el
<button id=lrswitcher-largetl>Bigger</button>'
método denominado . queue ( ) . Aquí tiene el aspecto que tendría en el ejemplo que <button id=tlswitcher-small">Smaller</button>
estamos desarrollando: </div>

---------------- 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 }) ¡

equal. $('p,eq(3) ') .css('backgroundColó~', '#eee') .hide();


</p> }) ;

<p>Now we are engaged in a great civil war, testing whether


that nation, or any nation so conceived and so dedicated, Una captura de estos dos efectos confirma que, de hecho, se producen prácticamente
can long endure. We are met on a great battlefield of al mismo tiempo, como muestra la figura 4.9:
that war. We have come to dedícate a partian of that
field as a final resting-place for those who here gave
their lives that the nation might live. It is altogether e brave men, livIng and dead, wha stnJggled hera have consecrated It, far above our poor power to add or detract. The
rld wlll IIttle note nor Ion m sa h r I e e wh t th did here. It Is ~ rus
fitting and proper that we should do this. But, in a
larger sense, we cannot dedícate, we cannot consecrate, 1~~~~']j~~~~ ~~H~lii~~:w~"t~;·~\t~ - -
we cannat hallow, this ground.
</p> Figura 4.9. Los efectos ocurren al mismo tiempo.
<a href="#" class="moreu>read maree/a>
<p>The brave men, living and dead, who Btruggled
here have consecrated it, far above our poor
El tercer párrafo, que empezó visible, está a mitad de camino en el desplazarse hacia
power to add or detracto The wor1d wi11 1ittle arriba al mismo tiempo que el cuarto párrafo, que empezó oculto, está a mitad de cami-
note, nor long remember, what we say here, but it no en el desplazarse hacia abajo.
can never forget what they did hare. It ia for ua
the living, rather, te be dedicated here to the
unfinished work which they who fought here have
thus far so nobly advanced.
Rellamadas
</p>
<p>It is rather for ua te be here dedicated to the Con el objetivo de permitir encolar efectos en diferentes elementos, jQuery propor-
great task remaining before us-that from ciona una función de rellamada para cada método efecto. Como hemos visto con los
these honored dead we take increased devotion to
manejadores de evento y con el método . queue ( ) , las rellamadas son simplemente fun-
that cause for which they gave the 1ast fu11
measure of devotion-that we here highly
ciones pasadas como argumentos de método. En el caso de los efectos, aparecen como
resolve that these dead shall not have died in el último argumento del método.
vain-that this nation, under God, shall Si utilizamos una rellamada para encolar los dos efectos de desplazamiento, pode-
have a new birth of freedom and that government
mos hacer que el cuarto párrafo se desplace hacia abajo antes de que el tercer párrafo
of the people, by the people, for the people,
sha11 not perish from the earth.
se desplace hacia arriba. Veamos primero cómo configurar el método. slideDown ()
</p> con la rellamada:
</div>
$ (doeument) .ready(funetion()
$ ( "p :eq (2) , )
A continuación, para ayudamos a ver qué sucede durante el efecto, asignaremos al
_css('border', 'lpx salid #333')
tercer párrafo un borde de 1 píxel y al cuarto párrafo un fondo gris. También ocultare- .eliek(funetion() {
mos el cuarto párrafo cuando el DOM esté listo: $(this) .next() .slideDown('slow',function()
// el código aquí se ejecuta después de que el desplazamiento
$ (doeument) .ready(funetion() { // hacia abajo del tercer párrafo ha terminado
$ ( 'p,eq (2) , ) . ess ('border', 'lpx sol id #333'); }) ¡
$ ("p : eq (3) ,) .ess ('baekgroundColor', '#eee ') .hide (); }) ;
}) ; $('p,eq(3) ') .ess('baekgroundColor', '#eee') .hide();
j);
Por último, añadiremos el método. click () al tercer párrafo de modo que cuando
se hace clíc, el tercer párrafo se desplaza hacia arriba (y fuera de la vista), mientras que Sin embargo, necesitamos tener cuidado aquí sobre qué es lo que se va a desplazar
el cuarto párrafo se desplaza hacia abajo (a la vista): hacia arriba. El contexto ha cambiado para $ (thi s) porque la rellamada está dentro
del método. slideDown (). Aquí, $ (this) ya no es el tercer párrafo, como era en el
$ (doeument) .ready(function()
$ ('p,eq(2)')
momento del método. click () i en su lugar, puesto que el método. slideDown ()
.ess('border', 'lpx sol id #333') está anexado a $ (thi s) . next ( ), todo dentro de ese método ahora ve $ (thi s)
lIiII 4. Efectos
r
Aprende jQuery 1.3 lID
como el siguiente hermano, o el cuarto párrafo. Por lo tanto, si situamos $ (this) . J. 'slow')
slideUp ( slow ') dentro de la rellamada, acabaremos ocultando el mismo párrafo
1
.fameTo('slow',l.O)
.slideUp('slow', function()
que hemos acabado de poner visible. !$switcher
Una forma sencilla de mantener la referencia de $ (thi s) estable es almacenarla en .css('backgroundCola~" '#fOO');
una variable dentro del método. c Li ck (), como var $thirdPara = $ (this) . })
Ahora $thirdPara hará referencia al tercer párrafo, fuera y dentro de la rellamada. -slideDown('slowt) ;
} );
Aquí tiene el aspecto del código utilizando nuestra nueva variable: }) ;

$( 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

$thirdPara de desplazarse hacia arriba, y antes de que se desplace hacia abajo.


.css('border', 'lpx solid #333')
.click(function() {
$ (this) .next () .slideDown ( 'slow' ,function
$thirdPara.slideUp('slow') ;
()
En pocas palabras
}) ;

}) ; 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 ( ) .

Figura 4.10. Los dos párrafos son visibles.


Resumen
Ahora que hemos tratado las rellamadas, podemos regresar al código anterior en este
capítulo en el que encolamos un cambio de color de fondo cerca del final de una serie de Al utilizar los métodos de efectos que hemos explorado en este capítulo, ahora debe-
efectos. En lugar de utilizar el método. queue ( ) , como hicimos anteriormente, simple- ríamos poder aumentar y disminuir incrementalmente el tamaño del texto al utilizar el
mente podemos utilizar una función de rellamada: método. ess () o . animate () .También deberíamos poder aplicar varios efectos para
ocultar y mostrar gradualmente elementos de página de diferentes formas y también
$ (document) .ready(function() (
$('div.label') .click(function() {
animar elementos, simultáneamente o de forma secuencial, en una serie de formas.
var paraWidth = $('div.speech p') .outerWidth() ; En los cuatro primeros capítulos del libro, todos nuestros ejemplos han implicado
var $switcher = $(this) .parent(); manipular elementos que se han insertado directamente en el HTML de la página. En el
var switcherWidth = $switcher.outerWidth(); siguiente capítulo, exploraremos formas en las que podemos utilizar jQuery para crear
$switcher
nuevos elementos e insertarlos en el DOM allí donde elijamos.
.fadeTo('slow' ,0.5)
.animate({
'left': paraWidth - switcherWidth
\,

/'

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-

D lar el atributo de clase (o, en jerga de programación DOM, la propiedad className).


El método. addClass () crea o añade al atributo, mientras que. removeClass () lo
elimina o lo acorta. Añada a estos el método. toggleClass () que alterna entre añadir
o eliminar una clase, y tenemos una forma eficiente y robusta de gestionar clases.
No obstante, el atributo class es sólo uno de los varios atributos a los que pode-
mos necesitar acceder o cambiar: por ejemplo, id y rel y href. Para manipular estos
atributos, jQuery proporciona los métodos. attr () y . removeAttr (). Podríamos
incluso utilizar. at tr () y . removeAt tr () para modificar el atributo class, pero los
métodos especializados. addClass () y . removeClass () son mejores en este caso
porque gestionan correctamente casos donde múltiples clases se aplican a un solo ele-
mento, como <di v c Las s « " first second" >.
mi 5. Manipulación DOM Aprende jQuery 1.3 lID
atributo común para todos los vínculos, podríamos hacerlo con una sola línea de código
Atributos que no son clase dentro de nuestro manejador $ (document) . ready () :

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

Figura 5.1. Párrafos sin vínculos para volver arriba.

"
)
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.

Insertar nuevos elementos


Una característica que normalmente se ve en las páginas FAQ es el vínculo volver
arriba (back io tap) que aparece después de cada par pregunta-respuesta. Se podría decir jQuery tiene dos métodos para insertar elementos antes de otros elementos:
que estos vínculos no sirven a ninguna finalidad semántica y por lo tanto se pueden . insertBefore () y . before ( ) . Estos dos métodos tienen la misma función; su di-
incluir vía JavaScript de forma legítima como una mejora para un subconjunto de los ferencia se encuentra solamente en cómo se encadenan a otros métodos. Otros dos mé-
visitantes a una página. Para nuestro ejemplo, añadiremos un vínculo "volver arriba" todos. insertAfter () y . after (), mantienen la misma relación entre sí, pero como
después de cada párrafo, así como el ancla al que nos llevarán estos vínculos. Para em- sugiere su nombre, insertan elementos detrás de otros elementos. Para los vínculos de
pezar, simplemente creamos los nuevos elementos: volver arriba utilizaremos el método. insertAfter () :
lmJI 5. Manipulación DOM Aprende jQuery 1.3 lIf.I
$ (document) .ready(function() { $ (document) .ready(function() {
$ (!<a href="#top">back to top<!a>/) $(/<a href="#top">back to top<!a>!)
.insertAfter(/div.chapter p/)¡ .insertAfter(!div.chapter p!);
$ (!<a id="top"><!a>!); $ (/ <a id= "t.op" name« t op" ></a>/)
ti

}) ; .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&mdash;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&mdash;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 ....

<p >The birth of a True Equilateral Triangle from Isosceles


parents is the subject af rejoicing in Our country for many
Figura 5.4. Destacar los pies de página.
furlongs raund. After a strict examination conducted by the
Sanitary and Social Board, the infant, if certified as Ahora podemos tomar los pies de página e insertar los entre <di v e 1as s ="chapt e r ">
Regular, is with salemn ceremonial admitted into the class y <di v .i d» "footer" >. Recuerde que incluso en casos de iteración implícita, el orden
of Equilaterals. He is then immediately taken from his
proud yet sorrowing parents and adopted by some childless de inserción está predefinido, empezando al principio del árbol DOM y descendiendo.
Equilateral. <span class=lIfootnote">The Equilateral is Puesto que es importante mantener el orden correcto de los pies de página en su nuevo
bound by oath never to permit the child henceforth to enter lugar en la página, deberíamos utilizar. insertBefore (/#footer /) .
his former horne or so much as to look upon his relations
Esto situará cada pie de página directamente antes de <di v Ld» " footer" > de modo
again, for fear lest the freshly developed organism may, by
force of unconscious imitation, fall back again into his
que el pie de página 1 se sitúa entre <di v class=" chapter" >y <di v I d» "footer" »,
hereditary level.</span> el pie de página 2 se sitúa entre pie de página 1 y <di v .i.d«"footer" », etc. Utilizar
</p>
. insertAfter (/ di v . chapter /) , por otro lado, haría que los pies de página aparez-
<p>How admirable is the Law of Compensation! <span
can en orden inverso. Hasta el momento, nuestro código se parece a esto:
class=tlfootnotell>And how perfect a proof of the natural
fitness and, I may almost say, the divine origin of the $(d ocument) .ready(function() {
aristocratic constitution of the States of Flatlandl</span> $(/span.footnote/) .insertBefore(/#footer/) ;
By a judicious use of this Law of Nature, the Polygons and j);
Circles are almost always able to stifle sedition in its
very cradle, taking advantage of the irrepressible and
Desafortunadamente, sin embargo, nos hemos encontrado con un gran problema. Los
boundless hopefulness of the human mind.&hellip;
</p> pies de página están en etiquetas « span», lo que significa que se muestran inline por
defecto, uno detrás de otro sin separación, como aparece en la figura 5.5.
lIfIiI ,
5. Manipulación DOM , Aprende jQuery 1.3 IEII
itWltat nud ola cmij'tC4te'" a Spaulllllli crltic may osk: "ls nol the procnoIion ola St¡uan Son a ceniftt:(JUj'rom NtlR1r6 herset¡; pl'OWng me
Equal-sldednes! o/tAl! FtJJ.htrrl" I rq1/y that no Lady 01 any pasitlan wUJmarry 1m uncemfíed 1Hangle. SqutllY. offiprflrg haJ sometJmes 1'e$ultm
no utilizar un elemento que los numere por nosotros automáticamente? Hemos asignado
from a sllghlJ)' lrrtJgwlar THangle; bUl tn almost f1W!1'JI sucn case ,he I~gularltyofthefirsr genuarton ts vtsued on th! rhlrr/: whlch elthu /a/ls lo a la lista un Il) de notes y lo hemos insertado detrás de «d v class="chapter">.
í

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 ( /&nbsp; «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,í

Aprende jQuery 1.3 lfD


Lospies de página todavía carecen de sus números, sin embargo. Aunque se han situa-
do dentro de un <01>, cada uno se debe situar individualmente en su propio <li>.

Losotros métodosjQuerypara envolverelementosson .wrapAll () y .wraplnner () .


Véasehttp://docs . jquery. com711anipulation/wrapAll andhttp://docs .
Situar elementos alrededor de otros j query. com/Manipulation/wraplnner para más información.

El método principal de jQuery para situar elementos alrededor de otros elementos es


el denominado. wrap (). Puesto que queremos que cada $ (this) se sitúe en <li></
1i », podemos completar nuestro código de pie de página de esta forma:
Copiar elementos
,l
$ (document) .ready(function() { Hasta el momento en este capítulo hemos insertado elementos recién creados, movi-
$ (/<01 id="notes"></ol>/) .insertAfter (/div.chapter/) ; do elementos desde una ubicación en el documento a otra, y situado nuevos elementos
$ (/span.footnote/) .each(function(index) {
$ (this) alrededor de existentes. Algunas veces, sin embargo, podemos querer copiar elemen-
.before( tos. Por ejemplo, un menú de navegación que aparece en el encabezado de la página se
[/<a href="#foot-note-/, podría haber copiado y situado en el pie de página también. De hecho, siempre que se
.índex-s j ,
/11 id= 11context _/ ,
pueden copiar elementos para mejorar una página visualmente, es una buena oportu-
index+l, nidad de utilizar un script. Después de todo, ¿por qué escribir algo dos veces y duplicar
/11 class=trcontextll>/, nuestra oportunidad de error cuando podemos escribirlo una vez y dejar que jQuery
/<sup>/ + (index+l) + /</sup>/, haga el trabajo difícil?
/</a>/
Para copiar elementos, el método. clone () de jQuery es justo lo que necesitamos;
J .join(/ j)
) toma cualquier conjunto de elementos coincidentes y crea una copia de ellos para uso
.appendTo(/#notes/) posterior. Como con el proceso de creación de elementos que hemos explorado ante-
.append( /&nbsp; «a href="#context-/ + (Lndex-s j ) + riormente en este capítulo, los elementos copiados no aparecerán en el documento hasta
/">context</a»/ ) que aplicamos uno de los métodos de inserción. Por ejemplo, la siguiente línea crea una
.wrap(/<li id="foot-note-j + (index+l) +
/» ></li>/); copia del primer párrafo dentro de <di v c La s s» "chapter" »:
}) ;

}) ; $ (/div.chapter p,eq(O)/) .c1one();

Hasta el momento, el contenido de la página no ha cambiado (véase la figura 5.10).


Ahora cada uno de los elementos <li> viene completo con un id que coincide con
el href del marcador. Al final, tenemos un conjunto de pies de página numerados y
vinculados, como se ve en la figura 5.9. Conc:ernlng the Inhabltants of Flatland
an excerpt

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>
}) ;
''-.....,

lfllI 5. Manipulación DOM


Aprende jQuery 1.3 1m
A continuación, encontramos el párrafo padre de cada cita y aplicamos la propiedad Aplicaremos la elipsis primero, y luego reemplazaremos todo el HTML de cita.con
CSSposi tion: una versión de sólo texto:

$ (document) .ready(function() ( $ (document) .rdady(function() (


$ (/span.pull-quote/) .each(function(index) ( $ (/span.pull-quote/) .each(functiod'index) (
var $parentParagraph = $(this) .parent(/p/); var $parentParagraph = $(this) .parent(/p/);
$parentParagraph.css(/position/, /relative/); $parentParagraph.css(/position/, /relative/);

) ;
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{/&hellip;/)
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 (&hellip;), 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(/&hellip;/)
.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

Cargar datos bajo demanda


Como siempre, una implementación del mundo real debería utilizar mejora progre-
Por debajo de todo este alboroto, AJAX es meramente un medio de cargar datos siva para hacer que la página funcione sin requerir JavaScript. Aquí, para simplificar
desde el servidor al navegador Web, o cliente, sin un refresco de página visible. Estos nuestro ejemplo, los vínculos no hacen nada hasta que los añadimos comportamientos
datos pueden adoptar muchas formas, y tenemos muchas opciones para lo que hacer con conjQuery.
ellos cuando llegan. Veremos esto al llevar a cabo la misma tarea básica de muchas for-
mas.
Vamos a crear una página que muestra entradas de un diccionario, agrupadas por
la letra inicial de la entrada de diccionario. El HTML que define el área de contenido de
Añadir HTMl
la página se parecerá a esto:
/-----------------------------------------------------------------------
Las aplicaciones AJAX a menudo no son más que una petición de un bloque de HTML.
cdiv id="dictionaryU> Esta técnica, algunas veces referida como AHAH (Asynchronous HITP and HTML, o HTIP
</div> asíncrono y HTML), es casi trivial de implementar con jQuery. En primer lugar necesi-
- tamos insertar algo de HTML, que situaremos en un archivo denominado a, htrnl junto
SÍ, de verdad. Nuestra página no tendrá contenido con el que empezar. Vamos a con nuestro documento principal. Este archivo HTML secundario empieza así:
utilizar varios métodos AJAX de jQuery para completar este <di v » con entradas de
cdiv class=lIentryll>
diccionario. •
ch3 class="term">ABDlCATIONc/h3>
Vamos a necesitar una forma de activar el proceso de carga, por lo que añadiremos <div class="part">~.</div>
algunos vínculos para nuestros manejadores de evento: <div c1ass="definition">
An act whereby a sovereign attests his sense of the high
cdiv class="letters '>
l
temperature of the throne.
cdiv class="letter id="letter-a'1>
1!
<div c1ass=!lquote">
ch3>ca href="#">Ac/a><!h3> <div class=l1quote-1inell>Poor Isabe11ats Dead, whose
</div> abdication</div>
cdiv class='1letter'l id='lletter-brr> cd í,v c Las s s vquot.evLane " >Set al1 tongues wagging in the
ch3><a href="#">Bc/a></h3> Spanish nation.</div>
c/div> <div c1ass=l1quote-linel1>For that performance ttwere
cdiv class="letter" id="letter-c11> unfair to scold her:</div>
ch3><a href=II#II>Cc/a></h3> <div c1ass="quote-1inel1>She wise1y 1eft a throne too
</div> hot to hold her.</div>
cdiv class=llletter id="letter-d">
11
<div c1ass=lIquote-1inel1>To History she'11 be no royal
<h3>ca href="#ll>Dc/a></h3> ridd1e &mdashi</div>
c/div> <div c1ass=lIquote-1ine">Mere1y a p1ain parched pea that
</div> jumped the griddle.</div>
<div class="quote-author">G.J.</div>
Al añadir algunas reglas CSS, obtenemos una página que se parece a la figura 6.1. </div>
</div>
The Devll's Dlctlonary </div>
by Amorosa Blerce <div class=t'entrytt>
<h3 class=!lterrn">ABSOLUTE</h3>
<div class="part >adj.</div>
ll

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

La página continúa con más entradas en esta estructura HTML. Si lo presentamos,


esta página es bastante sencilla, como muestra la figura 6.2. The Davll's Dictlonary
by Ambrose Bleroe

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.

v.t. Figura 6.3. El navegador muestra el nuevo HTML,


lb confess. Acknowledgemenr of one anotber's faults is the bighest duty imposed by our love of trutb.
Cuando probamos este ejemplo, las definiciones de diccionario aparecerán probable-
Figura 6.2. Presentar la estructura HTML. mente de forma instantánea cuando se hace die en el botón. Esto es un riesgo de trabajo
en nuestras aplicaciones a nivel local; es dificil dar cuenta de los retrasos al transferir
Observe que a. html no es un verdadero documento HTML; no contiene ningún documentos por la red. Suponga que hemos añadido que se muestre un cuadro de alerta
<html>, <he ad », o <body », que son normalmente obligatorios. Normalmente denomi- después de cargarse las definiciones:
namos a un archivo así un fragmento; su única finalidad es insertarse en otro documento $ (document) . ready (function () {
HTML, lo que abordaremos ahora: $('#letter-a a') .click(function() {
$ ( '#dictionary' ) .load I ' a. html ' ) ;
$ (document) . readyIfunction() {
alert ( Loaded!
I i I )

$ (, #letter-a a') . click (function () {


return false¡
$ ( '#dictionary' ) ,load ( 'a. html ' ) ;
}) ;
return false¡
});
}) ;
}) ;
Podríamos asumir por la estructura de este código que la alerta solamente se puede
mostrar después de que se haya realizado la carga. La ejecución JavaScript es normal-
El método .load () hace todo el trabajo por nosotros. Especificamos la ubicación
mente síncrona, trabajando en una tarea detrás de otra en secuencia estricta.
destino para el fragmento HTML al utilizar un selector jQuery normal, y luego pasamos
Sin embargo, cuando se prueba este código determinado sobre un servidor Web de
la URL del archivo a cargarse como un parámetro al método. Ahora, cuando se hace clic
producción, la alerta habrá posiblemente ido y venido antes de que la carga se haya
en el primer vínculo, se carga el archivo yse sitúa dentro de <di v id= "dictionary" >.
completado, debido a retraso de red. Esto sucede porque todas las llamadas AJAX son
El navegador mostrará el nuevo HTML tan pronto como se inserta, como muestra la fi-
gura 6.3. por defecto asíncronas. De lo contrario, tendríamos que llamado SJAX. Carga asíncro-
na significa que una vez que se lanza la petición HTTP para recuperar el fragmento
Observe que el HTML ahora tiene estilo, mientras que antes era plano, Esto se debe a
HTML, se reanuda inmediatamente la ejecución del código sin esperar. Cierto tiempo
las reglas CSS en el documento principal; tan pronto como se inserta el nuevo fragmento
HTML, las reglas se aplican también a sus etiquetas. después, el navega dar recibe la respuesta del servidor y lo gestiona, Éste siempre es un
comportamiento deseado; es poco amable bloquear todo el navegador Web mientras se
III!I 6. AJAX /
Aprende jQuery 1.3 I!II
r

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 :',

"Thkt fer devotions paid to Bacchus" ,


"The lictors dare to run ti':s in, 11,
"And resolutely thump and whack US?11
Trabajar con objetos JavaScript ] ,
vaut.hor v. "Jorace"
),
Extraer HTML bien formado bajo demanda es muy conveniente, pero hay momentos
(
en los que queremos que nuestro script pueda procesar datos antes de que se muestren. "term": "BACKBITE" 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,

"definition ll: "The hair that is commonly cut off by.


),
Recuperar un objeto JavaScript

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

La función $. getJSON () toma un segundo argumento, que es una función a invo-


car cuando la carga está completa. Como se ha mencionado antes, las llamadas AJAX
son asíncronas, y la rellamada proporciona una forma de esperar a que se transmitan los Este enfoque supone que los datos son seguros para el consumo de HTML; no debería
datos en lugar de ejecutar el código. La función de rellamada también toma un argumen- contener ningún carácter < perdido-por ejemplo.
to, que se completa con los datos resultantes. De modo que podemos escribir:
$ (document) .ready(function(} {
$ ('#letter-b a') .click (function () { Todo lo que queda es gestionar las entradas con citas, lo que lleva otro bucle
$.getJSON('b.json', function(data} $. each ():
)l;
return false¡
$ (document) .ready(function(} {
}} ;
$('#letter-b a') .click(function(} {
}} ;
$.getJSON('b.json', function(data}
$('#dictionary') .empty();
Aquí estamos utilizando una función anónima como nuestra rellamada, como ha sido $.each(data, function(entrylndex, entry}
común en nuestro código jQuery por brevedad. Se podría igualmente proporcionar una
función con nombre como la rellamada. var htrnl = '<div c La s s e vent zy"> ,;
htrnl += "ch S class="term >' ll + entry{'term'] + '</h3>';
Dentro de esta función, podemos utilizar la variable da t a para recorrer la estructura html += '<div claS9="part">' + en t r y l t par t t l + '<!div>';
de datos según sea necesario. Necesitaremos pasar por la tabla de alto nivel, creando el htrnl += '<div class="definition":>, ;
HTML para cada elemento. Podríamos hacer esto con un bucle estándar for, pero en su html += entry['definition'J;
if (errt r y l t quot.e t l ) {
lugar introduciremos otra de las funciones globales de utilidad de jQuery, $ . each ( ) .
html += l<div c Las ss vquoce's ".
Hemos visto su homólogo, el método . each ( ) , en el capítulo anterior. En lugar de tra- $.each(entry['quote'J, function(linelndex, line} {
bajar en un objeto jQuery, esta función toma una tabla o mapa como su primer paráme- htrnl += '<div class=ttquote-line >' ll + line + l</div>' i

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

And resolutely thump and whack us? "pa r t !': IIn.lI,

º 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,

BEGGAR n. 1Idefinitionll: I1The period of human life intermediate ... 11


ano who has rellad on tIle ssslstanoe 01 hls f1tends.
L
{
BELLAOO.NNA n. "t.e rrnv. IICLARIONETII,

~ It"lI.,
"pa r t v. IIn.lI,

"de f í.ni t i.onv . "An instrument of torture operated by ..


Figura 6.4. Comprobar las entradas. }.
(
"term": nCOMFORTrr,
IIpartl!: "n.lI,
Ejecutar un script "definition": "A state of mind produced by.
i.
(
De vez en cuando no queremos recuperar todo el JavaScript que necesitaremos cuan- rrterrnrr: rrCORSAIRII,
do la página se carga por primera vez. Podríamos no saber qué scripts serán necesarios IIpartll: IIn.",

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

Aprende jQuery 1.3 l!iI


The Devll's Dlctlonary
by Ambrose BIe"",
"
<line>All hail, Delusion! Were it not for thee</line>
<line~The world turned tapsy-turvy we should see¡
</line>
A CAlAMJTY n. <lin~>For Vice, respectable with cleanly fancies,
</line> ~,

§
ª 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

<entries> $.get('d.xml', function(data) (


c::entry term="DEFAME" part="V.t,,,>
<:definition> }) ;

return false¡
To lie about another. To tell the truth about another. }) ;
</definition>
</entry> }) ;

<entry term=IIDEFENCELESs" part="adj.lI>


c::definition> Esta vez es la función $ . get () la que hace nuestro trabajo. En general, esta función
Unable to attack. simplemente va a buscar el archivo en la URL facilitada y proporciona el texto plano para
</definition>
</entry> la rellarnada. Sin embargo, si la respuesta se sabe que es XML debido a su tipo MIME
<:entry term=IIDELUSION" part="n.rr:> proporcionado por el servidor, a la rellamada se le pasará el árbol XML DOM.
<definition> Afortunadamente, corno ya hemos visto, jQuery tiene considerables posibilidades
The father of a most respectable family, comprising transversales DOM. Podemos utilizar. find (), . filter () y otros métodos transver-
Enthusiasm, Affection, Self-denial, Faith, Hope, sales en el documento XML igual que haríamos en HTML:
Charity and many other goodly sons and daughters.
</definition>
$ (document) .ready(function() (
<guote author=uMumfrey Mappel > U
$ (,#letter-d a') .click (function () (
$.get('d.xml', function(data) {
r
IIII 6,AfAX , I
Aprende jQuery 1,3 ID
$('#dictionary') ,empty{);
$ (data) ,find{'entry') ,each{function{) The Oevll's Dlctlonary
var $entry = $(this);
by Ambrosa BiefVe
var html = 'cdiv class=uentryu>,;
html += "<hS class=lItermtr>, + $entry.attr( "t.e
rrn') "
+ '</h3>'; DANCE v./,
A
html += "<di.v class="part >'
l1
+ $entry.attr('part') To leap about lo the sound af tlttarlng rnuslc, preferably wltIl erms at>out your nelghllor's _ or
+ "cZd i.v»'i
htrnl += 'cdiv class="definition">'; ª
k
deughter, Them are many klnds af dances, bu! all !hose nlQulnng !he psrtlclpatlon af tI1e two sexes
hava two characterlstlcs In oommon: tIley are consplcuously Innocen~ and wannly Ioved by lile
vldous.
html += $entry,find{'definition') ,text{);
var $quote = $entry,find{'quote');
if ($quote,length) (
/ º DAY n,
A Penod of twenty-Iour hou/'S, mosUy mlsspent Thls pertod Is d1v1dedInto two psrts, tIle day prcper
html += 'cdiv class=lIquotell>'¡
and tIle nlgh~ or day Improper tI1e formar devuted te slns af business, tI1e latler oonsecmted to tIle
$quote,find{'line') ,each{function{) other sort These two klnds ot social actMty overfap,
html += 'cdiv class="quote-line">,
+ $ (this) .t.ext() + '</div>';
}) ; DEBT n,
Ao Ingenlous substituta ter the maln and whlp 01tila slBve-dr1ver.
if ($quote,attr{'author')) (
1 As, pen! In sn SQUanum, !he troutlet
html += 'cdiv class="quote-authortl> ,
SWlms round and round hJg tank to flnd en ouUet,
+ $quote.attr('authort) + 'c/div>'¡ Presslng hls nose sgalns! !he glasa !he! holds hlm,
} Nor ever seas !he prison that enfolds hlm;
SO !he pool' debtDr, seelng nsught sround hlm,
htrnl += 'c/div>' i Ya! feels tI1e narrow IImIts !ha! Impound hJm,
} Gr1eves al hl5 debt and s!lldles to evade It,
htrnl += '<!div>'¡ And Hnds a! last he mlgh! as wel have psld Il
,I Bar10WS, Vode
html += 'c/div>';
$('#dictionary') ,append(${html));
}) ; Figura 6.6. Mejorarla página con XML.
}l;
return false;
});
});
The Oavll's Dlctlonary
by Ambrose Blerte
Esto tiene el efecto esperado cuando se hace clic en el vínculo D, como se ve en la
figura 6.6.
A DEBT n.
Éste es un nuevo uso para los métodos transversales DOM que ya conocemos, arro- An Inganlous substttute for tIle chaln end whlp 01 !he sIa_er,
jando algo de luz sobre la flexibilidad del soporte de selector CSS de jQuery. La sintaxis
CSS se utiliza normalmente para ayudar a mejorar páginas HTMl" y por lo tanto los
ª
{;
As, pan! In en SQUanum, !ha troutlet
swtms round en<! round hls "'"k lo flnd an ootie!,
Presslng hIs nasa agalnst!he glass tila! holds hlm,
Nor ever seas the pr1son that oorolds hlm;
selectores en archivos. css estándar utilizan nombres de etiquetas HTML como di v y Q SO !he poor deIrtor, seelng naugh! around hlm,
body para localizar contenido, Sin embargo, jQuery puede utilizar nombres de etique- Ya! feels tI1e ll8!TOW Ilrnlts tila! Impound hlm,
GrieVes at hl5 debt and studles to evade It,
ta XML arbitrarios, como entry y definition aquí, tan fácilmente como las HTML And nnds a! las! he mlgh! as weU havo psld Il
Bar10WS, \Iode
estándar.
El motor selector avanzado dentro de jQuery facilita encontrar partes del documento
XML en situaciones mucho más complicadas, también. Por ejemplo, suponga que qui- DELUSION n.
The la!her 01 e mas! respsctable famlly, oomprlslng Entlluslssm, AIfecIIon, SeW..oonlal, FaI!h, Hope,
siéramos limitar las entradas mostradas a aquellos que tienen citas que a su vez tienen Chenly and meny other goodly 5005 and daughters,
autores de atributo. Para hacer esto, podemos limitar las entradas a aquéllas con elemen- AlI hall, Ooluslool Were H no! Ior!hae
The worId turnad topsy-turvy we should seo;
tos -cquot.e s anidados al cambiar entry por entry:has (guate}, Luego podemos For \/Ice, respectallI8 wltIl cleanly fancles,
WOUkl fty abandonad V1r!ue's gross s<!Vences,
limitar además las entradas a aquéllas con atributos authar en los elementos <guate> Mumfl1ly Mappel
al escribir entry: has (quate [autharl ). La línea con el selector inicial ahora dice:
$ (data) ,find('entry:has{quote(author)) ') ,each{function{) {
Esta nueva expresión selector limita las entradas devueltas según corresponde, como
muestra la figura 6.7. Figura 6.7. Limitarlas entradas mostradas,
lE.iI 6. AJAX Aprende jQuery 1.3 mi
Elegir un formato de datos Pasar datos al servidor
Hemos examinado cuatro formatos para nuestros datos externos, cada uno de los Nuestros ejemplos hasta este punto se han centrado en la tarea de recuperar archi-
cuales se gestiona de forma nativa por las funciones AJAX de jQuery. También hemos vos de datos estáticos del servidor Web. Sin embargo, la técnica AJAX entra en juego
verificado que los cuatro pueden gestionar la tarea en cuestión, cargando información solamente cuando el servidor puede configurar dinámicamente los datos basándose
sobre una página existente cuando el usuario lo solicita y no antes. ¿Cómo, entonces, en la entrada desde el navegador. Nos ha ayudado jQuery en esta tarea también; todos
decidimos cuál utilizar en nuestras aplicaciones? los métodos que hemos tratado hasta el momento se pueden modificar de modo quela
transferencia de datos se convierte en una calle de doble sentido.
• Los fragmentos HTML requieren muy poco trabajo para implementar. Los datos
externos se pueden cargar e insertar en la página con un solo método, que no
requiere ni siquiera una función de rellamada. Los datos no se estructuran nece-
sariamente en una forma que los hagan reutilizables para otras aplicaciones. El
archivo externo está estrechamente unido a su contenedor. Puesto que demostrar estas técnicas requiere interacción con el servidor Web, necesita-
• Los archivos JSON están estructurados para reutilización sencilla. Son compactos remos utilizar código del lado del servidor por primera vez aquí. Losejemplos facilitados
y fáciles de leer. La estructura de datos se debe recorrer para extraer información utilizarán el lenguaje de script PHP, que está ampliamente utilizado así como disponible
y presentarla en la página, pero esto se puede realizar con técnicasJavaScript es- gratuitamente. No trataremos cómo configurar un servidor Web con PHP aquí; ayuda
tándar. Puesto que los archivos se pueden analizar con una sola llamada al eval () sobre ello se puede encontrar en los sitios Web de Apache (http : // apache. org/) O
de JavaScript, leer un archivo JSON es extremadamente rápido. Cualquier uso PHP (http://php .rie t z ), O desde la empresa de hospedaje de su sitio.
de eval () implica riesgos inherentes, sin embargo. Errores en el archivo JSON
pueden causar fallo silencioso o incluso efectos secundarios en la página, de modo
que los datos se deben elaborar cuidadosamente por alguien de confianza.
llevar a cabo una petición GET
• Los archivos JavaScript ofrecen lo último en flexibilidad, pero no son realmente un
mecanismo de almacenamiento de datos. Puesto que los archivos son específicos Para ilustrar la comunicación entre cliente y servidor, escribiremos un script que
del lenguaje, no se pueden utilizar para proporcionar la misma información a solamente envía una entrada de diccionario al navegador en cada petición. La entrada
sistemas dispares. En su lugar, la posibilidad de cargar un archivo JavaScript elegida dependerá de un parámetro enviado desde el navegador. Nuestro script extraerá
significa que los comportamientos que apenas se necesitan se pueden separar en sus datos de una estructura de datos interna como ésta:
archivos externos, reduciendo el tamaño del código a menos que y hasta que sea
< ?php
necesario.
$entries = array(
• Los documentos XML son los reyes de la portabilidad. Puesto que XML se ha 'EAVESDROP' => array(
part V.i.
convertido en la lingua franca del mundo del servicio Web, proporcionar datos en =>
l l t I,

'definition' => 'Secretly to overhear a catalogue of the


este formato permite que los datos se puedan reutilizar en cualquier parte. Por
crimes and vices of another or yourself. 1,

ejemplo, Flickr (http://flickr.com/), del.icio.us (http://del. icio. us /) Iguate , => array(


yUpcoming (http://upcoming . org/) todos exportan representaciones XML lA lady with one of her ears applied',
de sus datos, lo que ha permitido que surjan muchas interesantes aplicaciones 'To an open keyhole heard, inside,l,
'Two female gossips in converse free &mdash¡
de sus datos. El formato XML es algo voluminoso, sin embargo, y puede ser algo 1,

'The subject engaging them was she.',


más lento de analizar y manipular que otras opciones. 'ftI think,tI said ene, lIand rny husband thinks 1,

'That she\ls a prying, inquisitive minx!1l1,


Con estas características en mente, normalmente es más sencillo proporcionar datos 'As seon as no more of it she could hear 1,

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 ~

Llevar a cabo una petición POST Enve!ope


~
~
Las peticiones HTTP que utilizan el método POST son casi idénticas a las que utilizan ~
GET. Una de las diferencias más visibles es que GET sitúa sus argumentos en la cadena
de consulta de la URL, mientras que las peticiones POST no. Sin embargo, en llamadas Figura 6.10. Petición realizada con POST.
lmI 6.AJAX
Aprende jQuery 1.3 ID
Serializar un formulario $ (document) .ready(function() {
$('#letter-~ form') .submit(function()
$ ( '#dictionary' ) .load ( 'f. php' ,
Enviar da tos al servidor a menudo implica que el usuario rellene formularios. En lugar { ,tetm': $ ( ,input [name=" term"] ,) .val () } ) ;
de basarse en el mecanismo normal de envío de formulario, que cargará la respuesta en return Pfalse¡ .l:.,
toda la ventana del navegador, podemos utilizar el conjunto de herramientas AJAX de j);
jQuery para enviar el formulario de forma asíncrona y situar la respuesta dentro de la }) ;

página actual. Para probar esto, necesitaremos construir un formulario sencillo:


Este código tiene el efecto pretendido, pero buscar campos de entrada por nombre y
cdiv class="letter" id="letter-f">
<h3>F</h3>
anexarlos a un mapa uno a uno es complicado. En particular, este enfoque no se escala
cform> .' bien ya que el formulario se vuelve muy complejo. Afortunadamente, jQuery ofrece un
cinput type="text" name=lItermll value=1I1I id="termll /> método abreviado para ello. El método. serialize () actúa sobre un objeto jQuery y
<input type=lIsubmit" name="searchl1 value=="searchl1 traduce los elementos DOM coincidentes en una cadena de consulta que se puede pasar
id="search" />
</form>
junto con una petición AJAX. Podemos generalizar nuestro manejador de envío de la
</div> siguiente forma:
$ (document) .ready(function() {
Esta vez devolveremos un conjunto de entradas del script PHP al buscar el término $('#letter-f form') .submit(function() (
de búsqueda facilitado como una subcadena de un término de diccionario. La estructura $.get('f.php', $(this) .serialize(), function(data)
$ ('#dictionary') .html(data);
de datos será del mismo formato que antes, pero la lógica será un poco diferente:
}) ;
foreach ($entries as $term => $entry) ( return false;
if (strpos($term, strtoupper($ REQUEST['term'])) })
!== FALSE) ( -
.,- ;

}) ;
$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

La llamada a strpos () analiza la palabra para la cadena de búsqueda facilitada.


~....=:.=J
Ahora podemos reaccionar a un envío de formulario y diseñar los parámetros de con- ~ •• rc~
sulta apropiados al recorrer el árbol DOM:
Figura 6.11. Mostrar entradas coincidentes.
1m 6.AJAX Aprende jQuery 1.3 lImI

Sin embargo, en consonancia con el espíritu de la mejora progresiva, no situaremos


Estar pendiente de la petición este código HThiL directamente en la página. Solamente es relevante para nosotros cuan-
do JavaScript está disponible, por lo que lo insertaremos utilizando jQuery:
Hasta el momento, ha sido suficiente para nosotros realizar una llamada a un método
AJAX y esperar pacientemente la respuesta. Sin embargo, a veces, es de utilidad saber $ (document) .ready(function() { ".
$ ( t c d i v id=" loading" >Loading ... </div>' )
un poco más sobre la petición HTTP a medida que progresa. Si surge dicha necesidad,
.insertBefore('#dictionary')
jQuery ofrece un conjunto de funciones que se pueden utilizar para registrar rellamadas }) ;
cuando ocurren varios eventos relacionados con AJAX.
Los métodos. aj axStart () y . aj axStop () son dos ejemplos de estas funcio- Nuestro archivo CSSproporcionará a este <di v » una regla de estilo display: none;
nes, y se pueden anexar a cualquier objeto jQuery. Cuando una llamada AJAX empieza de modo que el mensaje está oculto inicialmente. Para mostrarlo en el momento correcto,
con ninguna otra transferencia en progresos, la rellamada . aj axStart () se activa. "'simplemente lo registramos como un observador con. aj axStart ():
Por el contrario, cuando termina la última petición activa, la rellamada anexada con
$ (docu ment) .ready(function() {
. aj axStop () se ejecutará. Todos los observadores son globales, en la medida que se $ ( '<di v id= "loading
ll >L~ading... </div> t )

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;
}) ;

En primer lugar, ocultamos el elemento destino, y luego iniciamos la carga. Cuando


-Limitaciones de seguridad
la carga está completa, utilizamos la rellamada para mostrar el elemento recién incor-
porado al desvanecerse. .' Para su utilidad en la elaboración de aplicaciones Web dinámicas, XMLHttpRequest
(la tecnología de navegador subyacente detrás de la implementación AJAX de jQuery)
está sujeta a límites estrictos. Para impedir varios ataques cross-site scripting (XSS),ge-
neralmente no es posible solicitar un documento de un servidor que no sea el que alber-
AJAX Y eventos ga la página original.
Ésta es por lo general una situación positiva. Por ejemplo, algunos citan la imple-
Suponga que quisiéramos permitir que cada nombre de término de diccionario con- mentación del análisis JSON al utilizar eval () como inseguro. Si código malicioso está
trolara la visualización de la definición que sigue; hacer clic en el nombre del término presente en el archivo de datos, se podría ejecutar por la llamada eval ( ) . Sin embargo,
mostraría u ocultaría la definición asociada. Con las técnicas que hemos visto hasta el puesto que el archivo de datos debe residir en el mismo servidor que la propia página
momento, esto debería ser bastante sencillo: Web, la posibilidad de incorporar código en el archivo de datos es en gran medida equi-
$ (document) .ready(function() { valente a la pósíbilidad de incorporar código en la página directamente. Esto significa
$ (, .term' ) .click (function O ( que, para el caso de cargar archivos JSON de confianza, eval () no es una preocupación
$(this) .siblings(' .definition') .slideToggle(); se seguridad significativa.
». Sin embargo, existen muchos casos, en los que sería beneficioso cargar datos desde
}) ;
una fuente de terceros. Existen varias formas de solucionar las limitaciones de seguridad
Cuando se hace clic en un término, el código encuentra hermanos de! elemento que y permitir que esto suceda.
tienen una clase def ini t ion, y la desliza hacia arriba o hacia abajo según sea el caso. Un método es basarse en el servidor para cargar los datos remotos, y luego propor-
Todo parece en orden, pero un clic no hace nada con este código. Desafortunadamente, cionarlos cuando los solicite el cliente. Se trata de un enfoque muy potente ya que el
los términos todavía no se han añadido al documento cuando hemos anexado los ma- servidor puede llevar a cabo pre-procesamiento en los datos según se necesite. Por ejem-
nejadores el i ck. Incluso si consiguiéramos anexar manejadores el i ck a estos elemen- plo, podríamos cargar archivos XML que contienen feeds de noticias RSS desde varias
tos, una vez que hacemos clic en una letra diferente los manejadores dejarían de estar fuentes, agregarlas en una sola feed en el servidor y publicar este nuevo archivo para el
anexados. cliente cuando se solicita.
Éste es un problema común con áreas de una página completada por AJAX. Una Para cargar datos desde una ubicación remota sin implicación del servidor, tene-
solución popular es volver a vincular manejadores cada vez que se refresca el área de mos que actuar de forma diferente. Un enfoque popular para el caso de cargar archivos
página. Sin embargo, esto puede ser complicado ya que el código de vinculación de JavaScript del exterior es incorporar etiquetas <script> bajo demanda. Puesto que
evento se tiene que invocar cada vez que cualquier cosa hace que la estructura DOM de jQuery puede ayudamos a insertar nuevos elementos DOM, es sencillo realizar esto:
la página cambie. $(document.createElement('script'))
Una alternativa a menudo superior se presentó en un capítulo anterior. Podemos .attr('src', 'http://example.com/example.js')
implementar delegación de evento, vinculando el evento a un elemento ancestro que .appendTo('head') ;
llifI 6. AJAX Aprende jQuery 1.3 lIliI
De hecho, el método $. getScript () se adaptará automáticamente a esta técnica $.getJSON(url + '?callback=?I, function(data)
si detecta un host remoto en su argumento URL, por lo que incluso esto se gestiona por $I'#dictionary') .emptyl);
nosotros. $.eachldata, functionlentrylndex, entry) {
V!ar htrnl = '<:div class="entry">';
El navegador ejecutará el script cargado, pero no existe mecanismo para recuperar html += • <h3 class= rt termJ; > I + entry [1 term 1]
resultados del script. Por esta razón, la técnica requiere cooperación del host remoto. El + ·</h3>';
html += 'cdiv class="part >' + ent ry I t par t l
script cargado debe tomar alguna acción, tal como establecer una variable global que ll t

+ 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

de opciones que se pueden utilizar para personalizar su comportamiento. success: functionldata) (


$I'#dictionary') .htmlldata);
Nuestro primer ejemplo cargó un fragmento HTML utilizando $ ( '#dictio- }
nary' ) . load ( , a. html ' ) . Esta acción se podría conseguir con $ . aj ax () de la si- }) ;
guiente forma:
" Esta secuencia de operaciones se comporta igual que nuestro ejemplo $ . aj ax () ante-
$.ajax({
rior. Observe que la URLde la petición se especifica como un valor predeterminado por
url: r a. htrnl 'I

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>

<a href="index.php?sort=name >Name</a>


ll
disponible. Una alternativa viable en este caso es llevar a cabo la ordenación por com-
</th> pleto en el navegador utilizando programación JavaScript del lado del cliente.
cth class="date"> Por ejemplo, suponga que tenemos una tabla que lista libros, nombres de los autores,
ea href="index.php?sort=date">Date</a> fechas de publicación y precios, como se ve en la figura 7.1.
IDDI 7. Manipulación de tabla Aprende jQuery 1.3 lfD

- TIUo

Building Websltes wllh


Joom1a11.5Bata 1

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.

Etiquetas' de agrupación de filas


.

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&nbsp;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

Joornla! 1.5 Beta 1 /> 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:
\;":""''''

lIiI 7. Manipulación de tabla Aprende jQuery 1.3 lIfiI


cthead:::. if ($header.is(' .sort-alpha')) (
<tr> ~header.addClass('clickable') .hover(function()
cth></th> $header.addClass('hover') ,
cth class="sort-alpha">Titlec/th> ¡, function () {
<th>Author(s) </th> $header. removeClass (,~hover' ) i
<th>Publish&nbsp,Date</th> }) .click(function() (
cth>Pricec/th> var rows = $table.find('tbody > tr') .get(),
</tr> rows. sort (function(a, b) (
</thead> var keyA = Sta) .children('td') .eq(column) .text()
.toUpperCase() ,
Utilizar JavaScript para ordenar tablas var keyB = $(b) .children('td') .eq(column) .text()
.toUpperCase() ,
Para llevar a cabo la ordenación, podemos utilizar el método incorporado de JavaScript if (keyA < keyB) return -1,
if (keyA > keyB) return 1,
. sort ( ) . Realiza una ordenación sobre una tabla, y puede tomar una función compara- return O;
dor como argumento. Esta función compara dos elementos en la tabla y debería devolver ».
un número positivo o negativo dependiendo de qué elemento debería venir primero en $.each(rows, function(index, row) (
la tabla ordenada. Por ejemplo, tome una sencilla tabla de números: $table.children('tbody') .append(row) ,
}),
var arr = [52, 97, 3, 62, 10, 63, 64, 1, 9, 3, 4}, }) ,
}
Podemos ordenar esta tabla al invocar arr . sort
están en orden:
( ) . Después de esto, Ios elementos
}) , ».
}),
[1, 10, 3, 3, 4, 52, 62, 63, 64, 9, 97}
Lo primero a destacar es nuestro uso del método . each () para crear la iteración ex-
Por defecto, como vemos aquí, los elementos se ordenan lexicográficamente (en orden plícita. Aunque podríamos vincular un manejador click a todos los encabezados que
alfabético). En este caso podría tener más sentido ordenar los elementos numéricamente. tienen la clase sort-alpha al invocar $ ( table. sortable th. sort -alpha ') .
I

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) ;

Esto corrige el color de la fila, solucionando el problema, como se muestra en la fi-


gura 7.4.

Figura 7.3. Se ha perdido la alternancia de color.

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:

Hemos realizado tres cambios importantes a la función. $.each(rows, function(index, row) (


row.sortKey = $(row) .children('td') .eq(column)
• Se define como una nueva propiedad de j Query . fn en lugar de como una .text() .toUpperCase();
}) ;
función independiente. Esto registra la función como un método plug-in. ,. rows.sort(function(a, b) {
if (a. sortKey < b. sor t xey) return -1;
• Utilizamos la palabra clave this como un sustituto de nuestro parámetro $table.
if (a.sortKey > b.sortKey) return 1;
Dentro de un plug-in de método, this hace referencia al objeto jQuery sobre el return Di
que se actúa. }) ;

$.each(rows, function(index, row) (


• Por último, devolvemos thi s al final de la función. Proporcionar el objeto jQuery $table.children('tbody') .append(row) ;
como el valor de retorno hace que nuestro nuevo método se pueda encadenar. row.sortKey = null¡
}) ;

En el nuevo bucle, estamos realizando todo el trabajo y almacenando el resultado en


una nueva propiedad. sortKey. Este tipo de propiedad, anexada a un elemento DOM
Más adelante en el libro puede encontrar información sobre escribir plug-ins jQuery. pero no un atributo DOM normal, se denomina un expando. Se trata del lugar adecuado
Ahí tratamos de poner disponible un plug-in para consumo público frente al pequeño para almacenar la clave, ya que necesitamos una por elemento de fila de tabla. Ahora,
ejemplo aquí que solamente se va a utilizar por nuestro propio código. podemos examinar este atributo dentro de la función comparador, y nuestra ordenación
es considerablemente más rápida.
Con nuestro plug-in definido, podemos invocar $table . al ternateRowColors (),
una sentencia más natural jQuery, en lugar de al ternateRowColors ($table) .

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 ••

return O; $.eaeh(rows, funetion(index, row) (


}) ; var $eell ~ $(row) .ehildren('td') .eq(eolumn);
$.eaeh(rows, funetion(index, row) ( row.sortKey = $eell.find(' .sort-key') .text() .toUpperCase()
$table.ehildren('tbody') .append(row); + ' , ~ $eell.text() .toUpperCase();
$ (row) .removeData{'sortKey'); }); -',
}) ;
Ordenar por la columna Author(s) ahora utiliza la clave proporcionada, ordenando.
Utilizar. data () en lugar de propiedades expanda puede ser, a veces, más conve-

-
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.

Manipular las teclas de ordenar


Ahora, queremos aplicar el mismo tipo de comportamiento de ordenación a la colum-
na Author(s) de nuestra tabla. Al añadir la clase sort- alpha a su celda encabezado, la
columna Author(s) se puede ordenar con nuestro código existente. De forma ideal los au-
tores se deberían ordenar por el apellido, no por el nombre. Puesto que los libros tienen
múltiples autores, y algunos autores tienen varios nombres o iniciales, necesitamos ayuda
para determinar qué parte del texto utilizar corno nuestra clave de ordenación. Podemos
proporcionar esta ayuda al situar la parte relevante de la celda en una etiqueta:
<tr>
<td><img sre=" ../images/eovers/small/1847192386.png" Figura 7.5. Tabla ordenada por el apellido.
width="49 height=H61" alt=I1Building
11 Websites with
Joomla! 1.5 Beta 1 1> 11
Si dos apellidos son idénticos, la ordenación utiliza toda la cadena para decidir la
</td>
posición.
<td>Building Websites with Joomla! 1.5 Beta l</td>
<td>Hagen <span class=tlsort-keylr>Graf</span></td>
<td>Feb 2007</td>
<td>$40.49</td>
Ordenar otros tipos de datos
</tr>
<tr>
Nuestro usuario debería poder ordenar no sólo por las columnas Title y Author(s),
c t d sci mq src« " ../images/eovers/small/1904811620 .png" sino por las columnas Publish Date y Price también. Puesto que hemos agilizado nuestra
width="49It height="6111 alt=IILearning Mambo: A función comparador, puede gestionar todos los tipos de datos, pero primero las claves
Step-by-Step Tutorial to Building Your Website" /> calculadas se tendrán que ajustar para otros tipos de datos. Por ejemplo, en el caso de
</td>
<td>Learning Mambo: A Step-by-Step Tutorial to Building
precios a los que necesitamos quitar el carácter $ inicial y analizar el resto de modo que
Your Website podamos compararlos numéricamente:
</td>
<td:>Douglas <span class="sort-keyl1>paterson<!span></td> var key = parseFloat($eell.text() .replaee(/A(A\d.J*/, "));
<td>Dee 2006</td> row.sortKey = isNaN(key) ? o : key;
<td>$40.49</td>
</tr> La expresión regular utilizada aquí elimina cualquier carácter distinto de los números
y puntos decimales, pasando el resultado a parseFloat (). El resultado de parse-
Ahora, tenemos que modificar nuestro código de ordenación para tener en cuenta Float () luego se tiene que comprobar, porque si no se puede extraer ningún número
esta etiqueta sin molestar el comportamiento existente para la columna Title, que ya fun- del texto, se devuelve NaN(not a number). Esto puede causar estragos en . sort () .
ciona bien. Al anexar la clave de ordenación marcada a la clave que hemos calculado Para las celdas de fecha, podemos utilizar el objeto JavaScript Date:
previamente, podemos ordenar primero por el apellido si se invoca, pero sobre toda la
cadena corno seguridad: row.sortKey = Date.parse('l ' + $eell.text());
El 7. Manipulación de tabla Aprende jQuery 1.3 lID
Las fechas en esta tabla contienen un mes y año solamente; Date. parse () requiere return o;
}) /
una fecha especificada completamente, por lo que precedemos la cadena con 1. Esto pro- $.each(rows, function(index, row) (
porciona un día para complementar el mes y año, y la combinación luego se convierte en $table.children('tbody') .append(row) /
una marca de tiempo, que se puede ordenar utilizando nuestro comparador normal. row. sortKey = nul,~i
Podemos repartir estas expresiones entre funciones aparte, e invocar la apropiada }) /

$table.alternateRowColors() /
basándonos en la clase aplicada al encabezado de tabla: }) /

jQuery.fn.alternateRowColors = function() ( }
$('tbody tr:odd', this) }) /

.removeClass('even') .addClass('odd') / })/

$('tbody tr:even', 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', "

+ ' , + $cell.text() .toUpperCase()¡


};

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

$table.find('td') .removeClass('sorted') return this¡


.filter(' :nth-child(' + (column + 1) + ') ') };
.addClass('sorted') ; $ (document) .ready(function() (
$('table.sortable') .each(function()
Este fragmento primero elimina la clase sorted de todas las celdas, luego la añade a var $table = $(this);
las celdas que están en la misma columna que hemos utilizado para nuestra ordenación. $table.alternateRowColors() ;
$('th', $table) .each(function(column)
Observe que tenemos que añadir 1 al índice de columna que hemos encontrado anterior- var $header = $(this);
mente, ya que el selector : nth-child {)es en base uno en lugar de en base cero. Con var findSortKeYi
este código en su lugar, recibimos una columna resaltada después de cada operación de if ($header.is(' .sort-alpha')) (
ordenación, como muestra la figura 7.8. " findSortKey = function($cell) (
return $cell.find(' .sort-key') .text() .toUpperCase()
+ ' , + $cell.text() .toUpperCase() ;
e Auttlor(s) I };

else if ($header.is(' .sort-numeric')) (


K SOOttAlIen
findSortKey = function($cell) (
var key = $cell.text() .replace(/'['\d.l*/, ");
key = parseFloat(key);
return isNaN(key) ? o : key;
SteveA1wa1 };

elsa if ($header.is(' .sort-date')) (


findSortKey = function($cell) (
Or. Ma/1( A_e, aaln
return Date.parse('l ' + $cell.text());
};

PhUlppe aaurnann, Henriette


aaumann, Pa1l1ck Grassle
if (findSortKey) (
$header.addClass('clickab1e') .hover(function()
$header.addClass('hover') ;
Figura 7.8. Resaltar una columna. }. function () (
$header.removeC1ass('hover') ;
}) .c1ick(function() {
var sortDirection = 1;
Alternar la dirección de la ordenación if ($header. is ( , . sorted-asc' ) )
sortDirection = -1;
Nuestra última mejora de ordenación es permitir ordenación ascendente y descen- }
dente. Cuando el usuario hace clic en una columna que ya está ordenada, queremos in- var rows = $table.find('tbody > tr') .get();
$.each(rows, function(index, row) (
vertir el orden actual de ordenación.
var $ce11 = $(row) .chi1dren('td') .eq(co1umn);
Para invertirlo, todo lo que tenemos que hacer es invertir los valores devueltos por row.sortKey = findSortKey($ce11);
nuestro comparador. Podemos hacer esto con una sencilla variable: }) ;

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
}) ;

$.each(rows, function(index, row) {


mostrar se almacenan en una base de datos, es fácil extraer un bloque de información de
$table.children('tbody') .append(row); cada vez utilizando la cláusula LIMIT de MySQL, ROWNUM en Oracle, o métodos equi-
row.sortKey = null¡ valentes en otros motores de base dedatos.
}) ;
Como con nuestro ejemplo de ordenación inicial, la paginación se puede activar al
$table.find('th') .removeClass('sorted-asc')
.removeClass('sorted-desc')¡
enviar información al servidor en una cadena de consulta, como index . php ?page= 52 ..
if (sortDirection _. 1) { y nuevamente, como antes, podemos llevar a cabo esta tarea con una carga completa de
$header.addClaas('sorted-asc'); página o al utilizar AJAX para recuperar sólo una parte de la tabla. Esta estrategia de-
} pende del navega dar, y puede gestionar amplios conjuntos de datos muy bien.
e1se {
$header.addClass('sorted-desc');
}
$table.findl'td') .removeClass('sorted')
Ordenar y paginar van juntos
.filter(' :nth-child(' + (column + 1) + ')')
.addClass('sorted') ; Los datos que se benefician de la ordenación es probable que también sean un candida-
$table.alternateRowColors(); to para la paginación. No es raro desear combinar estas dos técnicas para la presentación
}) ;
}
de datos. Puesto que ambos afectan al conjunto de datos que está presente en una página,
sin embargo, es importante considerar sus interacciones mientras se implementan.
]l;
}) ;
.' Tanto ordenar como paginar se pueden realizar tanto en el servidor, o en el nave-
}) ;
gador Web. Sin embargo, debemos mantener las estrategias para las dos tareas en sin-
cronía; de lo contrario, podemos acabar con un comportamiento confuso. Suponga, por
Como un beneficio secundario, puesto que utilizamos clases para almacenar la direc-
ejemplo, que tenemos una tabla con ocho filas y dos columnas, ordenada inicialmente
ción de ordenación, podemos aplicar estilo a los encabezados de columna para indicar
por la primera columna.
el orden actual también, como se ve en la figura 7.9.
Si los datos se vuelven a ordenar por la segunda columna, muchas filas pueden cam-
biar de lugar, como se ve en la figura 7.10.

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.

Ahora consideremos lo que sucede cuando se añade paginación a la mezcla. Suponga


Paginación del lado del servidor solamente que las cuatro primeras filas se facilitan por el servidor y el navegador intenta
ordenar los datos.
Ordenar es una forma estupenda de pasar por una amplia cantidad de datos para Si la paginación se realiza por el servidor y la ordenación por el navegador, todo el
encontrar información. También podemos ayudar al usuario a centrarse en una parte conjunto de datos no se encuentra disponible para la rutina de ordenación, haciendo
del amplio conjunto de datos al paginarlos. que los resultados sean incorrectos, como muestra la figura 7.11.
lImI
r lmII
7. Manipulación de tabla
II Aprende jQuery 1.3

.-- 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

Paginación JavaScript var $pager = $('<div class=Upager ></div>')¡


for (var page = o; page < numPages; page++) {
l1

$('<span class=lIpage-number > , + (page + 1) +


1t '</span>')
Por lo tanto, examinemos cómo añadiremos paginación JavaScript a la tabla que ya .appendTo($pager) .addClass('clickable');
hemos hecho que se pueda ordenar en el navegador. En primer lugar, nos centraremos }
$pager. insertBefore ($table) ;
en mostrar una determinada página de datos, sin tener en cuenta la;interacción de usua-
rio por ahora. El número de páginas se puede encontrar al dividir el número de filas de datos por
$ (document) .ready(function() {
el número de elementos que deseamos mostrar en cada página. Puesto que la división
$('table.paginated') .each(function() puede no mostrar un entero, debemos redondear el resultado utilizando Math.cei 1 ( )
var currentPage = o; para aseguramos de que la última página parcial estará accesible. Luego, con este nú-
var numPerpage = 10;
mero a mano, creamos botones para cada página y posicionamos el nuevo paginador
var $table = $(this);
$table.find('tbody tr') .hide()
sobre la tabla, como muestra la figura 7.13.
.slice(currentPage * numPerpage,
(currentPage + 1) * numPerpage) 1112113114115116117
.show() ; e Author(s)
}) ;
}) ;

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.
}) ;

Esto funciona, porque la nueva función repaginate () se invoca cuando se carga


la página y cuando se hace clie en cualquiera de los vínculos de página. Todos los vín-
culos nos presentan una tabla que, sin embargo, no tiene filas de datos, como se ve en
Figura 7.15. Desplazarse a diferentes páginas.
la figura 7.14. - ,.

Marcar la página actual


!"!t!!1ItlIQ4MIfflHillml
~ I Nuestro paginador se puede hacer más amigable al resaltar el número de la página
actual. Solamente necesitamos actualizar las clases en los botones cada vez que se hace
Figura 7.14. Preparar los botones del paginador.
clic en uno:
El problema es que al definir nuestro manejador click, hemos creado un cierre. El $Idocument ) .ready(function() (
manejador click hace referencia a la variable page, que se define fuera de la función. $('table.paginated') .each(function()
Cuando la variable cambia la siguiente vez que se pasa por el bucle, esto también afecta var currentPage = o;
a los manejadores click que ya hemos establecido para los botones anteriores. El efec- var numPerpage = 10;
var $table = $Ithis);
to es que, para un paginador con 7 páginas, cada botón nos dirige a la página 8 (el valor var repaginate = function() (
final de página cuando se completa el bucle). Más información sobre cómo funcionan $table.find('tbody tr') .hide()
los cierres se puede encontrar en el Apéndice C. .slice(currentPage * numPerpage,
mi 7. Manipulación de labia Aprende jQuery 1.3 11m
(currentPage + 1) * numPerpage)
.show() ; porque se encuentra contenido dentro de un manejador $ (document) . ready (! di-
}; ferente. Podríamos simplemente consolidar las dos piezas de código, pero en su lugar
var numRows = $table.find('tbody tr') .length; hagamos algo diferente. Podemos desacoplar los comportamientos, de modo que una
var numpages = Math.ceil(numRows / numPerpage} i ordenación in'¿;ocael comportamiento ,repaginate si existe, pero lo ignora de lo con-
var $pager = $ ('c:::div class=lIpagertl></div>');
foro (var page = O; page < numpages; page++) {
trario. Para conseguir esto, utilizaremos un manejador para un evento personalizado.
$('c:::span class="page-numbertl></span>') .text(page + 1) En nuestra explicación de gestión de evento anterior, nos limitamos a nombres de
.bind('click', {newPage, page} , function(event) { evento que se activaban por el navegador Web, corno click y mouseup. Los métodos
currentPage = event.data(tnewPage t] i
.bind () y . trigger () no se limitan a estos eventos, sin embargo; podemos utilizar
repaginate() ;
$(this) .addClass('active')
cualquier cadena corno un nombre de evento. Utilizando esta posibilidad de uso, po-
.siblings() .removeClass('active'); / demos definir un nuevo evento denominado repaginate corno un sustituto para la
}) .appendTo($pager) .addClass('clickable'); función que hemos estado invocando:
}
$pager. insertBefore ($table) $table.bind('repaginate', function() {
.find('span.page-number:first').addClass('active')¡ $table.find('tbody t r v.) .hide()

}) ; .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

$('tbody tr,even', this)


.rernoveClass(lodd l) .addClass('even') i
Figura 7.16. Indicadorde la páginadel paginador. return this¡
};
$ (document) .ready(function() {
$('table.sortable') .each(function()
var $table = $(this);
Paginación con ordenación
$table.alternateRowColors();
$('th', $table) .each(function(column)
Comenzamos este debate indicando que los controles de ordenación y paginación var $header = $(this);
tenían que ser conscientes unos de otros para evitar resultados confusos. Ahora que te- var findSortKey;
nemos un paginador que funciona, tenemos que hacer que las operaciones de ordenación if ($header.is(' .sort-alpha')) (
respeten la selección de página actual. findSortKey = function($cell) {
return $cell.find('.sort-key') .text() .toUpperCase()
Hacer esto es tan sencillo corno invocar nuestra función repaginate () siempre + ' , + $cell.text() .toUpperCase() ;
que se lleva a cabo una ordenación. El ámbito de la función, sin embargo, hace que sea r.
problemático. No podemos llegar a repaginate () desde nuestra rutina de ordenación
mi 7. Manipulación de tabla Aprende jQuery 1.3 lliiI
else if ($header.is(' .Bort-numeric·» {
"
$('table.paginated') .each(function()
findSortKey = function($cell) ( var currentPage = O;
var key = $cell.text() .replace(/A[A\d.J*/, "); var numPerpage = 10i
key = parseFloat(key); var $~able = $(this);
return isNaN(key) ? o , key; $table.bind('repaginate', fun~tion()
}; $table.find('tbody tr') .hide()
.slice(currentpage * numPerPage,
else if ($header.is(' .sort-date')) ( (currentPage + 1) * numPerpage)
findSortKey = function($cell) ( .show ();
return Date.parse('l ' + $cell.text()); }) ;
}; var numRows = $table.find('tbody tr') .length;
r'
var numpages = Math.ceil(nurnRows / numPerPage);
var $pager = $('<div class="pager ></div>');l1

if (findSortKey) ( for (var page = o; page < numPages; page++) {


$header.addClass('clickable') .hover(function() $('<span class="page-number"></span>') .text(page + 1)
$header.addClass('hover') ; .bind('click', {newPage, pagel, function(event) (
}, function () ( currentPage = event.data{'newPage'];
$header.removeClass{'hover') ; $table.trigger('repaginate') ;
}) .click(function() { $(this) .addClass('active')
var sortDirection = 1; .siblings() .removeClass('active') i
if ($header.is(' .sorted-asc')) }) .appendTo($pager) .addClass('clickable');
sortDirection = -1; " }
} $pager.insertBefore($table)
var rows = $table.find('tbody , tr') .get(); .find(rspan.page-number:first') .addClass('active') i
$.each(rows, function(index, row) ( }) ;
var $cell = $ (row) .children('td') .eq(column); }) ;
row.sortKey = findSortKey($cell);
}) ;

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.

Figura 7.18. Patrón a franjas aplicado a filasubencabezado.


Alternar color de filas
Ya hemos visto un sencillo ejemplo de resaltar fila anteriormente en este capítulo, y Podemos garantizar que nuestro patrón de alternar fila empieza de nuevo con cada
en un capítulo anterior. Marcar las filas de un color diferente es una forma común de año al utilizar la pseudo-clase : nth -child () que hemos aprendido en un capítulo an-
dirigir la vista del usuario entre múltiples columnas de forma precisa. terior, como se ve en la figura 7.19.
l'
ElI!I 7. Manipulación de tabla
,r Aprende jQuery 1.3 II!II
Como eje~plo, modificaremos el diseño a franjas de nuestra tabla de noticias 'para co-
lorear sus filas de tres en tres.

Figura 7.19. Patrón a franjas comienza de nuevo con cada año.


$ (document) .ready(function() {
$('table.striped tr:nth-child(odd) ') .addClass('odd');
$('table.striped tr:nth-child(even) ') .addClass('even');
}) ; Figura 7.21. Mejorarla presentación de filas a franjas.

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 ') ;
}) ;
}) ;

Figura 7.20. Excluirlas filassubencabezado.


Este código realiza mucho en muy poco espacio, por lo que lo dividiremos en peque-
$ (document) .ready(function() { ñas piezas. Primero, utilizamos el método. each () como antes para segmentar nuestra
$('table.striped tr:not(:has(th)) :odd') .addClass('odd'); tarea al agrupar filas. Queremos que nuestras franjas de tres filas empiecen de nuevo des-
$('table.striped tr:not(:has(th)) :even') .addClass('even');
pués de cada subencabezado, por lo que esta técnica nos permite trabajar en una sección
}I;
de cada vez. Luego, utilizamos. f ind () como en nuestro último ejemplo para localizar
Ahora los encabezados se excluyen, pero las agrupaciones empezarán con una fila odd todas las filas que no tienen elementos -c t h» (y por lo tanto, no son subencabezados).
o even dependiendo de la clasificación aplicada a la fila de datos anterior. Reconciliar Ahora, necesitamos seleccionar los tres primeros elementos de este conjunto, saltar-
estos dos comportamientos puede ser complicado; una opción sencilla es incorporar algo se tres elementos, etc. Aquí es donde entra en juego. f i 1ter ( ) . La función filtro toma
de iteración explícita utilizando el método . each ( ) . un argumento que contiene el índice del elemento dentro del conjunto coincidente, es
decir, el número de fila en el apartado de la tabla que estamos examinando. Si, y sólo si,
$( document) .ready(function() { nuestra función filtro devuelve true, el elemento permanecerá en el conjunto.
$('table.striped tbody') .each(function() {
$(this) .find('tr:not(:has(th)) :odd') .addClass('odd');
El operador módulo (%) nos proporciona la información que necesitamos. La expresión
$(this) .find('tr:not(:has(th)) :even') .addClass('even'); index % 6 evalúa en el resto del número de fila cuando se divide entre 6; si este resto es
}) ; O,1, o 2, entonces marcaremos la fila como odd; si es 3, 4 ó 5, la fila será even.
});
El código, según se ha presentado, solamente marca los conjuntos odd de filas. Para
Ahora cada agrupación se alterna en franjas de forma independiente, y las filas su- aplicar también la clase even, podríamos escribir otro filtro que aplique el filtro contra-
rio, o podríamos ser un poco más creativos:
bencabezado se excluyen de los cálculos, como se ve en la figura 7.21.
$ (document) .ready(function() {
$('table.striped tbody') .each(function() {
Alternar colores de fila avanzado $(this) .find('tr:not(:has(th)) ') .addClass('even')
.filter(function(index) {
Estas manipulaciones de filas par e impar nos han preparado para técnicas más com- return (index % 6) < 3;
plicadas. En tablas particularmente densas, incluso los colores de filas alternos pueden }) .removeClass('even') .addClass('Odd');
});
ser confusos para la vista, y puede ser beneficioso alternar colores en un intervalo mayor.
}) ;
Aprende jQuery 1.3 ElD
Aquí aplicamos la clase even a todos las filas, y la eliminamos si añadimos la clase $authorcells.clicklfunctionl)
odd. Nuestra tabla ahora tiene aplicado estilo alternando agrupaciones de fila, lo que // Resaltar filas aquí,
empieza de nuevo con cada nueva sección de la tabla, como se ve en la figura 7.22. }) ;
}) ;

li3III HaadUn 8 ~ "


r
.<' "'," ...,.·.. ~·':.Y
~té;':-:::i1;~, . ;~' .r Observe que utilizamos la pseudo-clase : nth- child (n) como parte de la expresión
~~,..~{~}~f~~~~~~~~ ~ ti;;: selector que apunta a la tercera columna donde está la información de autor, En caso de
seo 28 '
.'JOh~:~~{g',;il~t1~(
~i~-party que la estructura de tabla fuera a cambiar más adelante, querríamos que esta constan-
Aug31 ~;r," :'R-eY-8;~'g'O'(?\::~\~:~~~~ferti~fe
Aug29
te 3 estuviera solamente en un lugar en el código, de modo que se pudiera actualizar
: Jahn Reslg "ennooncement
Aug 15 Kan Swedberg conference
fácilmente. Por esta razón, almacenamos el resultado de nuestro selector en la variable
Jul14 Paut Bakaus ralease
$authorCells en lugar de repetir el selector cada vez que se necesita, 'f:
r.;.;'
Jun 26 Paul Bakaus release
Jun '26 . ..
" ';,;
• :1' f;; R~y"8anQ~':,>?:·k;·,: 'i;nf~r~~,ca E.'"
l
Jun 9
'-"':;',.- .: -"".,•••" ...•.'.,x\:
~;.:Pa~18aka~s ...;~.;: reíease
:.:1):.··· .. ;·
\
Jun 4 "John Reslg Recuerde que a diferencia de los índices JavaScript, la pseudo-clase : nth- child (n}
Mar7 Rlchard Wonh conrerenoe
Feb B
basada en CSSempieza a numerar en 1, no en o.
John Reslg release
Jan 23 Paul Bakaus

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 }) ;

(que será el nombre del autor) y lo mostrará.


}) ;
».
Luego lo situamos en la ubicación apropiada en la página con la función pos i t ion-
Tooltip (). Puesto que la descripción emergente se ha anexado al elemento body, ne- Observe que los argumentos para los métodos . hover () y . mousemove () están
cesitaremos algo de CSS para hacer que flote sobre la página en la ubicación correcta: haciendo referencia a funciones que están definidas en alguna otra parte. Como tal, orni-
#tooltip ( timos los paréntesis que seguirían a llamadas a las funciones. La descripción emergente
position: absolute; ahora aparece cuando pasamos por encima de una celda autor, como muestra la figura
z-index: 2; 7.24, se mueve con el movimiento del ratón y desaparece cuando movemos el cursar
background: #efd;
del ratón fuera de la celda.
border: lpx solid #ccc;
padding: 3px;

Por último, escribimos una sencilla función hideTooltip ():


var hideTooltip = function()
$tooltip.hide() ; Figura 7.24. Descripciónemergente al pasar sobre celda de autor.
};

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

}; if ($(this) .parent() .is(' .highlight')


$authorCells action = 'Unhighlight';
lElI 7. Manipulación de tabla Aprende jQuery 1.3 EiD
} Ahora la descripción emergente ofrece una sugerencia más inteligente cuando el pun-
$tooltip
.text(action + I all articles by , + authorName)
tero pasa por 'encima de una fila que ya está resaltada, como se ve en la figura 7.26.
.show() ;

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>~:'_·
;~'.

$ (document) .ready(function() { Ju'n 26, jCiuery UI1.5, 1


var $authorCells = $('table.striped td:nth-child(3) ');
var $tooltip = $('<div id=lItooltip"></div>') .appendTo('body'); Figura 7.26. Mejorar la descripción emergente.
var positionTooltip = function(event) (
var tPosX = event.pageX;
var tPosY = event.pageY + 20;
$tooltip.css({top: tPosY, left: tPOSX}); Contraer y expandir secciones
};
var showTooltip = function(event) (
Cuando un amplio conjunto de datos se divide en secciones, puede resultar de uti-
var authorName = $(this) .text();
var action.= 'Highlight';
lidad ocultar información en la que no estamos interesados en el momento. En nuestra
if ($(this) .parent() .is(' .highlight')) tabla de artículos de noticias, las filas se agrupan por año¡ contraer, u ocultar, un año
action = 'Unhighlight' i de artículos puede ser una forma conveniente de obtener un visión global de todos los
} datos de la tabla sin tener que desplazarse demasiado.
$tooltip
,text(action + ' all articles by , + authorName)
Para que se puedan contraer las secciones de la tabla de artículos de noticias, primero
.show () ; necesitamos crear un elemento de página que se utilizará para activar el comportamien-
positionTooltip(event) ; to. Una interfaz estándar para elementos que se pueden contraer es un signo menos, con
}; un correspondiente signo más para elementos que se pueden ampliar. Insertaremos el
var hideTooltip = function()
$tooltip. hide () ;
icono con JavaScript, siguiendo las técnicas de mejora progresiva.
};
$ (document) .ready(function() {
var collapselcon = r../images/bullet_toggle_minus.pngr¡
$authorCells
var collapseText = rCollapse this section'¡
.addClass('clickable')
var expandlcon = '.. /images/bullet_toggle-plus.png';
.hover(showTooltip, hideTooltip)
var expandText = rExpand this section' i
.mousemove(positionTooltip)
$('table.collapsible tbody') .each(function()
.click(function(event) (
var $section = $(this);
var authorName = $(this) .text();
$('<img />') .attr('srcr collapselcon)
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) ;

}) ;
}) ;

}) ;

Obtenemos la etiqueta para el cuadro de filtro de la columna encabezados de


modo que este código se puede volver a utilizar para otras tablas bastante fácilmente.
Ahora tenemos un encabezado a la espera de algunos botones, como se muestra en la
Figura 7.27. Contraer una sección. figura 7.28.
lIfI 7. Manipulación de tabla Aprende jQuery 1.3 lID
Hoadllno accidentales debido a las propiedades de los cierres. Luego, en el manejador click,
comparamos los contenidos de cada celda contra la palabra clave y ocultamos la fila si
no hay coincidencia. Puesto que nuestro selector de fila excluye filas que contienen un
elemento « t.h », no tenemos que preo¡:uparnos por que se oculten subencabezados.
Ambos vínculos ahora funcionan según lo anunciado, como se ve en la figura 7.29.

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);

var keywords = {};


$table.find('td:nth-child(' + (column + 1) + ') ')
.each(function() (
keywords [$ (this) .text ()1 = $ (this) .text () ;
});
$.each(keywords, function(index, keyword) {
$('cdiv class="filterrr></div>') .text(keyword)
.bind('click', {key: keyword}, function(event) (
$('tr:not(:has(th))', $table) .each(function() {
var value = $('td', this) .eq(column) .text();
if (value == event.data['key'l) (
$(this) .removeClass('filtered');
}
else (
$(this) .addClass('filtered');
}
».
$(this) .addClass('active')
.siblings() .removeClass{'active') i
$table.trigger(/stripe') ;
}) .addClass ('clickable') .appendTo ($filters) ;
}) ;
$filters.insertBefore($table) ;
}
}) ;

}) ;

}) ;
/
¡

~
.. .'\

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.

con funciones Mejorar un formulario básico


Igual que aplicamos jQuery a sitios Web, siempre debemos preguntamos cómo se
verán y funcionarán las páginas cuando JavaScript no se encuentra disponible para
nuestros visitantes (a menos, por supuesto, que sepamos exactamente quiénes serán los
visitantes y cómo estarán configurados sus navegadores). Sin embargo, esto no quiere
decir que no podamos crear un sitio más atractivo y más completo para visitantes con
JavaScript activado. El principio de mejora progresiva es popular entre los desarrollado-
res JavaScript porque respeta las necesidades de todos los usuarios mientras proporciona
ED 8. Formularios con funciones Aprende jQuery 1.3 mi
c::/li>
algo adicional a la mayoría de ellos. Para demostrar mejora progresiva con respecto a c::li>
formularios, crearemos un formulario de contacto que podemos mejorar tanto en apa- <label for=1I1ast-namell>Last Namec::/label>
riencia como comportamiento utilizando jQuery. < in~ut clase=," required type= text" name= IIlast
ti ti -name 11
. ~d="la9t-namel~ ..,/>
<span> (required) c::/span>
</li>
Estilo de formulario mejorado de forma progresiva c::li>How would you like to be contacted?
(choose at least one methodl
En primer lugar, realicemos algunas modificaciones estéticas a nuestro formulario. <ul>
<li>
Sin JavaScript activado, el primer conjunto de campos del formulario se muestra como
<label for=l1by-emaill1>
se ve en la figura 8.1. ,.'
<input type=l1checkbox" name=l1by-contact-typell
value=I1E-maill1 id=l1by-emaill1 />
PenIonalln by E-Mail
Fln51Neme I j(raqulnld) </label>
Laal Neme I I(requlnld) <input class=l1conditional" type=lItext" name=lIemail"
id=l1email" />
How would you Ilke lo be oontacted'l (choose 81lees! one meIhod)
<span>(required when corresponding checkbox
9 by E.Mell I I(requlnld when correspondlng checl<box checked)
checkedl</span>

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 ••

de elemento "estándar" para elementos dentro de un conjunto de campos, la lista orde-


Un enfoque alternativo que mantiene los elementos <1eqerid» intactos implica situar
nada parece ser lo más parecido a cualquier cosa que represente el significado semántico sus contenidos en una etiqueta e span»:
de los elementos en un formulario de contacto.
Con nuestro HTML en su lugar, estamos listos para utilizar jQuery para la mejora $ (document) :!ready(function () (
$('legend') .wraplnner(l<span></s~an>');
progresiva. }) ;

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.

El by E-MaIl I :::J (mqulred when correspondlng 00_ OOeeked)


$ ( 'form :input' )
El by Phone L ::J (requlred when correspondlng cheekbox OOeeked) .filter('.required')

!!I by Fax L .__ ::J (mqulred when correspondlng chocldlox cheeked)


.next('span')
.end ()
.text(requiredFlag) .end()

.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 conditlonalKey = $ ('input.conditional:first')


ter ( , . required' ) y el segundo lo revierte a $ ( , form : input' ) . De esta forma, cuan- .next(lspanl) .text{)¡
do . fi1ter ( , . conditiona1' ) selecciona elementos con c l as s« "condi tiona1",
se aplica a todas las entradas de datos dentro del formulario. Ahora, puesto que un solo requiredKey = requiredFlag +
requiredKey.replace(/'\(.+)\)$/, '$1');
asterisco (*) puede no captar inmediatamente la atención del usuario, también añadi-
conditionalKey = conditionalFlag +
remos c1ass=" req-1abe1" a « Labe l » para cada campo obligatorio y aplicaremos conditionalKey.replace(/'\((.+}\)$/, '$1');
font -weight :bo1d a esa clase. Para ello, podemos ampliar aún más la cadena.
// . código continúa
$ (document) .ready(function() { j);
var requiredFlag = * ,. I

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¡_
, .

mil 8. Formularios con funciones Aprende jQuery 1.3 mi


Insertar la leyenda del mensaje del campo ocultarlas, junto con sus indicaciones correspondientes, cuando el documento se carga
inicialmente:. .
Ahora que hemos recuperado los mensajes del campo sin los paréntesis, podemos
insertarlos, junto con sus indicaciones correspondientes, por encima del formulario: $ (document) .ready(function() (
$ (,input: condi tional ') .next (,spa'l') .andSelf ().hide ();
$ (document) .ready(function() ( }) ;

var requiredFlag = * '; !

var conditionalFlag = ' ** '¡

var requiredKey = $('input.required:first')


.next (,span ') .text ();
P.rsonallnfo
var conditionalKey = $('input.conditional:first')
.next(lspan') .text() i flrstNama r.=.::: 1-
requiredKey = requiredFlag + L.aI1Namo I 1-
requiredKey.replace(/A\«.+)\)$/, '$1'); How wouid you IIke lo be oonIBcIed'I (choose alleast one molhod)
conditionalKey = conditionalFlag + [_n_ -- HJ"
conditionalKey.replace(/A\«.+)\)$/, '$1'); I!J by E-Mail

$('<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 .
}) ;

El conjunto de campos ahora tiene su interfaz modernizada, como muestra la si-


Las cinco nuevas líneas le deberían ser relativamente familiares ahora. Aquí tiene lo guiente figura:
que hacen:
- ------------_._----

1. Crear un nuevo elemento de párrafo. Personal Info

2. Asignar al párrafo una clase de field-keys. fl •.•1 N.",. [-----------J-


L.aI1 N.",. I 1-
3. Anexar requiredKey y un salto de línea al párrafo. How wouId you IIke lo be conIacIed'I (choooe al leas! one melhod)

4. Anexar condi tionalKey al párrafo. !!! by E-Mail

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; }) ;

si es así, mostramos la entrada de texto, mostramos la indicación, y añadimos la clase


req-label al elemento « Labe L» padre: y esto concluye la parte de estilo de esta transformación de formulario. A continua-
ción, añadiremos algo de validación del lado del cliente.
$ (document) .ready(function() (
$('input.conditional') .next('span') .andSelf() .hide()
.end () .end () - Validación de formulario
.each(function() (
var $thislnput = $(this);

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 == ") ( errorMessage += " when its related checkbox


var errorMessage = 'This is a required field'¡ i s checked' j
if ($(this) .hasClass('conditional'» {
errorMessage += " when its related + I
$ ('<span></span>')
'checkbox ia checked'; .addClass('error-message')
.text(errorMessage)
w

mi 8. Formularios con funciones Aprende jQuery 1.3 lID


.appendTo($listltem) ;
campos de correo electrónico, teléfono y tarjeta de crédito. Para nuestra demostración,
$listItem.addClass( 'warning');
situaremos 'en su lugar una sencilla comprobación de expresión regular para el campo
} de correo electrónico, Echemos un vistazo al código completo para la validación de co-
j); rreo electrónico antes de adentrarnos en la propia expresión regular:
»; $ (document) .ready(function() {
// ... código continúa ..
Por último, tenemos un script de validación en funcionamiento para campos obliga- if (this.id == 'email') {
torios y condicionalmente obligatorios. Incluso después de incorporar y salir de campos var $listltem = $(this) .parents('li:first');
obligatorios repetidamente, nuestros mensajes de error ahora se muestran de forma co- if ($(this).is(':hidden')) {
rrecta, como se ve en la figura 8.7. this.value = 11;

}
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('&bull; , + list.join('<br />&bull; '))
.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.',

&El 8. Formularios con funciones Aprende jQuery 1.3 &ni


Elprimer cambio en el código es la variable list establecida en una tabla vacía. Luego, especialmente si el usuario desea hacer clic en la mayoría de ellas. Una opción p"!rase-
obtenemos cada etiqueta que es un descendiente de un elemento con la clase warning leccionar, o deseleccionar, todas las casillas de verificación es de utilidad en este tipo de
y empujamos su texto en la tabla list (con la función nativa JavaScript push). Ahora, situación. Po? lo tanto, creemos una.
el texto de cada una de estas etiquetas constituye un elemento aparte en la tabla listo Para empezar, creamos un nuevo.elemento <1i », lo completamos con un <1abe 1 »,
Modificamos nuestra primera versión del contenido <di v id=" submi t -message" > dentro del cual situamos <input t.ype "checkbox" id= "discover-all"
e > y algo
un poco y le anexamos nuestra tabla listo Utilizando la función JavaScriptnativa j oin () de texto, y lo anexamos al elemento -e u L» dentro de -e Lí class="discover" >:
para convertir la tabla en una cadena, unimos cada uno de los elementos de tabla con $ (document) .ready(function() {
un salto de línea y un boliche, como se ve en la figura 8.9. $ ('<1i></li>')
.html('<label><input type="checkbox" id="discover-allll />' +
Ploase oonect """'" wtth 1he fo!owIng 3I1e1d.:
-F1rs1Name I <em>check all</em></label>')
-wtName .prependTo('li.discover :> ul')¡
- by E-MaIl }) ;

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');

Manipulación de casilla de verificación $('#discover-all').click(function()


var $checkboxes
{
= $(this).parents('ul:first')
.find(':checkbox')¡
Nuestro formulario de contacto también tiene una sección de varios, que contiene if (this.checked) {
una lista de casillas de verificación, como se muestra en la figura 8.10. $checkboxea.attr('checked', true};
el se {
MIsceltaneous $checkboxea.attr('checkedl, 11);

}
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

var requiredKey = $('input.required:first')


.next('span') .text() I
Mlscellaneous var conditionalKey = ~('input.conditional:first')
How dld you <IIscover us? (Check all !ha! apply) .next('span') .text() I
requiredKey = requiredFlag +
13 check o" requiredKey.replace(/'\«.+)\)$/, '$1') I
conditionalKey = conditionalFlag +
13 Magazine conditionalKey.replace(/'\«.+)\)$/, '$1') I
" $('<p></p>')
I3webs1t8
.addClass('field-keys')
8 Televlslon .append(requiredKey + '<br />')
.append(conditionalKey)
S MovIe .insertBefore('#contact') ;
El School
$ ('form :input' )
SMom .filter(' .requiredr)
.next('span') .text(requiredFlag) .end()
B BUlboord
.prev (' Labe l addClass ('req-label') .end O
i ) •

SGratIIII .end O
.filter(' ,conditional')
13 Detritus .next('span') .text(conditionalFlag);

El HeteMall // Alternar casilla de verificación: entradas condicionales de texto.


Figura 8.11. Permitirseleccionar todas las casillas de verificación.
$('input.conditional') .next('span') .andSelf() .hide()
.erid t ) .end()
y con check all seleccionada, el grupo se parece a la figura 8.12. .each(function() (
var $thislnput = $(this) I
Miscellaneous
var $thisFlag = $thislnput.next('span')I
$thislnput.prev('label') .find(' :checkbox')
How <lid you dlscover us7 (Check all that apply)
.attr('checked', false)
• un-check 0/1 .click(function() {
if (this.checked) (
I!f Magazine $thislnput.show() .addClass('required') I
$thisFlag.show() I
I!fW&b1llte $(this) .parent('label') .addClass('req-label') I
el se (
1f1l>levlslon
$thislnput.hide() .removeClass('required') .blur() I
I!I Movle $thisFlag.hide() I
$ (this) .parent('label') .removeClass('req-label') I
I!f School }
}),
Figura 8.12. Todas seleccionadas. }) I
BfI 8. Formularios con funciones Aprende jQuery 1.3 ea
// Validar campos al perder foco. .append('&bull; , + fieldList.join('<br />&bull; '))
$('form :input') .blur(function() ( .ansertBefore('#send');
$(this) .parents('li:first') .removeClass('warning') return false¡
.find('span.error-message') .remove() ; };
});
if ($(this) .hasClass('required')) (
var $listltem = $(this) .parents('li:first'); // Casillas de verificación
if (this. value == ") { $('form :checkbox') .removeAttr('checked')¡
var errorMessage = 'This is a required field';
if ($ (this) .is (, .conditional')) { // Casillas de verificación con (un)check all.
errorMessage += " when its related checkbox is $ ('<li></li>')
checked' i .htrnl('<:label><input type=ucheckbox" id="discover-allu />' +
('
} I<em>check all</em></label>')
$('<span></span>') .prependTo('li.discover > ul');
.addClass('error-message') $('#discover-all') .click(function() {
.text(errorMessage) var $checkboxes = .$(this) .parents ('ul: first')
.appendTo($listltem); . find ( checkbox' ) ;
I :

$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

}); /1 Lista continúa ...


'xarnl' ,
$searchLabel.click(function() ( 'xoops "
$searchlnput.trigger('focus') ; ) ;

}) ; $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.

print (' ['. implode(', $possibilities). '] ');


Autocompletar AJAX
La página compara la cadena proporcionada contra el principio de cada término, y
Podemos realzar aún más nuestro campo de búsqueda al ofrecer autocompletar sus compone una tabla JSON de coincidencias. Las operaciones de manipulación de cadena
contenidos. Esta característica permitirá a los usuarios escribir el principio de un térmi- aquí (como str _replace () e implode () ) se aseguran de que el resultado del script
no de búsqueda y que se le presenten todos los posibles términos que comienzan con la sea JSON con formato adecuado, para evitar errores JavaScript durante el análisis.
cadena escrita. Puesto que la lista de términos se.puede extraer de una base de datos que
dirige el sitio, el usuario puede saber que los resultados de búsqueda están disponibles si En el navegador
se utiliza el término facilitado. También, si la base de datos proporciona los términos en
orden de popularidad o número de resultados, se puede guiar al usuario hacia búsquedas Ahora podemos realizar una petición de este script PHP desde nuestro código
más apropiadas. Autocompletar es un tema muy complicado con sutilezas introducidas JavaScript:
por diferentes tipos de interacción de usuario. Diseñaremos un ejemplo operativo aquí, $ (document) .ready(function() {
pero no podemos, en este espacio, explorar todos los conceptos avanzados como limitar var $autocomplete = $('<ul class=lIautocompletetr></ul>')
el índice de peticiones o completar múltiples términos. Se recomienda el componente .hide()
autocompletar en la colección plug-ins de interfaz de usuario jQuery para implementa- .insertAfter('#search-text');

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() ,

} Completar el campo de búsqueda


}) ,
». Nuestra lista de sugerencias no nos hace mucho bien si no podemos situadas en el
}) ; cuadro de búsqueda. Para empezar, permitiremos que un clic del ratón confirme una
.sugerencia:
Necesitamos utilizar keyup, no keydown o keypress, como el evento que activa la
petición AJAX. Los dos últimos eventos ocurren durante el proceso de pulsar la tecla, 'success': function(data)
if (data.1ength) {
antes de que los caracteres se hayan incorporado realmente en el campo. Si intentamos
$autocomp1ete.empty() ;
actuar sobre estos eventos y lanzamos la petición, la lista de sugerencia iÍá por detrás $.each(data, function(index, term) {
del texto de búsqueda. Cuando se incorpora el tercer carácter, por ejemplo, la petición $('<li></li>') .text(term)
AJAX se realizará utilizando simplemente los dos primeros caracteres. Al actuar sobre .appendTo($autocomp1ete)
.click(function() {
keyup, evitamos este problema. En nuestra hoja de estilo, posicionamos esta lista de
$ ('#search- text' ) .val (term) ,
sugerencias de forma absoluta, de modo que solapa el texto por debajo. Ahora cuando $autocomplete.hide();
escribimos el campo de búsqueda, vemos nuestros términos posibles presentados, como }) ;
se ve en la figura 8.17. }),
$autocomplete.show() ;
I~.m I
'deep
deftnltlVe
d•• lgn Esta modificación establece el texto del cuadro de búsqueda en cualquiera que sea
deslgnlng el elemento de lista sobre el que se haya hecho clic. También ocultamos las sugerencias
developers
dOYOlopment
después de esto.

Figura 8.17. Términos de búsqueda posibles.


Navegación por medio de teclado
Para mostrar adecuadamente nuestra lista de sugerencias, tenemos que tener en Puesto que el usuario está ya en el teclado, y escribiendo el término de búsqueda, es
cuenta el mecanismo incorporado de autocompletar de algunos navegadores Web. Los muy conveniente permitir que el teclado controle la selección desde la lista de sugerencia
navegadores a menudo recordarán lo que los usuarios han incorporado en un campo de también. Necesitaremos mantener un registro del elemento actualmente seleccionado
formulario, y sugieren estas entradas la siguiente vez que se utiliza el formulario. Esto para activado. Primero, podemos añadir una función de ayuda que almacenará el índice
puede parecer confuso cuando se utiliza junto con nuestras sugerencias personalizadas del elemento y llevará a cabo los efectos visuales necesarios para revelar qué elemento
de auto completar, como muestra la figura 8.18. Afortunadamente, esto se puede des- está seleccionado actualmente:
habilitar en los navegadores que realizan autocompletar al establecer el atributo auto-
var selectedltem = null¡
complete del campo de formulario en off. Podríamos hacer esto en el HTML, pero
var setSelectedltem = function(item)
esto no estaría en consonancia con el principio de mejora progresiva porque estaríamos selectedltem = item¡
deshabilitando la función autocompletar del navegador sin ofrecer la nuestra. En su if (selectedltem === null)
lugar, podemos añadir este atributo desde nuestro script: $autocomplete.hide(),
return¡
$('#search-text') .attr('autocomplete', 'off')
r
DiI 8. Formularios con funciones I
Aprende jQuery 1.3 mi
if (selectedltem < O)
En tercer lugar, el primer elemento se resalta inmediatamente cuando se muestra la lista
selectedltem =O;
de sugerencias, como se ve en la figura 8.19.
l
if (selectedltem >= $autocomplete.find('li') .length) (
selectedltem = $autocomplete.find('li') .length - 1;
l
$autocomplete.find('li') .removeClass('selected')
jde~eps I J
detlnl1lve
.eq(selectedltem) .addClass('selected'); q'
deslgn
'g
$autocomplete.show() ; doalgnlng
l; developeno
development ~
La variable selectedItern se establecerá en null siempre que no haya un elemen-
Figura 8.19. Efecto del manejador mouseover.
to seleccionado. Al invocar siempre setSelectedItern () para cambiar el valor de la
variable, podemos estar seguros de que la lista de sugerencias solamente está visible
Ahora necesitamos permitir que las teclas del teclado cambien qué elemento está ac-
cuando existe un elemento seleccionado.
tualmente activo en la lista.
Las dos pruebas para el valor numérico de selectedItern están presentes para ase-
gurar los resultados en el rango apropiado. Sin estas pruebas, selectedItern terminaría
con cualquier valor, incluso negativo. Esta función se asegura de que el valor actual de Gestionar las teclas del cursor
selectedItern siempre es un índice válido en la lista de sugerencias ..
Ahora podemos revisar nuestro código existente para utilizar la nueva función: Podemos utilizar el atributo keyCode del objeto evento para determinar qué tecla se
ha pulsado. Esto nos permitirá ver los códigos 38 Y 4 O,correspondientes a las flechas
$('#search-text') .attr('autocomplete', 'off') .keyup(function() arriba y abajo, y reaccionar en consecuencia:
$.ajax({
'url': /search/autocomplete.php',
l .. $('#search-text') .attr('autocomplete', 'off') .keyup(function(event)
'data': ('search-text': $ ('#search-text') .val() l, if (event.keyCode > 40 I I event.keyCode == 8) {
'dataType': 'json', // Teclas con códigos 40 e inferior son especiales
'type': 'GET', // (lntro, teclas del cursor, escape, etc.).
'success': function(data) // Código de tecla 8 es retroceso.
if (data.length) ( $.ajax({
$autocomplete.empty() ; 'url': r •• /search/autocomplete.php',
$.each(data, function(index, term) ( 'data': ('search-text': $('#search-text') .val()},
$('<li></li>') .text(term) 'dataType': 'json',
.appendTo($autocomplete) 'type': 'GET' I

.mouseover(function() { 'success': function(data)


setSelectedltem(index); if (data.length) (
}) $autocomplete.empty() ;
.click(function() ( $.each(data, function(index, term) {
$('#search-text') .val(term); $ (,<1i></li>') .text (term)
$autocomplete.hide() ; .appendTo($autocomplete)
}) ; .mouseover(function() (
}) ; setSelectedltem(index) ;
setSelectedltem(O); ».click
} (function () (
else ( $('#search-text') .val(term);
setSelectedltem(null); $autocomplete.hide() ;
}) ;
} }) ;
}) ; setSelectedltem(O);
}); }
else (
Esta revisión tiene varios beneficios inmediatos. Primero, la lista de sugerencias se setSelectedltem(null);

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

else if (event.keyCode •• 38 &&


Eliminar la lista de sugerencias
selectedltem 1== null) (
// Usuario pulsa la Flecha arriba. Existe una última modificación que haremos a nuestro comportamiento autocomple-
setSelectedltem(selectedltem - 1); tar. Deberíamds ocultar la lista de sugerencias cuando el usuario decide hacer alguna
event.preventDefault()¡
otra cosa en la página. En primer lugar, podemos reaccionar a la tecla Ese en nuestro
}
else if (event.keyCode == 40 && manejador keyup, y dejar que el usuario descarte la lista de esa forma:
selectedltem 1== null) (
// Usuario pulsa la Flecha abajo. el se if (event.keyCode == 27 && selectedltem 1== null) (
setSelectedltem(selectedltem + 1); // Usuario ha pulsado la tecla Esc.
setSelectedltem(null) ;
event.preventDefault();
}
}) ;
Aún más importante, deberíamos oculta! la lista cuando el campo de búsqueda pier-
Nuestro manejador keyup ahora comprueba el keyCode que se ha enviado, y lleva de foco. El primer intento en esto es bastante sencillo:
a cabo la acción correspondiente. Las peticiones AJAX se saltan si la tecla pulsada era
'$('#search-text') .blur (function (event)
especial, corno una tecla del cursor o la tecla Ese. Si se detecta una tecla del cursor y la
setSelectedltem(null);
lista de sugerencia se muestra, el manejador cambia el elemento seleccionado en 1 en la }) ;

dirección apropiada. Puesto que escribimos setSelectedItem () para asegurar los


valores en el rango de índices posibles para la lista, no tenemos que preocupamos por Sin embargo, esto provoca un efecto secundario no deseado. Puesto que un clic de
que el usuario se aleje en cualquier extremo de la lista. ratón en la lista elimina foco del campo, este manejador se invoca y la lista se oculta.
Esto significa que nuestro manejador el i ck definido anteriormente nunca se invoca, y
resulta imposible interactuar con la lista utilizando el ratón.
Insertar sugerencias en el campo
Existe una solución sencilla a este problema. El manejador bl ur siempre se invocará
A continuación, necesitamos gestiona! la tecla Intro. Cuando se muestra la lista de antes del manejador el i ck. Una solución alternativa es oculta! la lista cuando se pierde
sugerencias, pulsa! la tecla Intro debería completa! el campo con el elemento actualmente el foco, pero esperar una fracción de segundo primero:
seleccionado. Puesto que ahora vamos a hacer esto en dos lugares, deberíamos separar $I'#search-text') .blur(function(event)
la rutina de completa! campo (que hemos codificado anteriormente para el botóndel setTimeout(function() (
ratón) en una función aparte: setSelectedltem(null) ;
}, 250);
var populateSearchField = function() ( }I;
$('#search-text') .vall$autocomplete
.findl'li') .eqlselectedltem) .text(»;
setSelectedItem(null) ;
Esto nos proporciona una oportunidad para que se active el evento click en el ele-
}; mento de lista antes de que el elemento de lista se oculte.
Ahora nuestro manejador click puede ser una sencilla llamada a esta función.
Podemos invocar esta función cuando gestionamos la tecla Intro también: Autocompletar frente a live search
$('#search-text') .keypress (function (event) {
if (event.keyCode == 13 && selectedltem 1== null) (
El ejemplo anterior se ha centrado en autocompletar el campo de texto, ya que es una
// Usuario pulsa tecla Intro. técnica que se aplica a muchos formularios. Sin embargo, pala búsquedas en particular,
populateSearchFieldl) ; se prefiere una alternativa denominada live search. Esta característica lleva a cabo las
event.preventDefault() ; búsquedas de contenido a medida que el usuario escribe.
}
Funcionalmente, autocompletar y live search son muy similares. En ambos casos,
}) ;
pulsar una tecla inicia un envío AJAX al servidor, pasando los contenidos del campo
Este manejador está anexado al evento keypress en lugar de keyup corno antes. actual junto con la petición. Los resultados luego se sitúan en un cuadro desplegable por
Tenemos que hacer esta alteración de modo que podamos impedir que la pulsación de debajo del campo. En el caso de autocompletar, corno hemos visto, los resultados son
tecla envíe el formulario. Si esperamos hasta que se activa el evento keyup, la presen- posibles términos de búsqueda a utilizar. Con live search, los resultados son las páginas
tación estará ya en marcha. que contienen los términos de búsqueda que se han escrito.
mi 8. Formularios con funciones
Aprende jQuery 1.3 1m
En el extremo JavaScript, el código para crear estas dos características es casi idén- var populateSearchField = function() (
tico, por lo que no entraremos en detalle aquí. Decidir cuál utilizar es una cuestión de $('#sea!ch-text') .val($autocomplete
.find('li') .eq(selectedltem) .text());
equilibrio; live search proporciona más información al usuario con menos esfuerzo, pero setSel~ctedltem(null) ;
típicamente requiere más recursos. };
",
$ ('#search-text')
El código terminado .attr(rautocomplete',
.keyup(function(event)
'offl)
(
if (event.keyCode > 40 II event.keyCode == 8) (
Nuestro código completo para la presentación del campo de búsqueda y comporta- // Teclas con códigos 40 e inferior son especiales
mientos autocompletar es de la siguiente forma: // (Intro, teclas del cursor, Esc, etc.).
// Código de tecla 8 es Retroceso.
$ (document) .ready(function() ( $.ajax«
var $search = $('#search') .addClass('overlabel'); r r: ' •• /search/autocomplete.php',
var $searchlnput = $search.find('input'); 'data', ('search-text', $('#search-text') .val()},
var $searchLabel = $search.find('label'); r dataType ' : . r j son' ,
"t ype ", 'GET',
if ($searchlnput.val()) 'success': function(data)
$searchLabel.hide() ; if (data.length) (
}
$searchlnput
.focus(function() (
.' $autocomplete.empty()
$.each(data,
;
function(index, term) (
$('<li></li>') .text(term)
$searchLabel.hide(); .appendTo($autocomplete)
}) .mouseover(function() (
.blur(function() ( setSelectedltem(index) ;
if (this.value == ") ( }) .click(populateSearchField);
$searchLabel.show()
}
; ».
}) ; setSelectedltem(O);
$searchLabel.click(function() ( }
$searchlnput.trigger('focus') ; else (
}) ; setSelectedltem(null) ;

var $autocomplete = $('<ul fl


class=lIautocomplete ></ul>') }
.hide () }) ;

.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"

&lI 8. Formularios con funciones


Aprende jQuery 1.3 mi
</td>
ctd class="price">$17.99c/td>
Antes de pasar a manipular los campos de formulario, aplicaremos una línea estándar
ctd class="cost">$17.99</td> de código de.fila de diferente color para mejorar la apariencia de la tabla:
</tr>
ctr>
$ (docurnent) .~eady(function() {
ctd class="item"> $ ('#cart "t body tr: nth-child (everO.,') .addClass ('alt' ) ;
}) ;
Drupal: Creating 810g5, Forums, Portals, and
Community Websites
</td> Una vez más, nos aseguramos de seleccionar solamente filas a colorear si están en el
ctd class="quantity"> cuerpo de la tabla, como se ve en la figura 8.21.
<input type="text ll name="quantity-411 value=1I111
id="quantity-4 11 maxlength="3 11 />
</td>
ctd class="pricer'>$35.99</td>
ctd class="cost">$35.99</td>
</tr>
</tbody>
</table>
c/form>

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ü.~

Figura 8.21. Coloreas filas a bandas.


nem QuanUty Prlce Total
Building Telephony Syalems WIIh_
D $26.99 $26.99

571.99 Rechazar entrada no numérica


Smar1y PHP Templata Prograrnmlng and Appllcatlons $35.99
D
eraeUng your MySQL Dombase $17.99 $17.99
D Cuando hemos mejorado el formulario de contacto, hemos tratado algunas técnicas
Drupa!: CraaUngBIogs, Forums. PorIBls, 800 COmmunlly $35.99 $35.99
Websltes D de validación de entrada de datos. Con ]avaScript, verificamos que lo que ha escrito el
SubtolBl $152.95 usuario coincide con lo que esperábamos de modo que pudiéramos proporcionar feedback
Tax 6% $9.18
antes incluso de que se enviara el formulario al servidor. Ahora, examinaremos el equi-
Shlpplng $2 perltem $10.00
valente a validación de entrada de datos denominada enmascarar entrada de datos.
Tocal $1n.13
La validación de entrada de datos comprueba lo que el usuario ha escrito según cier-
~ o €'i!lKi'i0i'd~
tos criterios para entradas válidas. Enmascarar entrada de datos aplica criterios a las
Figura 8.20. Uso de <tfoot> y <thead>. entradas mientras se están escribiendo en primer lugar, y simplemente desestima pulsa-
ciones de teclado no válidas. En nuestro formulario de carro de la compra, por ejemplo,
El orden de este código fuente, aunque no intuitivo para los diseñadores que piensan los campos de entrada de datos deben contener sólo números. El código de enmascarar
visualmente sobre cómo se muestra la tabla, es de utilidad para aquellos con problemas entrada de datos puede hacer que cualquier tecla que no sea un número no haga nada
visuales. Cuando la tabla se lee en alto por dispositivos de ayuda para usuarios con ne- cuando uno de estos campos recibe el foco:
cesidades especiales, el pie de página se lee antes que el contenido potencialmente ex- $('td.quantity input') .keypress(function(event)
tenso, permitiendo al usuario recibir un resumen de lo que va a venir. if (event.which && (event.which < 48 II
También hemos situado una clase en cada celda de la tabla, identificando qué colum- event.which > 57)) (
event.preventDefault() ;
na de la tabla contiene esa celda. En el capítulo anterior, hemos demostrado algunas for-
}
mas de encontrar celdas en una columna al examinar el índice de la celda dentro de su }) ;
fila. Aquí permitiremos que el código ]avaScript sea más sencillo al hacer que la fuente
HTML sea un poco más compleja. Con una clase identificando la columna de cada celda, Cuando capturamos pulsaciones de teclado para la función autocompletar de nues-
nuestros selectores se pueden hacer más sencillos. tro campo de búsqueda, observamos el evento keyup. Esto nos permite examinar la
mi 8. Formularios con funciones r Aprende jQuery 1.3 mi
propiedad . keyCode del evento que nos ha dicho qué tecla se ha pulsado en el teclado. mismo símbolo. Por el contrario, necesitamos una cadena a pasar al método. t extt ) de
Aquí, observamos el evento keypress en su lugar. Este evento no tiene una propiedad jQuery cuando mostramos el resultado del cálculo, de modo que utilizamos la función
. keyCode, pero en su lugar ofrece la propiedad. which. Esta propiedad informa del String () para crear una nueva utilizando nuestra cantidad total calculada.
carácter ASCII que está representado por la pulsación de teclado que acaba de ocurrir. Cambiar una cantidad ahora actualiza el total automáticamente, como se ve en la
Si la pulsación de teclado resulta en un carácter (es decir, no es una tecla del cursor, figura 8.22. .>
Supr, o alguna otra función de edición) y ese carácter no está en el rango de códigos
ASCII que representan numerales, entonces invocamos. preventDefaul t () en el
evento. Como hemos visto antes, esto impide que el navegador actúa sobre el evento; en
este caso, eso significa que el carácter nunca se inserta en el campo. Ahora, los campos
de cantidad pueden aceptar solamente números.

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~

Figura 8.25. Errores de redondeo.

6% Afortunadamente, la solución para ambos problemas es sencilla. La clase Number de


16 $2 perltem
JavaScript tiene varios métodos para tratar con este tipo de problema, y . toFixed ()
ajusta la cuenta aquí. Este método toma un número de lugares decimales como pará-
~lliliiil¡¡¡¡~
metro, y devuelve una cadena que representa el número en coma flotante redondeado
Figura 8.23. Cálculos totales. a esos muchos lugares decimales:

$('#cart tbody tr') .each(function() (


var price ~ parseFloat($('td.price', this) .text()
.replace(j"["'d.J*j, "));
Tratar con decimales price = isNaN(price) ? o : price;
var quantity ~ parselnt($('td.quantity input', this) .val());
Aunque hemos situado signos de dólar delante de nuestros totales, JavaScript no es var cost = quantity * price¡
consciente de que estamos tratando con valores monetarios. Por lo que se refiere al or- $('td.cost', this) .text('$' + cost.toFixed(2));
denador, son símplemente números, y se deberían mostrar con tal. Esto significa que totalQuantity +~ quantity;
}) ;
si el total termina en un cero detrás del punto decimal, se eliminará, como se ve en la
figura 8.24. Ahora nuestros totales todos se muestran como valores monetarios normales.

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>&nbsp;</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

'src': l •• /imag8s/cross.png', $deleteButton ~ $('<irng />') .attr({


'alt': 'remove from cart', 'width': '16',
'title': 'remove from cart', 'height': '16',
'class': 'clickable' 'sre': '../images/eross.png',
) ; 'alt': 'remove fraro cart',
$ ( "<t.do c y t do ' ) 'title 'remove froro cart',
1:
i~~"

_ 8, Formularios con funciones Aprende jQuery U BD


class
I 'clickable'
1:

}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');

Recibimos el siguiente código [resumido]:

éi'i¡¡¡¡_di'~ <tr> . . . </tr>


e t r- s t.y Lee "display: none 11:> • • . </tr>
e t r s . . . </tr>
Figura 8.32. Actualizar totales.
<tr class="alt"> . . . </tr>

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

(shipping .php) devuelve solamente un fragmento de toda la página, el formulario,


cuando se solicita con el método $. get () .
En la rellamada del método $ . get ( ) eliminamos el nombre en el que se ha hecho
r

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

envío. En realidad, no se trata de un formulario cuando se carga la página, y sin JavaScript


habilitado, se muestra como un cuadro escondido a la derecha del área de contenido, ~
que contiene un vínculo a una página donde el usuario puede editar la información de
Figura 8,36. Cuadro de envío como formulario editable.
envío, como se ve en la figura 8.35.
El usuario ahora puede editar la información de envío sin dejar la página.
El siguiente paso es secuestrar el envío del formulario y publicar los datos editados
4staveScll de vuelta al servidor con jQuery. Empezamos al serializar los datos en el formulario y
almacenarlos en una variable postData. Luego publicamos los datos de vuelta al ser-
Figura 8,35. Acceso a la información de envío. vidor utilizando shipping. php una vez más:
&iI 8. Formularios con funciones Aprende jQuery 1.3 mi
$ (document) .ready(function() { var saveShipping = function() {
$('shipping form') .submit(function() ( var postData = $('#shipping ,input') .serialize();
var postData = $(this) .serialize(); $.post("shipping.php', postData, function(data) {
$.post('shipping.php', postData); $('~shipping form') .remove();
return false¡ $ (data) .appendTo('#shippin~');
}; $('#shipping-name') .click(editShipping),
j); }) ;
return false¡
};
$ ('#shipping-name') .click(editShipping),
}) ;

Elplug-injQuery Form ofrece un método . serialize () más robusto. El plug-in, que


se puede encontrar en http://www.malsup.com/j query/form/, se recomienda El código ha formado un patrón circular de cierto tipo, en el que una función permite
para la mayoría de escenarios de envío de formularios AJAX. la otra al volver a vincular sus respectivos manejadores de evento.

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:
}) ;

return false¡ $ (document) .ready(function() (


};
var stripe = function() {
}) ;
$('#cart tbody tr') .removeClass('alt')
.filter(' :visible:odd') .addClass('alt')¡
Pero, esto no funcionará. La forma en la que lo tenemos configurado ahora, el mane- };
jador de evento . submi t () se vincula al formulario Shipping to tan pronto como se stripe()¡
carga el DOM, pero el formulario no está en el DOM hasta que el usuario hace clic en el
$('#recalculate') .hide();
nombre Shipping to. El evento no se puede vincular a algo que no existe.
Para solucionar este problema, podemos situar el código de creación de formulario $(' .quantity input') .keypress(function(event)
en una función denominada edi tShipping () y el código de envío del formulario o if (event.which ó<ó< (event.which < 48 II

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>&nbsp;</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: ' ••

'alt': 'remove from cart l,


formularios mientras se preserva su utilidad y flexibilidad.
'title': 'remove from cart ¡ 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>&nbsp; </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
, /

.'

Hemos visto algunas formas de ocultar información cuando no se necesita y revelarla


bajo demanda. Algunas veces, sin embargo, queremos mover contenido en y fuera de la

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~

Normalmente cuando utilizamos el método . each ( ) , estamos iterando sobre un


conjunto de elementos posiblemente extenso. Sin embargo, aquí nuestro selector #news-
feed está buscando un ID, por lo que solamente existen dos resultados potenciales. La
función factory podría hacer que un objeto jQuery coincidiera con un único elemento con
el ID news - f eed, o podría no encontrar elementos en la página con ese ID y producir
un objeto jQuery vacío. La llamada . each () se ocupa de ejecutar el código contenido
Figura 9.1. Área de contenido inicial. si, y sólo si, el objeto jQuery no está vacío.
Al principio de nuestro bucle . each ( ) , el contenedor del feed de noticias se vacía
Éste es nuestro apreciado escenario de degradación, en caso de que el usuario no para dejado listo para su nuevo contenido, como se ve en la figura 9.2.
tenga habilitado JavaScript. El contenido con el que trabajaremos procederá de un feed
RSS en su lugar.
RecentNews
El CSS para este <di v » es importante ya que determinará no solamente cuánto de
cada noticia se mostrará cada vez, sino también dónde en la página aparecerán los ele-
mentos de noticias. Junto con la regla de estilo para los elementos de noticias individua-
les, el CSS se parece a esto:
#news-feed {
position: relative¡
height: 200px;
width: 17em;
overflow: hidden; Figura 9.2. Vaciar el contenedor.
}
.headline {
position: absolute;
height: 200px; Recuperar el feed
top: 210px;
overflow: hidden; Para recuperar el feed, utilizaremos el método $ . get ( ) , una de las muchas funciones
AJAX de jQuery para comunicarse con el servidor. Este método, como hemos visto antes,
nos permite operar sobre contenido desde una fuente remota al utilizar un manejador de
lIlIlII 9. Rotativos Aprende jQuery 1.3 mil

é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.

Además de los titulares, deseamos mostrar un poco de información de soporte sobre


- cada artículo. Capturaremos la fecha de publicación y resumen del artículo, y mostra-
remos esto también.
Para más información sobre $. get () y otros métodos AJAX, consulte enun capítulo $ (document) .ready(function() (
anterior. • $('#news-feed') .each(function()
var $container = $(this);
$container.empty() ;
$.get('news/feed.xml'. function(data) {
Ahora, necesitamos combinar las partes de cada elemento en un bloque de código $('rss item', data) .each(function() {
HTML que se pueda utilizar. Podemos utilizar . each () de nuevo para pasar por los var $link = $ ('<a></a>')
elementos en el feed y crear los vínculos de los titulares: .attr('href', $('link', this) .textO)
.text($('title', this) .text(»;
$ (document) .ready(function() ( var $headline = $('<h4></h4>') .append($link);
$('#news-feed') .each(function()
var $container = $(this); var pubDate = new Date(
$container.empty(); $('pubDate'. this).text(»;
$.get('news/feed.xml', function(data) { var pubMonth ~ pubDate.getMonth() + 1;
$('rss item', data) .each(function() { var pubDay = pubDate.getDate();
var $link = $('<a></a>') var pubYear = pubDate.getFul1Year();
.attr('href'. $('link'. this) .text(» var $publication = $('<div></div>')
.text ($ (,title'. this). text O); .addClass('publication-date')
ver $headline = $('<h4></h4>').append($link); .text(pubMonth + '1' + pubDay + '1'
+ pubYear);
$ ('<div></div>') var $summary = $('<div></div>')
.append($headline) .addC1ass('summary')
.appendTo($container); •html ($ ('description'. this). text O) ;
}) ;

}) ; $ (' <div></div>')
}) ; .append($head1ine. $publication. $summary)
}) ; .appendTo($container) ;
});

Obtenemos el texto de los elementos <ti t Le s y <link> de cada elemento, y cons- }) ;


}) ;
truimos el elemento <a> a partir de ellos. Este vínculo luego se sitúa en un elemento
}) ;
<h4 >. Situamos cada elemento de noticias en <di v id= "news - feed" », pero por ahora
estamos omitiendo la clase headl ine en el <di v » contenedor de cada noticia, de La información de fecha en un feed RSS se codifica en formato RFC 822, que incluye
modo que podemos ver más fácilmente nuestro trabajo en progreso, como muestra la información de fecha, hora y zona horaria (por ejemplo, sun , 28 Sep 2 OO8 18 : 01 : 55
figura 9.3. + O O O O). Este formato no es particualrmente agradable a la vista, por lo que utilizamos el
Ea 9. Rotativos Aprende jQuery 1.3 lID
objeto Date incorpordo de JavaScript para producir una representación más compacta También queremos que el primer titular sea visible inmediatamente al cargarse la
de la fecha (como 9/28/2008). página. Para conseguir esto, podemos establecer su propiedad top en O.
La información de resumen es más fácil de recuperar y aplicar formato. Sin embargo,
merece la pena destacar que en nuestro feed de ejemplo, algunas entidades HTML pue- $('div.headli~') .eq(currentHeadline) .css('top', 01;
den existir en la descripción. Para asegurarnos de que no se escapan automáticamene El área del rotativo de la página está ahora en el estado inicial correcto.
por jQuery, necesitamos utilizar el método. htrnl () para insertar la descripción en la
página, en lugar del método. text ().
Con estos nuevos elementos creados, los insertamos en el documento utilizando el Recent Newa

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.

IQueo( MIc!'QIoll and NaJIla


9/211I2008

We llaVe twa pleces of fanIasUc, albeIt


sernndlpltous, news tDday: 60th
MlcroBOft and Nakla are Ialclng 1Ile
maJarstep of ado¡lUng jQuelY as par!
.
' Figura 9.5. Primer titular visible .
011llelr 011I0101
applloatlon
development plaiform. Por último, almacenamos el número total de titulares para uso posterior y definimos
IQueo( UJ 1.6rc2 una variable de tiempo de espera a utilizar para el mecanismo de pausa entre cada ro-
9/1912008
tación.
Hev evervone. j'm oled 10 announce

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.

Configurar el rotativo la función rotar titular


Puesto que el elemento de noticias visible cambiará con el tiempo, necesitamos una Ahora estamos listos para rotar los titulares, fechas y resúmenes. Definiremos una
forma de mantener fácilmente registro de qué elementos son visibles y dónde están. En función para esta tarea de modo que podamos repetir fácilmente la acción cada vez
primer lugar, estableceremos dos variables, una para el titular actualmente visible, y otra
que lo necesitamos. En primer lugar, ocupémonos de actualizar las variables que están
para el titular que acaba de no estar a la vista. Inicialmene, ambos valores serán O. registrando qué titular está activo. El operador módulo (%) nos permitirá fácilmente pasar
var currentHeadline = o, oldHeadline = o; en ciclo por los números de titulares. Podemos añadir 1 al valor currentHeadline
cada vez que nuestra función se invoca, y hacer que este valor módulo tome el valor
A continuación, nos ocuparemos del posicionamiento inicial de los titulares. Recuerde
headlineCount para limitar la variable a números de titular válidos.
que en la hoja de estilo acabamos de establecer la propiedad top de los titulares en la
píxeles más grande que la height de su contenedor; puesto que el contenedor tiene una
propiedad de overflow de hidden, los titulares no se muestran inicialmente. Será de
utilidad más adelante si almacenamos esa propiedad en una variable, de modo que po-
damos mover titulares a esta posición cuando sea necesario. Recuerde que hemos utilizado esta misma técnica para pasar en ciclo por los colores
de fila cuando aplicábamos color alterno a filas de tabla.
var hiddenPosition = $container.height(1 + 10;
mi 9. Rotativos Aprende jQuery 1.3 f1D

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) ;

var headlineRotate = function() { var pubDate = new Date($('pubDate', this) .text());


currentHeadline = (oldHeadline + 1) % headlineCount¡ var pubMonth = pubDate.getMonth() + 1;
$('div.headline') .eq(oldHeadline) .animate( var pubDay = pubDate.getDate();
{topo -hiddenPosition}, 'slow', function() var pubYear = pubDate.getFullYear();
$(this) .css('top', hiddenPosition); var $publication = $('cdiv></div>')
}) ;
.addClass('publication-date')
.eq(currentHeadline) ,anímate (
.'
$(rdiv,headline')
.text(pubMonth + '1' + pubDay + '1' + pubYear);
{top: aL' slow', function () (
pause = setTimeout(headlineRotate, 5000); var $summary = $('cdiv></div>')
}) ;
.addClass(lsurnmary')
oldHeadline = currentHeadline¡ .html($('description', this) .text());
}; $('cdiv></div>')
.addClass('headline')
En ambos casos, estamos animando la propiedad top del elemento de noticias. .append ($headline, $publication, $summary)
Recuerde que los elementos están ocultos porque tienen un valor top de hiddenPo- .appendTo($container) ;
sition (que es un número mayor que la altura del contenedor). Animar esta propie- }) ;

var currentHeadline = o, oldHeadline = o;


dad en O pone el elemento a la vista; animarlo a - hiddenPos i t ion lo mueve fuera de var hiddenPosition = $container.height() + 10;
la vista de nuevo. $('div.headline') .eq(currentHeadline) .css('top', O);
var headlineCount = $(rdiv.headliner) .length¡
var pause¡

var headlineRotate = function() {


currentHeadline = (oldHeadline + 1) % headlineCount;
Recuerdede un capítuloanterior que top es una propiedad de posicionamientoCSS,y $('div.headline') .eq(oldHeadline) .animate (
solamente tiene efectosi la posi tion del elemento es absolute b ré"lati ve. {topo -hiddenPosition}, 'slow', function()
$(this) .css('top', hiddenPosition);
}) ;
En ambos casos, también tenemos una función de rellamada especificada para actuar $('div.headline') .eq(currentHeadlinet.animate(
cuando se completa la animación. Cuando el titular antiguo ha salido completamente (top: o), 'slow', function () (
pause = setTimeout(headlineRotate, 5000);
de la vista, hace que su propiedad top se restablezca en hiddenPosi tion de modo }) ;
que está listo para regresar más tarde. Cuando el titular de noticias ha terminado con oldHeadline = currentHeadline;
su animación, queremos que ponga en cola la siguiente transición; esto se realiza con };
una llamada a lafunción}avaScript setTimeout (), que registra una función a invocar pause = setTimeout(headlineRotate, 5000) i

}) ;
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

bugfIx relsase lo our Google Codo


aoccunl. Whlle there's, sgain, no new
APllntroduced, more !han 30 Issues
clearTimeout(pause) i llaVe been deared and the'¡:odebese
}, function() ( l. growlng moréstable evaty day. The
full changelog IsavaIlable he", K YO"
pause = setTimeout(headlineRotate, 250) i
want lo Ilnd out K (••.]
});

Figura 9.7. Unos titulares encima de otros.


Cuando el ratón entra en el área del titular, el primer manejador . hover () invoca
la función JavaScript clearTimeout () . Esto cancela el cronómetro en progreso, impi-
Estas líneas adicionales mejoran nuestro rotativo de titulares sustancialmente. Pasar
diendo que se invoque headlineRotate (). Cuando el ratón se aleja, el segundo ma-
por encima de forma rápida y repetida ya no hace que los titulares se apilen uno encima
nejador . hover () reinicia el cronómetro, invocando así headl ineRota te () después
de 250 milisegundos de retraso. - >-
de otro. Sin embargo, este comportamiento de usuario todavía nos deja con un proble-
ma molesto: el ritmo del rotativo se pierde con dos o tres animaciones inmediatamente
Este sencillo código funciona bien la mayor parte del tiempo. Sin embargo, si el usua-
una detrás de otra, en lugar de todas ellas espaciadas de forma uniforme en intervalos
rio mueve el ratón por encima y fuera de <di v » rápidamente y de forma repetida, puede
de cinco segundos.
ocurrir un efecto poco deseado. Múltiples titulares estarán en movmiento a la vez, posi-
El problema es que más de un contador se puede activar de forma concurrente si un
cionándose uno encima de otro en el área visible, como muestra la figura 9.7.
usuario retira su ratón del <di v » antes de que el contador existente se complete. Por lo
Desafortunadamente, necesitamos llevar a cabo cierta cirugía para extirpar este cán-
tanto necesitamos poner más de una salvaguardia en su lugar, utilizando nuestra variable
cer. Antes de la función headlineRotate (), introduciremos una variable más:
pause como un indicador de si otra animación es inminente. Para hacer esto, estable-
var rotateInProgress = false¡ cemos la variable en false cuando el tiempo de espera se pasa o cuando se completa.
Ahora podemos comprobar el valor de la varaible para aseguramos de que no existe
Ahora, en la primera línea de nuestra función, podemos comprobar si una rotación tiempo de espera activo antes de situar uno nuevo en su lugar.
estáactualmente en progreso. Solamente si el valor de rotatelnProgress es false
queremos que el código se ejecute de nuevo. Por lo tanto, situamos todo dentro de la var headlineRotate = function()

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() ;

••\1 •••• var fadeHeight = $container.height() / 4;


"";'1\' for (var yPos = O; yPos < fadeHeight; yPos += 2) (
$('<div></div>')
.addClass('fade-slice')
.appendTo($container) ;
}
});
}) ;
Figura 9.8. Mostrar una imagen de carga.
Ahora tenemos 25 cortes (uno para cada segmento de 2 píxeles del area de degrada-
do de 50 píxeles), pero están todos apilados en la parte superior del contenedor. Para
- que nuestro truco funcione, necesitamos que cada uno tenga una posición y opacidad
Podemos fácilmente crear nuevas imágenes GIF animadas para utilizarse como indica- diferente. Podemos utilizar la variable de iteración yPos para determinar de manera
dores de carga AJAX al utilizar el servicio en http: / / aj axload. info/. matemática las propiedades opaci ty y top de cada elemento:
$ (document) .ready(function() (
$('#news-feed') .each(function()
= $(this);
Efecto degradado var $container
$container.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

40 0.80 190 '~lt': Loading. Please wait.'


,
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 ••••••••••••.•
" •••••••••.••••••••••.•
""' •••••••••

another earty verston of 1.6, a


upgmde lo 1.6m2 lo hlghly var $surnmary = $('<div></div>')
recommended. ThIs lo also (•••]
.addClass('sumrnaryl)
.htrnl($('description', this) .text());
1Que!y COnfll •••••••••2008 Agencia $('<div></div>')
8/3112008 .addClass('headline')
.append ($headline, $publication, $surnrnary)
The soId-ootlQuery Conference 2008.
betng held In BosIon a11he MIT S!ata .appendTo($container) ;
Center 00 5e¡¡temb<lr 281!1.18 nelH1y }) ;
upon USo WIth 13seoslons b<llng
¡l",,,,_,,~ •.•" th"" Ir<; ,,,"'. h,"' ••.• ..,.,. ,.,~",,1I
var currentHeadline = O, oldHeadline = O;
Figura 9.9. Efecto de transparente a opaco.
var hiddenPosition = $container.height{) + 10;
$('div.headline') .eq(currentHeadline) .css('top', O);
var headlineCount = $('div.headline') .length¡
El código terminado var pause¡
var rotateInProgress = false;
Nuestro primer rotativo está ahora completo. Los elementos de noticias se van ahora var headlineRotate = function()
a buscar desde un servidor remoto, se formatean, yse animan según lo planificado, y if (!rotateInProgress) {
cuentan con un bonito estilo: rotateInProgress = true¡
pause = false¡
$ (docurnent) .ready(function() ( currentHeadline = (oldHeadline + 1)
$('#news-feed') .each(function() % headlineCount¡
var $container = $(this); $('div.headline') .eq(oldHeadline) .anirnate(
$container.ernpty() ; {top: -hiddenPosition}, 'slow', function()
$(this) .css('top', hiddenPosition);
var fadeHeight = $container.height() / 4; }l;
for (var yPos = O; yPos < fadeHeight; yPos += 2) ( $('div.headline') .eq(currentHeadline) .animate(
$('<div></div>') .css({ {top: O}, "a Low! , function Il (
opacity: yPos I fadeHeight, rotateInProgress = false¡
top: $container.height() - fadeHeight + yPos if (!pause) (
}) .addClass('fade-slice') .appendTo($container); pause = setTimeout(headlineRotate, 5000) ¡
mi 9. Rotativos
Aprende jQuery 1.3 &a
});
cimg src="images/covers/medium/1847190871.jpgll
oldHeadline = currentHeadline¡
width="120" height="148"
alt=IICommunity Server Quickly" />
};
<sp,h clas9="price">$3S.99c/span>
if (!pause)
<la>
pause = setTimeout(headlineRotate, 5000);
<a href=" images/eovers/large/184 7190901. jpg"
title="Deep Inside osCornmerce: The Cookbook">
cimg src=lIimages/covers/mediurn/1847190901.jpgll
$eontainer.hover(funetion()
width="120" height="148 1t

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>

en respuesta a la interacción del usuario. <a href=" images/eovers/large/184 7191002. jpg"


title="Google Web Toolkit GWT Java AJAX Prograrnming">
dmg src» "images/eovers/medium/184 7191002. jpg"
width=1I120" height="148"
alt="Google Web Toolkit GWT Java AJAX Programming" />
cspan class=rtprice">$40.49c/span>
Un mecanismo alternativo para desplazarse porun conjunto de imágenes se implementa
<la>
por el plug-injCarousel parajQuery. Además, el plug-in altamente flexible SerialScroll <a href="images/eovers/large/1847192386.jpg"
permite desplazar cualquier tipo de contenido. Aunque no idéntico al resultado que title=tlBuilding Websites with Joomla! 1.5 Beta 1">

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>

Configurar la página </div>


</div>

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:
,,,.--

&!I 9. Rotativos , / Aprende jQuery 1.3 BiI


#featured-books {
el camino del posicionamiento que deseamos realizar para animar las portadas. Por lo
position: relative¡
background, #ddd; tanto, nuestra primera prioridad será anular algunos estilos:
width, 440px; r
$ (document) .ready(function()
height, 186px;
var spacing = 140;
overflow: scroll¡
margin: 1em auto;
$('#featured-books') .css({
padding, O;
'widthr: spacing * 3,
text-align: center;
r height': I166px 1 I
z-index: 2;
roverflow': hidden
I I

}
} ) .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;

La variable spac ing va a ser de utilidad en muchos cálculos. Representa la anchura


Estos estilos provocan un poco de discusión. El elemento más exterior necesita de una de las imágenes de portada, más el relleno a cada lado. La width del elemento
tener una propiedad z - index mayor que el que está dentro; esto permite que Internet contedor ahora se puede establecer en exactamente lo que es necesario para contener
Explorer oculte la parte del elemento interior que se extiende más allá de su contene- tres de las imágenes de portada, ya que no necesitamos espacio para la barra de despla-
dor. Establecemos la anchura del elemento exterior en 44 Opx, que acomoda tres imá- zamiento. En su lugar, cambiamos la propiedad overflow a hidden, y adiós barra de
genes, el margen de 1 Opx alrededor de cada una, y un 2 Opx adicional para la barra de desplazamiento. Las imágenes de portada se posicionan todas de forma absoluta, y em-
desplazamiento. piezan con una coordenada izquierda de 10 OO.Esto las sitúa fuera del área visible. Luego
Con estos estilos en su lugar, las imágenes se pueden ver utilizando una barra de movemos las tres primeras portadas en posición, una de cada vez. La variable $covers
desplazamiento estándar, como muestra la figura 9.10. que alberga todos los elementos de anda también será de utilidad más adelante.
Ahora las tres primeras portadas son visibles, sin ningún mecanismo de desplaza-
miento disponible, como muestra la figura 9.11.

Figura 9.10. Desplazarse por las imágenes con barra de desplazamiento estándar.
Figura 9.11. Sólo tres portadas sin desplazamiento.

Revisar los estilos con JavaScript


Mover las imágenes cuando se hace dic
Ahora que hemos pasado a hacer que la galería de imágenes sea de utilidad sin
JavaScript, necesitamos deshacer algunas de las sutilezas. La barra de desplazamiento Ahora, necesitamos añadir código para responder a un clicen alguna de las imágenes
será redundante cuando implementemos nuestro propio mecanismo de desplazamien- finales, y reordenar las portadas según sea necesario. Cuando se hace clic en la portada
to, y el diseño automático de la portada utilizando la propiedad float se pondrá en izquierda, esto significa que el usuario quiere ver más imágenes a la izquierda, que a su
&El 9. Rotativos Aprende jQuery 1.3 mi
vez significa que necesitamos mover las portadas a la derecha. De forma similar, cuan- La nueva función setUpCovers () incorpora el código de posicionamiento de ima-
do se hace die en la portada derecha tendremos que mover las portadas a la izquierda. gen que escribimos anteriormente. Al encapsular esto en una función, podemos repetir
Queremos que el carrusel gire, de modo que cuando las imágenes se van por el lado iz- el posícionamiento de la imagen después de que se hayan reordenado los elementos;
quierdo, se anexan a la derecha. Para empezar, simplemente cambiaremos las posiciones esto será importante, como pronto veremos.
de las imágenes sin animación. En nuestro ejemplo, hay seis imágenes en total (que JavaScript hará referencia con
(document) .ready(function() °
los números a 5), y los números 0, 1, Y 2 son visibles. Cuando se hace clic en imagen
var spacing = 140; #0, queremos mover todas las imágenes a la derecha en una posición. Primero move-
mos imagen #2 fuera del área visible (con . e s s ( , 1e f t ' , 10 O O ) ), ya que no queremos
$('#featured-books') .css({
que esté visible después del movimiento. Luego, movemos la imagen al final de la línea
'width': spacing * 3,
'height': '166px',
,.. (#5) al principio de la cola (utilizando .prependTo ( ) ). Esto reordena todas las imáge-
'overflow': 'hidden' nes de modo que cuando se invoca setUpCovers () de nuevo, el #5 anterior es ahora
j) . f ind (,.covers a') .css ({ #O,#Ose ha convertido en #1, y #1 se ha convertido en #2. El código de posicionamiento
'float': 'none',
existente en esta función es por lo tanto suficiente para mover las portadas a sus nuevas
'position': 'absolute',
,left': 1000
ubicaciones, como muestra la figura 9.12.
j);

var setUpCovers = functian() {


var $covers = $('#featured-hooks .covers a'); "

$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'); }) ;

$covers. eq (1) .animate ({ ,left ': O), 'fast');


En el CSS para la página, establecemos que el z - index de estos controles sea mayor $covers.eq(2) .animate(('left': spacing}, 'fast');
que el de las imágenes, y luego los posicionamos de forma absoluta de modo que pue- $covers.eq(3)
.css(Ileft', spacing * 3)
dan solapar las portadas: .animate (( 'left': spacing * 2), 'fast');
event.preventDefault() ;
#featured-books .control
}).hover(function() {
position: absolute;
$rightRollover.appendTo(this).show();
z-index: 3¡
}, function() {
left: o;
$rightRollover.hide();
topo o;
}) ;
// Imagen central
$covers.eq(l)
Los íconos rollover comparten todos la misma clase control, de modo que uno .css('left', spacing)
podría estar tentado a situar el estilo opaci ty en la hoja de estilo CSS. Sin embargo, la .hover(function() {
opacidad no se gestiona de forma consistente entre navegadores; en Internet Explorer, $enlargeRollover.appendTo(this) .show();
}, function () {
la sintaxis para 60 por ciento opacidad es filter: alpha (opacity=60) . En lugar $enlargeRollover.hide();
de lidiar con estas distinciones, establecemos el estilo opacidad utilizando el método }) ;
. css () de jQuery, lo que elimina estas inconsistencias de navegador. };
DI 9. Rotativos Aprende jQuery 1.3 lImI

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
...

0Btsbase: PractIcal DesIgn


T1ps and TechnlQUeS lOu!rt.com alC!Btdnkln
° ""'pal:<>oatIng-,
Forums. PortaIa, and
•••••••••
CommunJtyWebsltes We'Ve just puahed out a bl'Bnd new
sIte redesign (fct jQuery.com 8IId aU
locem ipsum dolor lit amet. It!saub-&nea). Thls has b8an a long
ccnsectetur adIpISJclng ent, lime yomlng 8tId 11.feele: great 10 get It
sed do elusmod tempor OIJI the door. New Homopeoe E8IIIy
Inckflduol lit labore et doIore tbemosloontentiouspartollhe
m9\V18 aBqua. ut enlm ad tMeslgrt l but absolutely the moet
mlnIm venlam, qu/s noatrud ."....1d1Ing. JQuer¡ ••• Iong baen
exeroItetlonullatnoolaborls
nl81 ut allquIp WI B8 convnodo -by """---"""( ..<
oonsequa1.OultautelnJre
dolor In ntpmhendorit In
voIuptateveHt88SeclllUn
dolara eu 1uQlaI nuIIa pariaU.
""""""" ""'oa:oocot
wpldatal non prokSent, stnt l.oremlpst.rnOOloteHa:
tl wIpa quI otnola deserunl dofota magna BIIqua. lit enIm lid rnInIm venlam; q.M nostrucI tOOiIdbibUítuaamco /8bOiIsnlilutalqulp ex
moIIlt anlm Id _1 Iaborum. ea oommodo conseqUlI. DulI sute ln.Ite dolor In repn;Ihendertt ti vaIuptate veHt esse áIIum dOOre eu
l'uglat nUlIa partatur. EXceptour aInt oooaocet cupldatat non ptOIdent, sunt In CUlpa quI o1'fIcta deserunt
moIItan/mldestl8borum.

Figura 9.18. Portada ampliada y centrada.

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') ,

// Imagen central, ampliar portada.


$covers.eq(l) Puesto que el precio será parcialmente transparente, un contraste alto entre el color
.css('left', spacing) de fuente y el fondo funcionará mejor:
.click(function(event) (
$enlargedCover.attr('src', $(this) .attr('href'» .enlarged-price (
.css ({ background-color, #373c40,
"Lef t t : ($('body') .width() - 360) / 2, color, #fff,
'top' : 100, width, 80px,
'width': 360, padding, Spx,
t height': 444 font-size: 18px;
}) font-weight, bold,
·show () text-align, right,
·one ('click', function () ( position: absolute;
$closeButton.unbind('click') .hide(); z-index: 6;
$enlargedCover.fadeOut() ,
]l;
$closeButton Antes de poder mostrar la etiqueta del precio, necesitamos completarlo con la infor-
·css ({ mación de precio real del HTML. Dentro del manejador eliek de la portada del centro,
'left', ($('body') .width() - 360) / 2, la palabra clave this hace referencia al elemento de vínculo. Puesto que el precio está
'top' , 100
}) en un elemento «apan» dentro del vínculo, obtener el texto es sencillo:
·show ()
.click(function() {
var price = $(this) .find(' .price') .text();

$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:

Query Fake Bookstore


Figura 9.20. Ajustar el precio en la portada. . FeeI free ID broWselllrough lile sita and pretend ID buy books.

Nuestras coordenadas de destino ahora se pueden calcular a partir de éstas bastante


fácilmente. Recopilaremos éstas en un objeto similar. o

var endPos = {};


endPos.width = startPos.width * 3;
endPos.height = startPos.height * 3;
endPos.top = 100;
endPos.left = ($('body') .width() - endPos.width) / 2;

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

.one('click', function() { Figura 9.21. Transición. Paso 1.


IJEI 9. Rotativos AprendejQuery 1.3 ED

Query Fake Bookstore

,/

met, consectetur adlplslclng ell1, sed do elusmod tempor lneldldunt ut la


I enlm ad mlnlm ventam. quls nostrud exercllatlon uUamco !abono nlsl utl
Figura 9.22. Transición. Paso 2. Figura 9.24. Transición. Paso 4.

Aplazar las animaciones hasta que la imagen se carga


Nuestra animación funciona bien, pero depende de una conexión rápida al sitio. Si la
portada ampliada tarda algo de tiempo en cargarse, entonces los primeros momentos de
la animación podrían mostrar la X roja indicando una imagen rota, o seguir mostrando
la imagen anterior. Podemos hacer la transición un poco más elegante al esperar hasta
que la imagen se ha cargado por completo antes de iniciar la animación:
$enlargedCover.attr('src'. $(this) .attr('href'))
.css(startPos)
.show ();

var perfor.mAnimation = function()


$enlargedCover.animate(endPos, 'normal', function()
$enlargedCover.one('click', function() (
$closeButton.unbind('click') .hide();
$priceBadge.hide() ;
$enlargedCover.fadeOut() ;
}) ;

$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();

Yal principio de la función performAnimation (), cuando sabemos que la imagen


se ha cargado, eliminamos el indicador de la vista:

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

El código terminado r .css('left', -spacing)


·animate ({ ,left ': o}, 'fast', function
$(this) .prependTo('#featured-books
() (
.covers');
Este código representa solamente una pequeña fracción de lo que se puede hacer en setUpCovers() ;
la Web con imagen animada y rotativos de texto. Todo junto, el código es: });
event.preventDefault (); C>,

$ (document) . ready(function() ( }) .hover (function () (


var spacing = 140¡ $leftRollover.appendTo(this) .show();
function createControl(src) }, function() (
return $ (' <irng/>') $leftRollover.hide() ;
.attr{'src', src) }) ;
// Imagen derecha; desplazarse a izquierda (para ver imágenes a la derecha) .
.addClass('control')
.css('opacity', 0.6) $covers.eq(2)
.ess ('display', 'nane') i
.css('left', spacing * 2)
.click(function(event) (
$covers. eq (O)
var $leftRollover = createControl('images/left.gif'); ·animate ({ 'left': -spacing}, 'fast', function ()
var $rightRollover = createControl('images/right.gif'); $(this) .appendTo('#featured-books .covers');
var $enlargeRollover = createControl('images/enlarge.gif'); setUpCovers();
var $enlargedCover = $('<img/>') });
.addClass('enlarged') , $covers. eq (1) .animate ({ ,left ': O}, 'fast');
.hide() • $covers.eq(2) .animate({ 'left': spacing}, 'fast');
·appendTo (,body ') ; $covers.eq(3)
var $closeButton = createControl('images/close.gif') .css('left', spacing * 3)
.addClass('enlarged-control') ·animate ({ ,left': spacing • 2}, 'fast');
·appendTo (,body ') ; event.preventDefault() ;
var $priceBadge = $('<div/>') }) .hover(function() (
.addClass('enlarged-price') $rightRollover. appendTo (this) .show() ;
.css('opacity',O.6) }, function() (
·ess ( 'display', 'none I )
$rightRollover.hide() ;
.appendTo('body') ; });
var $waitThrobber = $('<img/>') // Imagen central; ampliar portada .
.attr('src', 'images/wait.gif') $covers.eq(l)
.addClass('control') .css('left', spacing)
.css('z-index', 4) .click(function(event) (
.hide() ; $waitThrobber. appendTo (this) .show() ;
$ ('#featured-books') .css({ var price = $(this) .find(' .price') .text();
'width': spacing * 3, var startPos = $(this) .offset();
'height': '166px', startPos.width = $(this) .width();
'overflow': 'hidden' startPos.height = $(this) .height();
». find (,.covers a') .css (( var endPos = {};
'float': 'none', endPos.width = startPos.width • 3;
'position': 'absolute', endPos.height = startPos.height • 3;
'left': 1000 endPos.top = 100;
}) ; endPos.left = ($('body') .width() - endPos.width) / 2;
var setUpCovers = function() { $enlargedCover.attr('src', $(this) .attr('href'))
var $covers = $('#featured-books .CQvera a') i .css(startPos)
$covers.unbind('click mouseenter mouseleave') ; .show() ;
// Imagen izquierda; desplazarse a derecha (para ver imágenes a la izquierda). var performAnimation = function()
$covers.eq(O) $waitThrobber.hide() ;
.css (,left', O) $enlargedCover ,animate (endPos, 'normal·,
.click(function(event) ( function () (
$covers.eq(O) .animate({'left': spacing} , fast ');
I $enlargedCover.one('click', function() (
$covers. eq (1) .animate ({ 'left' : spacing * 2}, 'fast'); $closeButton.unbind('click') .hide();
$covers.eq(2) .animate({'left': spacing * 3}, 'fast'); $priceBadge.hide() ;
$covers.eq($covers.length - 1) $enlargedCover.fadeOut();
~~~l¡'"

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.. ' ,.'¡',,"'

tr: . .' \ _...


(¡J'¡ \
o 1')
,
'0"
~
@ \\
@ ,~ ..~. "1'f.,""@'
<>
~
~® .... .. ~-- ~..,
o ~...
,---~'
IJ!.
"
/
10? "
'l;;f'" ,&,. ~'" n r>~l j<"' ":!.I @1@- "SI
®
,
, r.;
,......
-,;,:.'~ ,~, O"I!!J .®,..,
-.~ r@
~,.~....;',
~·'V~._'
~.
' .. _••

___ '. "'. . -r-,


O ~).
>$l'
.-'--. . . .•.......
----:-~ (' (9)"

- " .
<-, "\
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

10 que ha quedado relativamente sin explorar es la ampliabilidad de jQuery. Tan potente


como su librería base, su elegante arquitectura de plug-in ha permitido a desarrol1adores
ampliar jQuery, haciendo que sea una librería aún más rica en características.
La creciente comunidad jQuery ha creado cientos de plug-ins, desde pequeños se-

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".

Encontrar plug-ins y ayuda


El sitio Web jQuery proporciona un amplio repositorio de plug-ins disponibles en
http://plugins . j query. com/, con características como valoraciones de usuario,
versiones, e informes de errores. Este repositorio de plug-ins es también un estupendo
lugar para empezar cuando se busca documentación. Cada plug-in listado en el repositorio
se puede descargar como un archivo. z ip, y muchos de ellos también tienen vínculos a
demos, código de ejemplo y tutoriales para ayudamos a empezar.
lEfI 10. Utilizar plug-ins r Aprende jQuery 1.3 1m
Incluso se pueden encontrar más plug-ins en repositorios de código general como
http://github . com/ y en los weblogs de los desarrolladores de plug-ins. El plug-in Forrn
Si no puede encontrar las respuestas a todas nuestras preguntas en el repositorio de
plug-ins, el sitio Web del autor, y los comentarios del plug-in, siempre podemos recu- El plug-in Form es un extraordinario ejemplo de un script que convierte una tarea di-
rrir al jQuery Google Group enhttp://groups . google. com/group/j query-en/. fícil Ycompleja en algo terriblemente sencillo. El archivo plug-in, junto con la documen-
Muchos de los autores de plug-ins son colaboradores frecuentes de la lista, y siempre tación detallada, se encuentra disponible en http://malsup/com/jquery/form/.
están deseosos de ayudar con cualquier problema que puedan encontrar los nuevos Como base del plug-in se encuentra el método. aj axForm () . Convertir un formu-
usuarios. lario convencional en un formulario AJAX requiere una sencilla línea de código:
$ (document) .ready(function()
r' $ (/ #myForm/) .ajaxForm ();
});
Cómo utilizar un plug-in
Este ejemplo preparará < form id=" myForm" > para enviarse sin tener que refrescar
Utilizar un plug-in jQuery es muy sencillo. El primer paso es incluirlo en el «head» del la página actuaL Esta característica es en sí misma bastante atractiva, pero el verdadero
documento, asegurándose de que aparece detrás del archivo fuente jQuery principal: - potencial viene con el mapa de opciones que pasamos al método. Por ejemplo, el siguiente
código invoca. aj axForm () con las opciones target, beforeSubmi t, y success:
<head>
<meta http-equiv="Content-Typel! $ (document) .ready(function() {
content=lItext/html¡ charset=utf-Sn/> function va1idateForm() {
<script src="jquery.js" type=l1text/javascriptll><!script> // el código de validación de formulario iría aquí
<script src="jquery.plugin.jsll // podemos devolver false para abortar el envío
type=lItext/javascriptll></script> };
<script src=!tcustorn.jsll type=lltext/javascript"></script> $ (/#test-form/) .ajaxForm({
<title>Example<!title> target: /#log/,
</head> beforeSubmit: validateForrn,
success: function() {
Después de ello, es cuestión de incluir un archivo JavaScript personalizado en el que a1ert(/Thanks for your commenti/);

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, ,)"",
,,'\"

IEI 10. Utilizar plug-ins Aprende jQuery 1.3 ID


"'r' '.
$ (document) .ready(function() o bien, se puede realizar con una opción añadida a un segundo mapa de opci~nes:
$ (/#mydiv/) .animate({
color, I#fff/. $ (document) .ready(function() {
backgroundColor, 1#0001 $ (/#mydiv/) .animate({
}./slow/); color, .1#fff/.
j); backgroundColor, 1#0001
}. {
duration: /slow/,
A un poco más de la mitad de la animación, el <di v » se parece a lo que muestra la
easing: /easelnQuart/
figura 10.1. }) ;
}) ;

u
Demostraciones del conjunto completo de funciones se encuentran disponibles en
http://gsgd.co.uk/sandbox/jquery/easing/.

Efectos adicionales

Figura 10.1. Animación en progreso.


Los archivos de efectos individuales añaden varias transiciones, que se pueden im-
plementar con el método. effect () y algunas de las cuáles amplían la funcionalidad
El elemento se muestra exactamente como debería, con el texto en su camino a con- de los métodos de jQuery . show (), . hide (), y . toggle () también. Por ejemplo, el
vertirse en blanco y el color de fondo acercándose a negro. efecto explode, que oculta elementos al ampliarlos en un número dado de piezas, se
puede conseguir con el método. effect () :
$ (document) .ready(function() (
Animaciones de clase $U#explode/) .effectUexplode/. {pieces: 16}. 800);
}) ;
Los tres métodos de clase con los que hemos trabajado en capítulos anteriores
(. addClass (), . removeClass (), y . toggleClass ()) ahora toman un segun-
o bien, se puede conseguir con el método . hide ( ) :
do argumento opcional para la duración de la animación. Podemos escribirlos como $ (document) .ready(function() {
$ (/#explode/) .hide(/explode/. {pieces: 16}. 800);
.addClass {/highlight/, /fast/} o .removeClass {/highlight/, /slow/} }) ;
o . toggleClass {/highlight, 1000}.
De cualquier forma, el efecto oculta un cuadrado que empieza así:

Aceleración y desaceleración avanzada

Las funciones avanzadas de aceleración y desaceleración varían l,avelocidad y dis-


tancia a las que ocurren las transiciones en varios puntos en el transcurso. Por ejemplo,
la función easelnQuart termina una animación a cuatro veces la velocidad a la que
empezó. Podemos especificar una función personalizada de aceleración y desaceleración
en cualquiera de los métodos principales de animación jQuery o los métodos de efec-
[J
Figura 10.2. Ocultar un elemento.

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"!

mil 10. Utilizar plug-ins Aprende jQuery 1.3 ID

••••
••••
••••
••••
Figura 10.3. Animación en progreso.

Figura 10.5. Arrastrar y soltar elementos.

Podemos mejorar la interacción de usuario al añadir opciones al método. sorta-


" ble () . Aunque este método tiene cerca de treinta opciones disponibles, utilizaremos
unas pocas para nuestro ejemplo:
$ (document) .ready(function() (
$ (/#sort-container!) .sortable ({
opacity: . s,
cursar: /move/,
axis, !y!
});

Figura 10.4. Lista no ordenada. }) ;

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();
}) ;

Esta sencilla línea dentro de $ (document) . ready () nos permite arrastrar


cada elemento y soltarlo en una posición diferente dentro de la lista, como muestra la
figura 10.5. Figura 10.6. Añadir opciones al método .sortablei),
ElI 10. Utilizar plug-ins Aprende jQuery 1.3 mi
-

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 ,

helper, al aplicar estilos a la clase en nuestra hoja de estilo.


Para más información sobre todos los componentes de interacción jQuery UT, visite
http://docs.jquery.com/UI#Interaction.

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.

<div id="dlg">My Dialog</div>


Ahora, las diferentes áreas están claramente indicadas, y el cursor del ratón cambia
Como era de esperar, este <di v » parece bastante sencillo;un sencillobloque de texto, para proporcionar incluso más feedback visual en las partes del cuadro de diálogo ha-
como muestra la figura 10.7. bilitadas para arrastrar y cambiar de tamaño. Como con los otros métodos jQuery DI,
. dialog () viene con un número de opciones. Algunas de las opciones afectan la apa-
riencia del cuadro de diálogo mientras que otras permiten que los eventos se activen:
I My rnalog I
$ (document) .ready(function() (
var $dlg = $ (/#dlg/) ;
Figura 10.7. Un sencillo bloque de texto.
var dlgText = $dlg.text();
$dlg.dialog({
Podemos invocar el cuadro de diálogo básico en nuestro archivo J avaScript tan pron- autoOpen: false t

to como el DOM está listo. - ,. title: dlgText,


open: function()
$ (document) .ready(function() $dlg.empty() ;
$(/#dlg/) .dialog(); ).
j); buttons: (
/add message/: function() (
El texto ahora se encuentra en un cuadro de diálogo, como muestra la figura 10.8. $dlg.append(/<p>Inserted message</p>/) ;
).
Este cuadro de diálogo se puede cambiar de tamaño al hacer clic en uno de su bordes
/erase messages/: function()
y arrastrar. Se puede mover al hacer clic en cualquier lado dentro del área superior del $(/p/, $dlg) .remove();
cuadro de diálogo, por debajo del borde superior. Y, se puede cerrar al hacer clic en el
vínculo X en la esquina superior derecha. )
}) ;
Podemos obviamente hacerlo mejor aplicando estilo, sin embargo. Aunque jQuery
$ (/#do-dialog/) .click(function()
DI proporciona un conjunto mínimo de estilos para asegurarse de que los widgets son $dlg.dialog(/open/) ;
funcionales, nos deja a nosotros las decisiones sobre la apariencia y comportamiento. En j);
la figura 10.9 tiene un cuadro de diálogo con un tema predeterminado aplicado. }) ;
lIi!I 10. Utilizar plug-ins Aprende jQuery 1.3 IDI
Hemos configurado el cuadro de diálogo para que esté oculto inicialmente y se abra
cuando el usuario haga clic en un botón con id= "do-dialog". También hemos movido
el contenido de texto inicial del cuadro de diálogo al área de título y añadido dos botones,
uno con add message (añadir mensaje) como su texto y otro con erase messages (eliminar
mensajes) como su texto. Cada botón tiene una función asociada para anexar o eliminar SUdor

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>'

o H.yl Sampkt ut-state-highlight ayle.

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 /'

Multiple Cities ~ocal): ,oolor SI'


XJUZij",J&&lL
muerer
Deerfteld

Deflanee .¡;
e Lorem ipsum dolor sil amel, oonsecteluer adipiscing slil, sed diam nonumm
dolars mágna aliquam eral volulp

Figura 10.13. Plug-inAutocomplete. Figura 10.15. Plug-in Jeditable.

Validation .' Masked input


http://bassistance.de/jquery-plugins/jquery-plugin-validation http://digitalbush.com/projects/masked-input-plugin/
http://plugins.jquery_com/project/validate http://plugins.jquery.com/project/maskedinput
Otro plug-in de J6m Zaefferer, Validation es una herramienta enormemente flexi- El plug-in Masked Input (véase la figura 10.16) ofrece una forma para que los usua-
ble para validar entrada de datos de formulario basado en un amplio rango de criterios rios incorporen datos de forma más fácil, como fechas, números de teléfono, o números
(véase figura 10.14). de la seguridad social, en un determinado formato. Sitúa automátieamente ciertos ca-
racteres (como una barra inclinada para las fechas) en el campo mientras permite que
Validating a complete fonn se incorpore solamente un determinado conjunto de caracteres, según se determina en
Firstname i i Please'entér your fimname las opciones del plug-in.

Lastname :1 i Plea,seenter }'Our lastname


The following example is a dernonstratlon from the usage tab.

Username ¡ ¡ Plé¡lsé en.ter a usemame


Date W04¡-----1 99199/9999
Password ! i Please provide a passworc!
" Phone L_. . J (999) 999-9999
Confirm i i Pleasé prov/de a password Tax ID I ] 99-9999999
passwotd SSN c= I 999-99-9999
Email i ' Please enter a va/id email address Product Key L.....m_=:=J a'-999-a999
!!Iease agree to e Pleil$e ,!(;(:ept our policy
Eye Script [ ::::J -9.99 -9.99 999

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

ID 10. Utilizar plug-ins Aprende jQuery 1.3 lB

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\'"

Figura 10.17. Plug-in Tablesorter.


Figura 10.19. Plug-in Flexigrid.

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

Jcrop » Thumbnail Demo

"

Vcry fancy captlon te thls Image

Figura 10.22. Plug-in FancyBox.


"

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
í

AJAX en un cuadro de diálogo modal híbrido (véase la figura 10.23).

Figura 10.21. Plug-in Magnify.

jQuery lightbox y cuadros de diálogo modales


Uno de nuestros ejemplos en el capítulo anterior mostraba cómo superponer informa-
ción detallada sobre una página sin el uso de una ventana emergente, una característica
a menudo denominada Lightbox.
Los siguientes plug-ins ayudan en la creación de esas superposiciones.

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

11I1 Germ any.,


11I1 Sweden ¡

¡
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

Figura 10.26. Plug-in Flot.


~ i'iM%ffi5 Ci
view (alert), View (<otloe)
ase in which :. "n'!';··.~r,~fev~r, '(.1/'~rc~~~Ü~ffd~"!!11 Ml ¿¡;.~cssis
bllows a desi nC!"\l,:"p!ó~~!\a.r!>~)~~'f'hwj C.l> m overfays, Sparklines
,edoI hope lo lVi'l>urotng)Or¡:~IMkitl!r.t""lIfI<t,hMm~ul:fM",.bg li··é'~ s. Interesf<
.tJ<>tt~Il1.of,l>
.lli lUins t. +he food. only h••.dwoods should be http://omnipotent.net/jquery.sparkline/
used for ...,king <D:Idgrilling. sueh <0$ook. peccn, http://plugins.jquery.com/project/sparklines
a (dialog) • iS'¡¡rM;.y-:~~.~~.'lí\'Jfr./¡¡W~?ií~me.squit e,
Nombrado así por un concepto popularizado por el experto de visualización de datos
b (alert) • aj ~ rtrlit!fing on t¡'" type of mea! b&ingcooked.
Al Edward Tufte, el plug-in Sparklines (véase la figura 10.27)genera gráficas en línea pe-
e (alert) • onShow. onHide callbacks.
queñas y sencillas. Como Flot, Sparklines utiliza el elemento <canvas> para mostrar
las gráficas, pero la conversión para Internet Explorer se realiza dentro del plug-in en
Figura 10.25. Plug-in jqModal.
lugar de basarse en Excanvas.

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: .-'-'-

CI;Imposile Inline ~ ('

In~ne wtth normal range ~

CompO$iI8 bar~

Discreta 111111111111111,11
Discreta with thrashold 1!IIIIUI

Customlze slzeand cpIOUt$~


Triste. chatte ••..•••••....••.
(Ihlnkgames won. loS! or drawn) .'
Tri$late ch$rt tlaIng a (XlIoUrmap: •• _••••• -_••

,Pis Charla 8$.


Bulle! !;haJ'IS

Figura 10.27. Plug-in Sparklines.

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
/

"

Los plug-ins de terceros disponibles proporcionan un grupo de opciones para mejorar


nuestra experiencia de codificación, pero algunas veces necesitamos llegar un poco más

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.

Añadir nuevas funciones globales


Algunas de las posibilidades incorporadas de jQuery se proporcionan a través de lo
que hemos denominado funciones globales. Como hemos visto, éstos son en realidad
métodos del objeto j Query, pero en sentido práctico, son funciones dentro de un espa-
cio de nombres j Query. Un ejemplo de esta técnica es la función $ . aj ax ( ) . Todo lo
que $ . aj ax () realiza se podría conseguir con una función global normal denominada
simplemente aj ax () , pero este enfoque nos dejaría abiertos a conflictos de nombre de
función. Al situar la función dentro del espacio de nombre j Query, solamente nos te-
nemos que preocupar por conflictos con otros métodos jQuery.
II!II 11. Desarrollar plug-ins
r
I Aprende jQuery 1.3 I!!I
Para añadir una función al espacio de nombre j Query, podemos simplemente asig- jQuery.myPlugin = (
nar la nueva función como una propiedad del objeto j Query: functionOne: function() (
alert(/This is a test. This is only a test./);
jQuery.globalFunction = function() ( }, f

alert(/This is a test. This is only a test./); functionTwo: function(param) (


}; alert(/The parameter is "/ + pai:am + /"./);

Ahora en cualquier código que utiliza este plug-ín, podemos escribir: };


jQuery.globalFunction() ;
Este patrón crea esencialmente otro espacio de nombre para nuestras funciones
También podemos utilizar el alias $ y escribir: globales, denominado jQuery. rnyPlugin. Aunque informalmente llamamos a estas
$.globalFunction() ; ,...funciones "globales", ahora son métodos del objeto rnyPlugin, una propiedad del ob-
jeto global j Query. Por lo tanto tenemos que incluir el nombre del plug-in en nuestras
Esto funcionará como una llamada de función básica, y se mostrará la alerta. llamadas de función:
$.myPlugin.functionOne() ;
Añadir múltiples funciones $.myPlugin. functionTwo (/test/) ;

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.

Esto produce los mismos resultados.


Corremos el riesgo, sin embargo, de un tipo diferente de polución de espacio de
Crear un método de utilidad
nombre. Aunque estamos protegidos de la mayoría de nombres de variable y función
JavaScript al utilizar el espacio de nombre j Query, podríamos todavía tener un conflicto Muchas de las funciones globales proporcionadas por la librería principal jQuery
con nombres de función definidos en otros plug-ins jQuery. Para evitar esto, es mejor son métodos de utilidad; es decir, proporcionan métodos abreviados para tareas que se
encapsular todas las funciones globales para un plug-in en un objeto: necesitan frecuentemente, pero que no son difíciles de realizar a mano. Las funciones
l!liI 11. Desarrollar plug-ins Aprende jQuery 1.3 lID
de gestión de tabla $ . each ( ) , $ . map ( ) , y $ . grep () son buenos ejemplos de éstas. Así que ahora hemos visto la protección del espacio de nombre y la disponibilidad de
Para ilustrar la creación de dichos métodos, añadiremos una nueva función $ . sum () a la librería garantizada que conceden los plug-ins jQuery. Sin embargo, éstos son simple-
su número. Nuestro nuevo método aceptará una tabla, sumará los valores en la tabla, y mente beneficios organizativos. Para aprovechar realmente el potencial de los plug-ins
devolverá el resultado. El código para nuestro plug-in es bastante breve: jQuery, necesitamos aprender cómo crear nuevos métodos en instancias individuales
jQuery.sum = function(array)
de objeto jQuery.
var total = Di

jQuery. each (array, function(index, value) {


total += value¡ Añadir métodos de objeto jQuery
}) ;

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

• OuIs no:rtrud tJxerclt1llion ullsmco


else if (this. hasClass (claSs2)) ( • LBborl5 nlsJ ut a/lqulp ex 68 oommodo
• Duls Bute lruf8 dolor
this.removeClass(elass2) .addClass(elasSl);
(5Swap-ctasses)
};
Figura 11.3. Efectos de la clase that.
En primer lugar, comprobamos la presencia de c Las s r en el elemento coincidente y
sustituimos class2 si se encuentra. De lo contrario, comprobamos class2 e intercam- Necesitamos recordar que una expresión selector jQuery siempre puede coincidir
biamos classl si es necesario. Si ninguna clase está presente, no hacemos nada. con cero, uno o múltiples elementos. Debemos permitir cualquiera de estos escenarios
Para comprobar nuestro método, necesitamos algo de HTML con el ,que jugar: cuando diseñamos un método plug-in.
<u1>
<li>Lorem ipsum dolor 8it ametc/li>
. En este caso, estamos invocando. hasClass (), que solamente examina el primer
elemento coincidente. En su lugar, necesitamos comprobar cada elemento de forma in-
eli class="this">Consectetur adipisicing elite/li>
dependiente y actuar sobre él.
cli>Sed do eiusmod tempor incididunt ut laborec/li>
e1i class=Uthat">Magna aliquac::/li> La forma más sencilla de garantizar un comportamiento adecuado con independencia
eli class="thislt>Ut enirn ad minim veniame/li> del número de elementos coincidentes es siempre invocar . each () sobre el contexto
<:1i>Qui8 nostrud exercitation ullarncO</li> del método; esto refuerza la iteración implícita, que es importante para mantener con-
cli>Laboris nisi ut aliquip ex ea cornmodoc/li>
eli class="that">Duis aute irure dolorc/li>
sistencia entre plug-in y métodos incorporados.
<fuI> Dentro de la llamada. each (), thi s hace referencia a cada elemento DOM en turno,
<input type="button" value="Swap classes" id=:: 11swapll /> por lo que podemos ajustar nuestro código a comprobar de forma separada y aplicar
clases a cada elemento coincidente.
La clase this tiene estilo aplicado como texto en negrita, y la clase that como texto
en cursiva, como muestra la figura 11.2. jQuery.fn.swapClass = funetion(clasBl, class2) (
this.eaeh(funetion() (
var $element = jQuery(this);
• Lorem Ipsum d~r 1ft amet if ($element.hasClass(classl)) (
• Con .•• ctetur aclJpl.5clng .IIt
$element.removeClass(elaS9l) .addCla9s(ela992);
• Sed do eiusmod tempor incidldunt utlabore
• Ma{Jfl8SUque }
• Ut _"1m ad mtnlm vltl1lam else if ($element.hasClass(class2)) (
• Qula nostrud 8X8rcllation uJlamco
$element.removeCla99 (ela992) .addClass(classl);
• Laborts nIsI ut Bllqulp 8'1: ea commodo
• Duls IJuts lrura dolor
}
}) ;
~~ };

Figura 11.2. Textocon los estilos aplicados.

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-~~

lB 11. Desarrollar plug-ins Aprende jQuery 1.3 DI


.css(/text-decoration/, /underlineJ};
• Lotem lpsum dofor sH. emet
• CoMect8tur adlplslcJng
• Sed do e1usmod tempor incidldunl
• Magn •• llqua
8lit
ut labore }); .
return false;

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

orWn IdrnJnlm wnJam


lit tabor!
</div>
<div>Duis aute irure dolor</div>
<div>In reprehenderit
• Qufs n9!!nJd 8X8ldtatlon ultamea <div>In voluptate</div>
• labods n-' ul a1lgulp!IX e8 commodo
• PUl. tu- Inu. dolor
<div>Velit esse
_ ••~_.i\. <div>Cillum dolore</div>
~»'éIá5","" <div class="target">Fugiat nulla pariatur</div>
</div>
Figura 11.5. Estilosubrayado. <div>Excepteur sint occaecat cupidatat</div>
</div>
$ (document) .ready(function() ( </div>
$ (/#swap/) .click(function() <div>Non proident</div>
$(/li/) </div>
.swapClass(/this/, /that/) <div>Sunt in culpa qui officia</div>
mi 11. Desarrollar plug-ins Aprende jQuery 1.3 1m
Identificaremos los elementos destino «di v c Las s e " target" » al aplicar estilo a Sin embargo, este método es destructivo. El objeto jQuery actual se modifica com~ efec-
su texto en negrita, como muestra la figura 11.6. to secundario; uno que es evidente si almacenamos el objeto jQuery en una variable:

o.••ru.nl molSt anim id eet J.borum


$ (docu rnent) .~eady(function()
var $target ~ $(/.target/);
{
-,
Ut enim ad minlm veniam
$target.grandparent() .addClass(/highlight/);
Qul. no.ttud uercitaUon
lJIlamco labom niai
$target.hide() ;
UleJiquipex ••
}) ;

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

Sunl ¡n culpa qui ofrlcia "


Figura 11.8. Ocultarlos abuelos.

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

UI: en,"" Id mlnlm ven.m


Al invocar . pushStack () en lugar de . setArray () , creamos un nuevo objeto
Qui. noaUud &JUHChalion " jQuery, en lugar de modificar el antiguo. Ahora, el objeto $target no se modifica, y los
Ullamoo Jabone nist
objetos destino originales se ocultan por nuestro código, como muestra la figura 11.9.
'Ut a1iquip ex ea

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

Exceplaur aint oc:caec:aI: cupklatat

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

1m 11. Desarrollar plug-ins Aprende jQuery 1.3 lB

Oeaenmt mollil ..,im jd ..t I.borum


que podemos seleccionar y elegir los que son de utilidad para cada proyecto y omitir
los irrelevantes.'
Ul
Q,n-..d....,..•.•.
tIfÚm ad mlnlm Y8f'1iem

Cuando n01encontramos repitiendo algo en nuestro código muchas veces, puede


Ullamco.borianiai
l.laJiquipex ••
requerir la creación de un método abreviado. Por ejemplo, suponga que animamos fre-
Ou.aulelruredotot
cuentemente elementos utilizando una'combinación de las técnicas incorporadas "des-
infWPf!~'!'_.
lizar" y "desvanecer". Agrupar estos efectos significa animar la height y opacity de
InvoluptaUI un elemento de forma simultánea. El método. animate () facilita esto:
~l~~~:
Clliu~doIore .animate( (height: /hide/, opacity: /hide/});

--,
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

lA. aliqui¡) ex ea jQuery.fn.slideFadeln = function()


c-modo•••••••••• return this.animate({
l.oI8m j~um doler .it amel
- -.. . height: /show/,
opacity: /show/
Ouiaauleirutedolor
}) ;
In,.~rit
};
Involuptate

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

Ahora podemos invocar. slideFadeOut () y activar la animación siempre que se


Figura 11.10. Encadenarmétodos.
necesite. Puesto que, dentro de una definición de método plug-in, this hace referencia
al objeto jQuery actual, la animación se realizará sobre todos los elementos coinciden-
tes a la vez.
Añadir nuevos métodos abreviados Por exactitud, nuestros nuevos métodos deberían soportar los mismos parámetros
que los métodos abreviados incorporados. En particular, métodos como. fadeln () se
Muchos de los métodos incluidos en jQuery son métodos abreviados para otros pueden personalizar con velocidades y funciones de rellamada. Puesto que. anima-
métodos subyacentes. Por ejemplo, la mayoría de los métodos de evento son métodos te () también toma estos parámetros, permitir esto es sencillo. Simplemente aceptamos
abreviados para llamadas a .bind () o . trigger (), y muchos métodos AJAX inter- los parámetros y los pasamos a . animate () .
namente invocan $. aj ax () . Estos métodos abreviados facilitan utilizar características jQuery.fn.slideFadeOut = function(speed, callback)
que de lo contrario se complican por muchas opciones. return this.animate({
La biblioteca jQuery debe mantener un delicado equilibrio entre conveniencia y height: /hide/,
complejidad. Cada método que se añade a la librería puede ayudar a los desarrollado- opacity: /hide/
}, speed, callback);
res a escribir ciertas piezas de código más rápidamente, pero también aumenta el ta- };
maño global del código base y puede reducir el rendimiento. Por esta razón, muchos
métodos abreviados para funcionalidad incorporada se relegan a plug-ins, de modo jQuery.fn.slideFadeln = function(speed, callback) (
[""'."
I'J

l!mI 11. Desarrollar plug-ins Aprende jQuery 1.3 DI


return this.animate({
height, !show!,
opacity, !show!
}, speed, callback);
};

jQuery.fn.slideFadeToggle function(speed, callback) {


return this.animate({
height, !toggle!,
opacity, !toggle! 1€ld•..••.
d fad••.•UiiJ~'5lld"' •••d;fad~~ ~ I
}, speed, callback);
}; Figura 11.11. Métodos abreviados personalizados.
/
Ahora, tenemos métodos abreviados personalizados que funcionan como sus
equivalentes incorporados. Para demostrar esto, necesitaremos una sencilla página
HTML: Parámetros de método
<body> Ahora hemos visto varios ejemplos de métodos de plug-ín, algunos de los cuáles toman
<p>Lorem ipsum dolor sit amet, consectetur adipisicing
parámetros explícitos, y otros no. Como hemos explorado, la palabra dave this siempre
elit, sed do eiusrnod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, guíe
nostrud exercitation ullamco laboris nisi ut aliquip
.' se encuentra disponible para proporcionar contexto para el método, pero también pode-
mos proporcionar información adicional para influir en la operación del método. Hasta
ex ea commodo consequat. Duis aute irure dolor in el momento, los parámetros han sido pocos, pero por supuesto, esta lista puede hacerse
reprehenderit in voluptate velit es se cillum dolore eu mayor. Existen varios trucos que podemos utilizar para gestionar nuestros parámetros
fugiat nulla pariatur. Excepteur sint~occaecat
cupidatat non proident, sunt in culpa qui officia
de método y facilitar la vida a aquellos que utilizan nuestros plug-ins.
deserunt mollit anim id est laborum.</p> Como nuestro ejemplo, empezaremos con un método plug-in que proporciona una
cdiv class="controls"> sombra en un bloque de texto. Nuestra técnica será similar a la utilizada para el efecto
<input type="button" value="Slide and fade out ll
desvanecido en el rotativo de noticias del capítulo 9: utilizaremos un número de ele-
id= out 1>
11 11

<input type="button1! value="Slide and fade in id=l1 in"l1 />


mentos que son parcialmente transparentes, superpuestos en diferentes posiciones en
<input type=nbutton" value="Togglen id="toggle" /> la página.
<!div>
<!body> jQuery.fn.shadow = function() {
return this.each(function() {
var $originalElement = jQuery(this);
Nuestro script simplemente invocará nuestros nuevos métodos cuando se hace die for (var i = o; i < 5 i i++) {
en los botones: $originalElement
.clone ()
$ (document) .ready(function() { .css ({
$ (/#out!) .click (function () { position, !absolute!,
$(!p!) .slideFadeOut(!slow!) ; left, $originalElement.offset() .left + i,
return false; top' $originalElement.offset() .top + i,
}) ; margin, 0,
$(!#in!) .click(function() { zIndex: -11

$(!p!) .slideFadeln(!slow!); opac t y : 0.1


í

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()

1- •••••••••••.•••• • ••. 1 $(/h1/) .shadow({

)) ;
opac i ty . 0.05

Figura 11,15, Utilizar mapa de valores.


)) ;

Los parámetros no especificados utilizan sus valores predeterminados. El método


Valores de parámetro predeterminados $. extend () acepta incluso valores null, por lo que si los parámetros predetermina-
dos son todos aceptables, nuestro método se puede invocar de forma muy sencilla sin
A medida que crece el número de parámetros para un método, se hace menos pro- errores:
bable que siempre deseemos especificar cada uno. Un conjunto razonable de valores $ (document) .ready(function()
predeterminados puede hacer que una interfaz de plug-in sea más utilizable. $(/h1/) .shadow();
Afortunadamente, utilizar un mapa para nuestros parámetros ayuda con esta tarea )) ;
también; es sencillo omitir cualquier elemento del mapa y reemplazarlo con un prede-
terminado.
Funciones de rellamada
jQuery.fn.shadow = function(options)
var defaults = {
Por supuesto, algunos parámetros de método pueden ser algo más complicados que
s L ices : 5,
opacity: 0.1,
un sencillo valor numérico. Un tipo de parámetro común que hemos visto frecuente-
zIndex: -1 mente a lo largo de la API jQuery es la función de rellamada. Las funciones de rellamada
}; pueden llevar gran cantidad de flexibilidad a un plug-in sin requerir una gran cantidad
var apta = jQuery.extend(defaults, options);
de preparación cuando creamos el plug-in.
return this.each(function() (
var $originalElement = jQuery(this);
Para emplear una función de rellamada en nuestro método, necesitamos simplemente
for (var i = O; i < opts.slices¡ i++) aceptar el objeto función como un parámetro e invocar esa función donde sea apropia-
$originªlElement do en nuestra implementación de método. Como ejemplo, podemos ampliar nuestro
.clone()
método de sombra de texto para permitir que el usuario personalice la posición de la
.css({
position: /absolute/,
sombra relativa al texto.
left: $originalElement.offset() .left + i,
jQuery.fn.shadow = function(options)
topo $originalElement.offset() .top + i,
var'defaults = {
margin: O,
slices: 5,
zIndex: opts.zIndex,
opacity: 0.1,
opacity: opts.opacity
z fndex . -1,
)) . I sliceOffset: function(i}
.appendTo(/body/);
return {x: i, y: i};
)
)) ;
);
);
var opts = jQuery. extend (defaults, options);

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
, /

Los métodos deberían devolver el objeto jQuery para preservar encadenamiento. Si


el conjunto de objetos coincidentes se modifica, se debería crear un nuevo objeto al invo-
car . pushSt.ack () y se debería devolver este objeto en su lugar. Si se devuelve alguna
cosa diferente, se debe documentar de forma perceptible.
Si los métodos toman varias opciones, es preferible utilizar un mapa como argumen-
to de modo que las opciones se etiqueten y se puedan especificar en cualquier orden.
Los valores predeterminados se deberían definir en un mapa que se puede anular si es
necesario.
Las definiciones de método deben terminar en un punto y coma (;) de modo que los
compresores de código puedan analizar adecuadamente los archivos. Además, los plug- l'
ins pueden empezar con un punto y coma, de modo que otros scripts mal codificados
no causen conflictos después de la compresión.

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

Navegador de la API jQuery Referencia JScript MSDN


Iorn Zaeferrer ha creado un navegador con vista en árbol de la API jQuery con una La referencia JScript MSDN proporciona descripciones de todo el conjunto de fun-
característica de búsqueda y ordenación alfabética o por categorías: ciones, objetos, etc. Es especialmente de utilidad para entender la implementación de
http://jquery.bassistance.de/api-browser-l.2/ Microsoft del estándar ECMAScript en Internet Explorer:
http://msdn.microsoft.com/en-us/library/x85xXsf4(VS.71) .aspx
jQuery visual

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:

http://developer.mozilla.org/en/docs/JavaScript/ Compresor YUI


Este compresor JavaScript de la librería de interfaz de usuario Yahoo! (Yahoo! UI
Dev.opera Library) se utiliza para reducir el tamaño del código fuente jQuery. La herramienta de
línea de comando basada en [ava es una descarga gratuita, El código resultante es muy
Aunque centrado principalmente en su propia plataforma de navegador, el sitio Opera eficiente en tamaño de archivo y rendimiento, y se puede reducir aún más con compre-
para desarrolladores Web incluye una serie de artículos de utilidad sobre JavaScript: sión Gzip si se desea:
http://dev.opera.com/articles/ http://developer.yahoo.com/yui/compressor/
E&lI Apéndice A. Recursos online
Aprende jQuery 1.3 ID
JSMin
la chuleta CSS de Mezzoblue
Creado por Douglas Crockford, JSMin es un filtro que elimina comentarios y espacio Dave Shea proporciona esta chuleta CSS de utilidad en un intento por hacer que el
en blanco innecesario de archivos JavaScript. Normalmente reduce el tamaño de archivo proceso de diseño sea más sencillo, y proporciona una referencia rápida a comprobar
a la mitad, resultando en descargas más rápidas, especialmente cuando se combina con cuando encuentra problemas:
compresión de archivo basada en servidor:
http://mezzoblue.com/css/cribsheet/
http://www.crockford.com/javascript/jsmin.html

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.

Página principal de W3C El blog jQuery


[ohn Resig y otros colaboradores en el blog jQuery oficial publican anuncios sobre
El World Wide Web Consortium (W3C) establece el estándar para (X)HTML, y la nuevas versiones y otras iniciativas entre el equipo del proyecto, así como tutoriales y
página principal HTML es un estupendo punto de lanzamiento para sus especificacio- otros datos.
nes y directrices:
http://jquery.com/blog/
http://www.w3.org/MarkUp/

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.

Página principal CSS de W3C Ajaxian

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

[ohn Resig DOM scripting


El creador de jQuery, [ohn Resig, trata temas JavaScript avanzados en su blog: El blog de jeremy Keith empieza donde lo deja el popular libro de programación
http://ejohn.org/ DOM, un recurso fantástico para JavaScript sencillo:
http://domscripting.com/blog/
JavaScript ant
Este sitio contiene un repositorio de artículos relacionados con JavaScripty su uso en
As days pass by
navegadores Web modernos, así como una lista organizada de recursos JavaScript: l'
Stuart Langridge experimenta con uso avanzado del DOM del navegador:
http://javascriptant.com/
http://www.kryogenix.org/code/browser/

Robert ' S talk


A list apart
Robert Nyman escribe acerca de desarrollar para Internet, especialmente programa-
ción del lado del cliente: • A List Apart explora el diseño, desarrollo y significado de contenido Web, con un
foco especial en estándares Web y mejores prácticas:
http://www.robertnyrnan.com/
http://www.alistapart.com/
Estándares Web con imaginación
Elblog de Dustin Díaz contiene artículos sobre diseño y desarrollo Weben JavaScript: Marcos de trabajo de desarrollo Web
http://www.dustindiaz.com/ utilizando jQuery
Snook A medida que los desarrolladores de proyectos de código abierto son conscientes de
: jQuery, muchos están incorporando la librería JavaScript en sus propios sistemas. Lo
El blog de programación/ desarrollo Web de [onathan Snook: siguiente es una lista abreviada de estos seguidores:
http://snook.ca/ • Digitalus Site Manager: http://code.google.com/p/digitalus-si te-
manager/
Recurso JavaScript de Matt Snider • Drupal: http://drupal. org/
Elblog de Matt Snider está dedicado a comprender JavaScripty sus marcos de trabajo: • DutchPIPE: http://dutchpipe .org/
http://mattsnider.com/ • Hpricot: http://code.whytheluckystiff . net/hpricot/
• JobberBase: http://www.jobberbase.com/
lean "t • Laconica: http://laconi . cal
Tres sitios de Christian Heilmann proporcionan entradas de blog, código de muestra • Piwik:http://piwik.org/
y extensos artículos relacionados con JavaScript y desarrollo Web: • Pornrno: http://pommo . org/
http://icant.co.uk/ • symfony:http://www.syrnfony-project.org/
http://www.wait-till-i.com/
http://www.onlinetools.org/ • SPIP:http://www.spip.net/
ElI!I Apéndice A. Recursos online
,
,

• 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' ,,

·'

La documentación puede ayudar en temas de solución de problemas con nuestras


aplicaciones JavaScript, pero no hay sustituto para un buen conjunto de herramientas

péndice B de desarrollo de software. Afortunadamente, existen muchos paquetes de software dis-


ponibles para inspeccionar y depurar código JavaScript, y la mayoría de ellos están dis-
ponibles de forma gratuita.

Herramientas Herramientas para Firefox


Mozilla Firefox es el navegador de elección para la mayor parte de los desarrollado-
res Web, y por lo tanto tenemos algunas de las herramientas de desarrollo más amplías

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'!

mi Apéndice B. Herramientas de desarrollo Aprende jQuery 1.3 E1I


• Herramientas de manipulación CSS para averiguar por qué una página tiene un y modificar en el momento con nuevas reglas CSS. También proporciona otras ayudas
determinado aspecto y cambiarlo. variadas de desarrollo, como una regla para medir elementos de página: .
• Una consola JavaScript interactiva. http://w..{w.microsoft.com/downloads/details.aspx?FamilyID=
e59c3964'- 672d-4511-bb3e-2d5eldb91038
• Un depurador JavaScript que puede observar variables y seguir la ejecución de
código.
Mierosoft Visual Web Developer
Barra de herramientas de desarrollador Web El paquete Visual Studio de Microsoft se puede utilizar para inspeccionar y depurar
,código JavaScript:
Esto no solamente solapa Firebug en el ámbito de inspección DOM, sino que también ~
contiene herramientas para tareas comunes como manipulación de cookie, inspección http://msdn.microsoft.com/vstudio/express/vwd/
.de formulario, y cambio de tamaño de página. También puede utilizar esta barra para Para ejecutar el depurador de forma interactiva en la versión gratuita (Visual Web
desactivar rápida y fácilmente JavaScript para un sitio para asegurarse de que la funcio- Developer Express), siga el proceso detallado aquí:
nalidad se degrada bien cuando el navegador del usuario es menos compatible:
http://www.berniecode.com/blog/2007/03/08/how-to-debug-
http://chrispederick.com/wark/web-developer/ javascriptwith-visual-web-developer-express/
"
Venkman DebugBar
Venkman es el depurador JavaScript oficial para el proyecto Mozilla. Proporciona Proporciona un inspector DOM así como una consola JavaScript para depurar:
un entorno de solución de problemas que es una reminiscencia del sistema GDB para http://www.debugbar.com/
programas de depuración que se escriben en otros lenguajes.
http://www.mozilla.org/projects/venkman/
Drip

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/

Herramientas para Safari


Herramientas para Internet Explorer Safari continúa siendo el recién llegado como plataforma de desarrollo, pero todavía
existen herramientas disponibles para situaciones en las que el código se comporta de
Los sitios a menudo se comportan de forma diferente en lE que en otros navegadores forma diferente en este navegador frente a otros lugares.
Web, por lo que tener herramientas de depuración par~ esta plataforma es importante.
Menú Develop
Microsoft Internet Explorer Developer Toolbar
En Safari 3.1, una opción en la pestaña de opciones avanzadas del menú Preferences
La barra de herramientas de des arrollador proporciona principalmente una vista (Preferencias) proporciona un menú especial denominado Develop (Desarrollo). Con este
del árbol DOM para una página Web. Los elementos se pueden localizar visualmente, menú activado, se encuentran disponibles un Inspector Web y Consola JavaScript.
lID Apéndice B. Herramientas de desarrollo Aprende jQuery 1.3 lID
en su DOM e inspección de objeto, aunque también dispone de una buena consola. La
Inspector Web consola y el inspector se pueden invocar al incluir una referencia al archivo JavaSéript
Nitobi y llamar a ni tobi . Debug . log ( ) .
Safari 3 incluye la posibilidad de inspeccionar elementos de página individuales y r
recopilar información especialmente sobre las reglas CSS que se aplican a cada una. http://www.nitobibug.com/
http://trac.webkit.org/wiki/web%20Inspector
Desarrollos actuales de WebKit han mejorado sustancialmente esta herramienta de Paquete TextMate jQuery
inspector Web, concediendo muchas de las excelentes características de Firebug como
un depurador JavaScript integrado denominado Drosera. Esta extensión para el popular editor de texto Mac OS X TextMate permite resaltar
"sintaxis para métodos y selectores jQuery, completar código para métodos y una refe-
http://trac.webkit.org/wiki/Drosera
rencia API rápida desde su código. El paquete también es compatible con el editor de
texto E para Windows:
Herramientas para Opera http://github.com/kswedberg/jquery-tmbundle/

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/

Firebug Lite Aptana


Aunque la extensión Firebug está limitada al navegador Web Firefox. algunas de las
Este IDE (Integrated Development Environment o Entorno de desarrollo integrado) de
características se pueden replicar al incluir el script FírebugLite en la página Web. Este
desarrollo Web basado en Java es gratuito y multiplataforma. Junto con las características
paquete simula la consola Firebug, incluido permitir llamadas a console .log () para
de edición de código estándar y avanzadas, incorpora una copia completa de la docu-
funcionar en todos los navegadores y no lanzar errores JavaScript: .
mentación API jQuery, y tiene su propio depurador JavaScript basado en Firebug.
http://www.getfirebug.com/lite.html
http://www.aptana.com/

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:~' '\

.') ,c"l'-~~~' -~0\" . ®\


15@). '@'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'));
}) ;
};

Con este método definido, podemos invocar $ ( '#example' ) .print ( 'hello' )


para añadir el mensaje "hello'' dentro de <di v id=" example" >.

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

pero no fuera de ella. El siguiente código resulta en un error JavaScript:


globalVar = innerFn¡
function outerFn() { }
$('#example-2') .print('Outer functiont)¡ $('#example-3') .print('outerFn(), ,);
function innerFn() { outerFn() ;
$('#example-l') .print(IInner Function'); $('#example-3') .print('globalVar(), ,);
global Var ();
}
$('#example-l') .print('innerFn(), ');
.' La llamada a outerFn () después de la definición de la función modifica la variable
innerFn (); global global Varo Ahora es una referencia a innerFn () . Esto significa que la última
llamada a global Var () opera como lo haría una llamada interna a innerFn (), y se
Sin embargo, podemos ejecutar con éxito el código al invocarinnerFn () desde llega a las sentencias print:
outerFn() :
outerFn() :
function outerFn() { Outer function
$('#example-2') .print('Outer function'); 'globalVar ():
function innerFn() { Inner function
$(I#example-2 .print('Inner function');
1)

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();

El gran escape $('#example-4') .print (fnRef (),');


fnRef() ;

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

return innerFn¡ fnRef() ;


fnRef ();
var fnRef = outerFn() ; var fnRef2 = outerFn();
fnRef() ; fnRef2 ();
fnRef(); fnRef2 ();
var fnRef2 = out.e r Fn () i

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

Interacciones entre closures Closures en jQuery


Cuando existe más de una función interna, los closures pueden tener efectos que no Los métodos 'que hemos visto a lo largo de la librería jQuery a menudo toman al
son tan fáciles de anticipar. Suponga que emparejamos nuestra función de incremento menos una función como parámetro. Por 'conveniencia, a menudo utilizamos funciones
con otra función, ésta incrementada por dos: anónimas de modo que podemos definir el comportamiento de función cuando se ne-
cesita. Esto significa que las funciones raramente están en el espacio de nombre de nivel
function outerFn() {
superior; son normalmente funciones internas, lo que significa que pueden fácilmente
var outerVar = o;
function innerFn1() crear closures.
outerVar++i
/
$('#example-B') .print(' (1) outerVar , + outerVar) í

}
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; }) ;

$('#example-lO a.add') .click(function() / j);


counter++i }) ;

$('#example-lO') .print('counter = • + counter);


return false¡
Puesto que la función más interna se define dentro de la rellamada . each ( ) , este có-
}); digo crea tantas funciones como vínculos hay. Cada una de estas funciones se anexa como
}) ; un manejador el i ck a uno de los vínculos. Las funciones tienen index en su entorno de
Puesto que la variable counter se declara dentro del manejador . ready (), sola- cierre, ya que es un parámetro para la rellamada . each () . Esto se comporta de la misma
mente se encuentra disponible para el código jQuery dentro de este bloque y no para forma como si el manejador click estuviera escrito como una función cori nombre:
código de fuera. Se puede hacer referencia por el código en el manejador di ck que, sin $ (document) .ready(function() {
embargo incrementa y muestra el valor de la variable. Puesto que se crea un closure, la $('#example-13 a') .each(function(index)
function clickHandler() (
misma instancia de counter se hace referencia cada vez que se hace clic en el vínculo. $('#example-13') .print('index = ' + index);
Esto significa que los mensajes muestran un conjunto en continuo crecimiento de valo- return false¡
res, no simplemente 1 cada vez: }
$(this) .click(clickHandler);
counter = 1
}) ;
counter = 2
}) ;
counter e 3
La versión con la función anónima es algo más corta. La posición de esta función con
Los manejadores de evento pueden compartir sus entornos de cierre, como hacen
otras funciones: nombre sigue siendo relevante, sin embargo:
$ (document) .ready(function() (
$ (document) .ready(function() {
function clickHandler() (
var counter = Oi + index);
$ ('#example-14') .print('index I

$('#example-ll a.add') .click(function()


return false¡
counter++¡
$('#example-ll') .print('counter = ' + counter) i

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,

lmlII Apéndice C. lavaScript Closures Aprende jQuery 1.3 la


se utilizan. Otros lenguajes como Objective-C ayudan al programador al implementar alert(outerVar) ;

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()

I I alert ("he Ll.o') ;

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.

El problema de pérdidas de memoria de Internet Explorer


Todo esto normalmente no es un problema porque JavaScript puede detectar estos
bucles y limpiarlos cuando se convierten en huérfanos. Internet Explorer, sin embargo,
Figura C.2. Organización más compleja. tiene dificultad para gestionar una determina clase de bucles de referencia. Cuando un
bucle contiene elementos DOM y objetos JavaScript normales, lE no puede liberar ningu-
Ahora hemos añadido una propiedad al objeto C que hace referencia a B. En este no porque se gestiona por diferentes gestores de memoria. Estos bucles nunca se liberan
caso, cuando se libera A, B sigue teniendo un puntero hacia él desde e Este bucle de hasta que se cierra el navegador, lo que consume una gran cantidad de memoria en el
referencia se tiene que gestionar especialmente por JavaScript, que debe observar que tiempo. Una causa común de un bucle así es un sencillo manejador de evento:
todo el bucle se aísla de las variables que están en ámbito.
$ (document) .ready(function() (
var div'= document.getElementByld('foo');
Bucles de referencia accidentales div.onclick = function()
alert ('hello' ) ;
{

};
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.

* Todos los elementos.

#id El elemento con el ID dado,

element Todos los elementos del tipo dado.

.class Todos los elementos con la clase dada .

a, b Elementos que coinciden por a O b.

ab Elementos b que son descendientes de a.

a>b Elementos b que son hijos de a.


lll!I Apéndice D. Referencia rápida Aprende jQuery 1.3 l:I!'a

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.

:last :last-child Elementos que son el último hijo de su padre.


El último elemento en el conjunto resultado.
:not(a) :only-child Elementos que son el único hijo de su padre.
Todos los elementos en el conjunto resultado que no coinciden
por a. / : input Todos los elementos <input>, -cs e Le c t.>, <textarea>,y

: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".

:lt(index) " :submit Elementos <input> con type~"submit".


Todos los elementos en el conjunto resultado antes (menor que)
el índice dado (base O).
:image Elementos <input> con type~"image".
:header Elementos de encabezado (por ejemplo. <hl >, <h2 ».
:reset Elementos < input> con t.ype e " reset".
:animated Elementos con una animación en progreso. :button Elementos <input> con t.ype e v bu t t on v , y elementos
:contains(text) Elementos que contienen el texto facilitado. « but t.on ».

: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" />.

:visible Lo inverso de : hidden.

[attrl Elementos que tienen el atributo attr.


Métodos transversales DOM
Ia t t reva Lue l Elementos cuyo atributo attr es value.

[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.

.not(selector) Elementos seleccionados que no coinciden con el selector


facilitado. /
. ready(handler) Vincular handler a invocar cuando el DOMy CSS se han
.add(selector) Elementos seleccionados, más cualquier elemento adicional que cargado totalmente.
coincide con el selector facilitado. . bi nd (type, [dat.a} , Vincular handler a invocar cuando el tipo facilitado de evento
.find(selector) Elemento descendiente que coincide con el selector. handler) se envía al elemento.

.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.

.change(handler) Vincular handler a invocar cuando el valor del elemento


·siblings ( [selectorl) Todos los hermanos, opcional mente filtrados por un selector.
cambia.
·parent ( [selectorl ) El padre de cada elemento seleccionado, opcionalmente filtrado
.click(handler) Vincular handler a invocar cuando se hace clic en el ele-
por un selector.
mento .
. parents ( [selectorl ) Todos los ancestros, opcional mente filtrados porun se lector.
.dblclick(handler) Vincular handler a invocar cuando se hace doble clic en el
·closest se lector El primer elemento que coincide con el selector, empezando en elemento.
el elemento seleccionado y desplazándose hacia arriba por sus . error (handler) Vincular handler a invocar cuando el elemento recibe un evento
ancestrosen el árbol DOM.
error (depende del navegador).
·offsetParent () El padre posicionado (por ejemplo, relati ve, absolute) del .focus(handler) Vincular handler a invocar cuando el elemento recibe foco del
primer elemento seleccionado. . teclado.
·andSel f () Los elementos seleccionados, más el conjunto previo de ele- . keydown(handler) Vincular handler a invocar cuando se pulsa una tecla y el
mentos seleccionados en la pila jQuery interna. elemento tiene foco del teclado.
·end () El conjunto previo de elementos seleccionados en la pila jQuery .keypress(handler) Vincular handler a invocar cuando ocurre una pulsación de
interna. teclado y el elemento tiene foco 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"

lB Apéndice D. Referencia rápida Aprende jQuery 1.3 lB

.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.

.mouseout(handler) Vincular handler a invocar cuando el puntero del ratón abandona


el 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.

.scroll(handler) Vincular handler a invocar cuando la posición de la barra de


desplazamiento del elemento cambia. ·show() Muestra los elementos coincidentes.
.select(handler) Vincular handler a invocar cuando se selecciona texto en el ·hide () Oculta los elementos coincidentes.
elemento.
·show (speed, Muestra los elementos coincidentes al animar altura, anchura y
.submit(handler) Vincular handler a invocar cuando se envía el elemento de [callbackl) opacidad.
formulario.
.hide(speed, Oculta los elementos coincidentes al animar altura, anchura y
.unload(handler) Vincular handler a invocar cuando el elemento se descarga [calll:;Jackl ) opacidad.
de la memoria.
·toggle ( [speedl , Muestra u oculta los elementos coincidentes.
.hover(enter,leave) Vincular enter a invocar cuando el ratón entra en el elemento, [callbackl)
y leave cuando el ratón sale de él.
·slideDown ( [speedl , Muestra los elementos coincidentes con un movimiento desli-
.toggle(handlerl, Vincular handlerl a invocar cuando se hace clíccon el ratón en [callbackl) zante.
handler2, ... ) el elemento, seguido de handler2, etc., para siguientes clics.
·slideUp ( [speedl , Oculta los elementos coincidentes con un movimiento desli-
. trigger (type, [data]) Activar manejadores para el evento en el elemento, y ejecutar [callback] ) zante.
la acción predeterminada para el evento.
.slideToggle([speed] , Muestra u oculta los elementos coincidentes con un movimiento
. triggerHandler(type, Activar manejadores para el evento en el elemento sin ejecutar [callbackl) deslizante.
[data] ) ninguna acción predeterminada.
·fadeln ( [speedl , Muestra los elementos coincidentes al desvanecerlos a opaco.
. blur() Activar el evento blur . [callback] )
. change () Activar el evento change . .fadeOut( [speedl, Oculta los elementos coincidentes al desvanecerlos a transpa-
. click () [callbackl) rentes.
Activar el evento click .
lIIiI Apéndice D. Referencia rápida
Aprende jQuery 1.3 11III

.fadeTo(speed, ·html (vaLue ) Establecer el contenido HTML de cada elemento coincidente en


Ajusta la opacidad de los elementos coincidentes .
opacity, [callbackl) value,

.animate(attributes, ·text () Obtener el contenido textual de todos los elementos coincidentes


Lleva a cabo una animación personalizada de los atributos CSS
[speedl, [easingl, especificados. como una sola cadena.
[callbackl) .text(value) Establecer el contenido textual de cada elemento coincidente
.animate(attributes, en value.
Una interfaz de bajo nivel para. animate (),permitiendo control
options) sobre la cola de animación. ,.. ·val () Obtener el atributo valor del primero elemento coincidente.
.stop( [clearQueuel, Detener la animación actualmente en curso, luego empezar las .val(value) Establecer el atributo valor de cada elemento en value .
[jumpToEndl ) animaciones en cola, si alguna.
.css(key) Obtener el atributo CSS denominado key .
.queue () Recuperar la cola de animaciones en el primer elemento coin-
·css (key, value) Establecer el atributo CSS denominado key en value.
cidente.
.css(map) Establecer valores de atributo CSS, facilitados como pares
.queue(callback) Añadir callback al final de la cola .
clave-valor.
.queue(newQueue) Reemplazar la cola con una nueva .
.' .offset() Obtener las coordenadas de pixel superior e izquierda del primer
.dequeue () Ejecutar la siguiente animación en la cola . elemento coincidente, relativo al visor.

.position() Obtener las coordenadas de pixel superior e izquierda del


primer elemento coincidente, relativo al elemento devuelto por
.offsetParent ().
Métodos de manipulación DOM ·scrollTop () Obtener la posición de desplazamiento vertical del primero
elemento coincidente.
Los métodos de manipulación DOM se tratan en el capítulo 5. .scrollTop(value) Establecer la posición de desplazamiento vertical de todos los
elementos coincidentes en value.

.scrollLeft() Obtener la posición de desplazamiento horizontal del primer


elemento coincidente.
.attr(key) Obtener el atributo denominado key . .scrollLeft(value) Establecer la posición de desplazamiento horizontal de todos
.attr(key, value) Establecer el atributo denominado key en value . los elementos coincidentes en value.

.attr(key, fn) .height () Obtener el ancho del primer elemento coincidente .


Establecer el atributo denominado key en el resultado de fn
(llamado por separado en cada elemento coincidente). .height(value) Establecer la altura de todos los elementos coincidentes en
.attr(map) Establecer valores de atributo, dados como pares clave-valor . value.
.width() Obtener el ancho del primer elemento coincidente .
.removeAttr(key) Eliminar el atributo denominado key .
.width(value) Establecer el ancho de todos los elementos coincidentes en
.addClass(class) Añadir la clase dada a cada elemento coincidente .
value.
.removeClass(class) Eliminar la clase dada desde cada elemento coincidente .
·innerHeight () Obtener la altura del primero elemento coincidente, incluido
.toggleClass(class) Eliminar la clase dada, si está presente, y añadirla si no lo está, relleno, pero no borde.
para cada elemento coincidente.
·innerWidth () Obtener el ancho del elemento coincidente, incluido relleno, pero
.hasClass(class) Devolver true si cualquiera de los elementos coincidentes tiene no borde.
la clase facilitada.
.outerHeight Obtener la altura del primer elemento coincidente, incluido relleno,
.html () Obtener el contenido HTMLdel primer elemento coincidente. (includeMargin) borde y margen opcional.
m Apéndice D. Referencia rápida Aprende jQuery 1.3 11II

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.

Iterar sobre collection, ejecutando callback para cada


/
alfabético
callback) elemento.

$.extend(target, Modificar el objeto target al añadir propiedades desde los otros


addition, ... ) objetos proporcionados.

$.grep(array, Filtrar array al utilizar callback como una prueba.


callback, [invert])

$.makeArray(object) Convertir obj ect en una tabla.

$. 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 ••

añadir métodos de objeto


H mostrar un botón cerrar, 307 ordenación alfabética básica, 175
contexto del método de objeto, 349
ocultar la portada ampliada, 305 ordenar otros tipos de datos, 183
sobre ampliar imagen, 304 plug-ins, 17~ . encadenar métodos, 352
herramientas
configurar la página, 294 rendimiento, 180 características
otras herramientas,
mover las imágenes cuando resaltar columna, 186 abstraer fallos de navegador, 31
Aptana,387
se hace clic, 297 paginación, 190 aprovechar conocimiento de CSS, 31
Charles, 387
añadir animación deslizable, 299 habilitar los botones permitir múltiples acciones, 31
Fiddler, 387
mostrar iconos de acción, 301 del paginador, 192 soportar extensiones, 31
Firebug Lite, 386
imágenes, plug-íns marcar la página actual, 183 trabajar con conjuntos, 31
NitobiBug, 386
Jcrop,337 mostrar el paginador, 191 configurar el elemento HTML, 33
paquete TextMate jQuery, 387
Magnify,337 paginación con ordenación, 194 definición, 28
para Firefox, 383
Internet Explorer, herramientas referencia, 374 descargar jQuery, 36
barra de herramientas
DebugBar, 385 Centro de desarrollo Mozilla, 374 documentación
de des arrollador Web, 384
Drip,385 Dev.opera, 374 API jQuery, 373
comprobador de expresiones
Microsoft Internet Explorer JavaScript Toolbox, 375 jQuery visual, 374
regulares, 384
Developer Toolbar, 384,' Quirksmode, 375 navegador de la API jQuery, 374
Firebug, 383
Microsoft Visual Web Developer, 385 Referencia [script MSDN, 375 visor jQuery API Adobe AIR, 374
Venkman, 384
JavaScript Object Notation, (JSON), 162 Wiki jQuery, 373
para Internet Explorer, 384
empezar a trabajar, 28
DebugBar, 385
Drip, 385
J jQuery
.animatef), método, 98 encontrar el texto del poema, 36
.insert.Afterf), método, 115 historia del proyecto jQuery, 32
Microsoft Internet Explorer JavaScript
.ínsertlíeforeí), método, 115 jQueryUI
Developer Toolbar, 384 closures, 395
acciones, 29 librería de plug-ins
Microsoft Visual Web Developer, 385 compresores de código, 375
acceder a elementos componentes de interacción, 327
para Opera, 386 compressor YUI, 375
en un documento, 30 definición de jQuery UI, 325
Dragonfly, 386 JSMin,375
alterar el contenido efectos, 325
Inspector W eb, 386 Pretty printer, 376
de un documento, 30 ThemeRoller, 332
para Safari, 385 referencia (X)HTML, 376
animar cambios realizados Widgets, 330
Inspector Web,386 objeto, "
a un documento, 30 efectos
menú Develop, 385 $.getJSONO, función, "
Asynchronous JavaScript y XML aceleración y desaceleración
HTML, elementos de formulario, 229 como función global, 141
(AJAX),30 avanzada,326
$.getJSONO, función, definición, 141 animaciones
modificar la apariencia
1 $.getJSONO, función, método
de una página Web, 30 de clase, 326
de clase, 141 de color, 325
recuperar información
funciones jQuery globales, 141 efectos adicionales, 327
imágenes, carrusel de un servidor sin refrescar
recuperar objeto, 140 la función factory $0, 42
ampliar imagen una página, 30
ordenación, marcos de trabajo de desarrollo
animar la ampliación responder a la interacción
alternar la dirección Web,379
de la portada, 309 de un usuario, 30
de ordenación, 186 métodos
añadir un indicador de carga, 314 simplificar tareas JavaScript
etiquetas de agrupación, 175 de manipulación DOM, 129
aplazar las animaciones hasta comunes,30
manipular teclas de ordenar, 182 transversales DOM, 51
que la imagen se carga, 313 añadir, 36
&1 Índice aIfabético Índice aIfabético ID
aplicar estilo a celdas específicas, 52 ThemeRoller, 332 métodos abreviados, Opera, herramientas
encadenar, 53 Widgets, 330 Dragonfly, 386
añadir, 356 ,
proyecto JSON,162 ordenación JavaScript, 173
mostrar elementos, 357
fase de desarrollo público, 32 JSONP, 162 alternar la dirección
ocultar elementos, 357
jQuery 1.1, 32 de la ordenación, 186
personalizados, 358
jQuery 1.1.3, 32
jQuery 1.2, 32
L métodos de objeto jQuery, 15 el poder de los plug-ins, 179
métodos transversales, DOM etiquetas de agrupación de filas, 175
jQuery 1.2.6, 33 licencia pública GNU, 32 manipular teclas de ordenar, 182
aplicar estilo a celdas específicas, 52
jQuery 1.3, 33 Lightbox, plug-ins ordenación alfabética básica, 175
, definición, 51
jQuery UI, 32 BlockUI,340 (
ordenar otros tipos de datos, 183
encadenar, 53
recursos online, 372 FancyBox, 338 plug-ins, 179
función filter, 52
blogs,377 jqModal, 340 rendimiento, 180
recorrer el DOM, 52
compresor de código JavaScript, 375 Thickbox,339 ventajas de encadenar, 53 resaltar columna, 186
documentación jQuery, 373 limitaciones de seguridad, mover elementos, 118
referencia CSS, 376
referencia JavaScript, 374
cargar datos remotos, 161 marcar el contexto, 121 p
utilizar .' numerar el contexto, 121
referencia etiqueta HTML <iframe>, 162 vincular el contexto, 121 página, cargar
expresiones de selector, 403 JSONP para datos remotos, 162 MSDN Script, 375 coexistir con otras librerías, 60
métodos
métodos abreviados para código, 60
AJAX, 413
de efecto, 409
M N múltiples scripts en una página, 58
planificación de la ejecución
de evento, 407 manejador de evento, 80 NitobiBug, herramienta, 386 de código, 57
de manipulación DOM, 410 eliminar, 80 paginación, datos
transversales DOM, 405
variados, 414
espacio de nombres de evento, 80
volver a vincular eventos, 81
o paginación del lado del servidor, 188
paginación JavaScript, 190
selectores manipulación de tabla objeto evento paginación del lado del servidor
CSS,43 filtrado, 211 .istt) método, utilizar, 79 definición, 188
personalizados, 48 modificar apariencia de la tabla, 197 .preventfzefaultf), método, 77 ordenar y paginar, 188
utilizar funciones de rellamada, 363 ordenación JavaScript, 173 .stopf'ropagationf), método, 77 paginación JavaScript, 190
jQueryUI ordenar y paginar, 172 acciones predeterminadas, 77 habilitar los botones del pagínador, 192
efectos paginación definición, 75 marcar la página actual, 183
aceleración y des aceleración del lado del servidor, 186 delegación de evento, 78 mostrar el paginador, 191
avanzada,326 JavaScript, 190
animaciones destinos de los eventos, 76 paginación con ordenación, 194
memoria, peligros de pérdidas, 397 propiedad evenUarget, 78 parámetros de método
de clase, 326
bucles de referencia accidentales, 398 objeto JavaScript, definición, 359
de color, 325
problema de pérdidas de memoria de $.getJSONO, función, funciones de rellamada, 363
efectos adicionales, 327 Internet Explorer, 399 como función global, 141 mapas de parámetro, 361
librería de plug-ins
mensajes de campo, formulario definición, 141 parámetros sencillos, 360
componentes de interacción, 327 expresión regular, 227 método de clase, 141 predeterminados personalizables, 364
definición de jQuery UI, 325 insertar, 225 funciones jQuery globales, 141 valores de parámetro
efectos, 325 leyenda, 227 recuperar, 140 predeterminados, 362
I!B Índice alfubetico Índice alfabético EflI
pasar datos al servidor, 151 imágenes jQuery de atributo
funciones de rellamada, 363 Jcrop, 337 expresiones de selector, 403 aplicar estilo a vínculos, 46
llevar a cabo una petición Magnify, 337 métodos f definición, 46
GET,151 librería de plug-ins jQuery DI, 325 AJAX, 413 personalizados
POST,154 Lightbox de efecto, 409 aplicar estilo a filas alternas, 48
serializar un formulario, 156 BlockDI,340 de evento, 407 definición, 48
peligros de pérdidas de memoria, 397 FancyBox, 338 de manipulación DOM, 410 numeración
bucles de referencia accidentales, 398 jqModal, 340 transversales DOM, 405 en base
problema de pérdidas de memoria de Thickbox, 339 . variados, 414 cero, 49
Internet Explorer, 399 tablas, l' (X)HTML uno,49
pérdidas de memoria, peligros, 397 Flexigrid, 337 página principal de W3C, 376 selectores de formulario, 51
bucles de referencia accidentales, 398 jqGrid,336 rotativos símbolos
problema de pérdidas de memoria de Tablesorter, 336 añadir un indicador de carga, 289 $.ajax(options),413
Internet Explorer, 399 proyecto jQuery configurar, 282 $.ajaxSetup(options),413
personalizados, selectores, 48 fase de desarrollo público, 32 definición, 276 $.each(collection, callback), 414
aplicar estilo a filas alternas, 48 jQuery 1.1, 32 .' detenerse al pasar por encima, 286 $.extend(target, addition, ..."),414
numeración jQuery 1.1.3, 32 efecto degradado, 290 $.get(url, [data],[ callback],
en base cero, 49 jQuery 1.2, 32 función rotar titular, 283 [returnType]),413
en base uno, 49 jQuery 1.2.6, 33 recuperar un feed de un dominio $.getJSON(url, [data],[callback]), 413
selectores de formulario, 51 jQuery 1.3, 33 diferente, 288 $.getScript(url,[callback]),413 .
plug-ins jQuery DI, 32 titular rotativo, 277 $.grep(array, callback,[invert]), 414
compartir un plug-in con el mundo, 368 $.grep(array, callback,[invert]), 414
convenciones de nombrado, 369
estilo de documentación, 370
R s $.inArray(value, array), 414
$.inArray(value, array), 414
interfaces de método, 369 $.isFunction( object)
recursos online jQuery .showí), método, 95
desarrollar, $.isFunction(object),414
blogs,377 .show('fast'), método, 96
añadir nuevas funciones globales, 345 $.makeArray(object),414
compresor de código JavaScript, 375 .show('normal'), método, 96
añadir nuevos métodos $.map(array, callback), 414
documentación jQuery, 373 .show('slow'), método, 96
abreviados, 356 $.merge(arrayl, array2), 414
referencia CSS, 376 : Safari, herramientas
añadir una expresión de selector, 366 $.noConflict ([extreme]), 414
referencia JavaScript, 374 definición, 385
compartir un plug-in $.param(map),413
referencia inspector Web, 386
con el mundo, 368 $.post(url, [data],[callback],
CSS menú Develop, 385
métodos transversales DOM, 353 [returnType]),413
la chuleta CSS de Mezzoblue, 376 seguridad, limitaciones,
parámetros de método, 359 $.trim(string),414
página principal CSS de W3C, 376 cargar datos remotos, 161
encontrar, 321 $.trim(string),414
position is everything, 377 utilizar JSONP para datos remotos, 162
eventos de usuario, $.unique(array),414
JavaScript utilizar etiqueta HTML <iframe>, 162
hoverlntent,342 $.unique(array),414
Centro de desarrollo Mozilla, 374 selecto res .
Live Query, 342 (X)HTML, referencia
Dev.opera, 374 cSS
gráficas .add(selector),406
JavaScript Toolbox, 375 aplicar estilo a niveles
Flot, 341 .addClass(class),410
Quirksmode, 375 de elementos de lista, 44
Sparklines, 341 .after(content),412
Referencia JScript MSDN, 375 definición, 43
mi Índice alfabético Índice alfabético ID
.ajaxComplete(handler),413 .dequeuer), 410 .live(type, handler), 407 .scrollTop0,411
.ajaxComplete(handler),413 .die(type, [handler]), 407 .load(handler); 408 .scrollTop(value), 411
.ajaxError(handler),413 .each(callback),414 .load(url, [dataj.lcallbackj), 413 .selectí), 409
.ajaxSend(handler),413 .empty0,412 .map(callback),406 .select(handler),408
.ajaxStart(handler),413 .end0,406 .mousedown(handler),408 .serializef), 413
.ajaxStart(handler),413 .eq(index),406 .mouseenter(handler),408 .serialize.Arrayl), 413
.ajaxStop(handler), 413 .errort), 409 .mouseleave(handler),408 .showí), 409
.ajaxStop(handler), 413 .error(handler),407 .mousemove(handler), 408 .showr). método, 93
.ajaxSuccess(handler),413 .fadeIn([ speed ],[callback]), 409 .mouseout(handler),408 .show(speed,[callbackD,409
.ajaxSuccess(handler), 413 .fadeOut( [speed],[ callback]), 409 ,...mouseover(handler), 408 .siblings([selectorD,406
.andSelf0,406 .fadeTo(speed, opacity.] callback]), 410 .mouseup(handler),408 .slice(start, [endD, 406
.animateí), método, 98 .filter(callback),406 .next([selector]),406 .slideDown([speed ],[callback D, 409
.animate(attributes, [speed], .filter( selector), 405 .nextAll([selectorD, 406 . .slideToggle([speed],[ callbackj), 409
[easing],[ callback D, 410 .find(selector),406 .not(selector),406 .slideUp([speed],[callbackD,409
.animate(attributes,options),41O .focusO,409 .offset0,411 .stop([ clearQueue ],[jump ToEnd D, 410
.append(content),412 .focus(handler),407 .offsetParent0,406 .submití), 409
.appendTo(selector),412 .getO,414 .one(type, [data],handler), 407 .submit(handler),408
.attr(key),41O .get(index),414 .outerHeight (includeMargin), 411 .text0,411
.attr(key, fn), 410 .hasClass(class),410 .outerWidth (includeMargin), 412 .textívalue), 411
.attr(key, value), 410 .hasClass(className),414 .parent([selectorD,406 .toggle([speedl,[callbackD,409
.attr(map),410 .height0,411 .parents([ selector D, 406 .toggle(handler1,handler2, ...),408
.before(content),412 .height(value),411 .positíonü.dl l .toggleClass( class), 410
.bind('load'), sintaxis, 314 .hídet), 409 .prepend(content),412 .trigger(type, [data]), 408
.bind(type, [data],handler), 407 .hidet), método, 83 .prependTo(selector),412 .triggert), método, 83
.blur0,408 .hide(speed,[callback]),409 .prev([selectorD,406 implementar degradación, 83
.blur(handler),407 .hover(enter,leave),408 .prev All( [selector D, 406 métodos abreviados, 84
.changeO,408 .htmlt), 410 .queueí), 410 página, contraer, 83
.change(handler),407 .htrnl(value),411 .queueí), método, 105 página, expandir, 83
.children([selectorD,406 .index(element),414: " .queue(callback),410 .triggerHandler(type,[dataD,408
.click0,408 .innerHeight0,411 .queue(newQueue),410 .unbind([type],[handler]),407
.click(handler),407 .innerWidth0,411 .ready(handler),407, .unload(handler),408
.clone([withHandlersD,412 .insertAfter(selector),412 .remove([selectorD,412 .valO,411
.closest selector, 406 .insertBefore(selector),412 .removeAttr(key),410 .val(value),411
.contents0,406 .is(selector),414 .removeClass(class),410 .width0,411
.css(key), 411 .keydowru), 409 .removeData(key), 412 .width(value),411
.css(key, value), 411 .keydown(handler),407 .replaceAll(selector),412 .wrapí), método, 124
.css(map),411 .keypressí), 409 .replaceWith(content),412 .wrap(content),412
.data(key), 412 .keypress(handler),407 .resize(handler),408 .wrapAll(content),412
.data(key, value), 412 .keyupt), 409 .scroll(handler),408 .wrapInner(content),412
.dblclickO, 409 .keyup(handler),407 .scrollLeft0,411 W3C hypertext markup language,
.dblclick(handler),407 .length,414 .scrollLeft(value), 411 página principal, 376
lEIlI Índice alfabéiico

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

Potrebbero piacerti anche