You are on page 1of 62

DOM y AJAX con jQuery + Rails!

Ingeniera de Sistemas de
Informacin!
Grado en Ingeniera en Tecnologas de
Telecomunicacin!
GSyC!

2012 Departamento GSyC, URJC!


Algunos derechos reservados. Este trabajo se
distribuye bajo la licencia Creative Commons
Attribution-NonCommercial-ShareAlike 3.0
Unported License!
2012 Armando Armando Fox & David
Patterson!
Licensed under Creative Commons AttributionNonCommercial-ShareAlike 3.0 Unported License!

GSyC!

2!

Contenidos!
Introduccin!
El Document Object Model (DOM)!
Registro de manejadores de eventos sobre
elementos del DOM!
AJAX!
Otros usos de JavaScript!
Precauciones!

GSyC!

3!

Consejos para aprender!


Asegrate de tener la barra de herramientas de Firefox instalada (Web
Developer Extension)!
Durante tu aprendizaje de JavaScript ten siempre a mano una consola
en tu browser para poder probar cdigo!
En Firefox instala firebug: http://getfirebug.com!
En Safari o en Chrome, busca el men de desarrollador y all arranca la
consola de JavaScript!

Para depurar: !
console.log("hola mundo")
alert("hola")
Depurador de firebug!

Extensin de Firefox tilt para ver DOM en 3D: disponible en las ltimas
versiones de Firefox en el men Tools->Web Developer!
Y para trastear con JavaScript + HTML + CSS:!
http://jsfiddle.net!
GSyC!

4!

Introduccin!

Usos de JavaScript!
1. Client-side JavaScript: !

2.

Aplicaciones tipo desktop en el lado del cliente, como Google Docs!

3.

Cdigo JavaScript que corre en el browser, asociado a la pgina Web!


Se usa JavaScript para mejorar la experiencia de usuario de aplicaciones SaaS
que siguen el patrn arquitectnico MVC!
Se combina JavaScript con HTML y CSS!
Es importante que no se mezcle JavaScript con el cdigo HTML!
Es importante que el servicio est disponible para cliente no-JavaScript!
Pueden operar offline!
El cdigo JavaScript en el browser usa un servidor delgado, slo para almacenar
en la BD, recibiendo objetos JSON que convierte en HTML (en el browser)!
Se programan normalmente con frameworks, como Ember.js, Backbone.js o
Angular de Google que soporta el patrn MVC!

Aplicaciones SaaS pero con JavaScript en el servidor!

Frameworks como node.js y extensiones: meteor, express!

GSyC!

6!

Client-side JavaScript!
En este tema no se abordan las aplicaciones tipo desktop
programadas en JavaScript ni JavaScript en el servidor ni JavaScript
en los mviles!
Aprenderemos cmo usar JavaScript en el browser para mejorar la
interactividad con la interfaz!
Aprenderemos cmo usar jQuery como framework para consultar y
modificar los elementos del documento HTML cargado en el browser!
Aprenderemos a usar AJAX para que el cdigo JavaScript cargado en
una pgina realice RPCs a las acciones de controladores Rails !
Las acciones Rails devolvern vistas parciales que el cdigo JavaScript que corre
en el browser usar para modificar el documento HTML desde el que se hizo la
RPC!
!

Como ejemplos prcticos aprenderemos:!


a ocultar pelculas no aptas para menores modificando sin intervencin del servidor!
a mostrar una ventana flotante con la informacin de una pelcula que ha sido
obtenido con AJAX!
GSyC!

7!

La posicin privilegiada de
JavaScript!
JavaScript es un lenguaje ms!
Pero al correr en el browser el cdigo
JavaScript puede:!
1. Ser ejecutado como consecuencia de la interaccin del
usuario con la interfaz grfica de la pgina: click de
ratn, ratn pasando por encima, pulsar tecla,!
2. Realizar peticiones HTTP al servidor sin necesidad de
tener que recargar toda una pgina!
3. Ser ejecutado cuando el servidor enve una respuesta
HTTP a una peticin !
4. Consultar y modificar el documento cargado,
redibujndolo si hay cambios!
GSyC!

8!

Dnde poner el cdigo


JavaScript?!

Las pginas que quieren usar funciones o variables


JavaScript tienen que incluir el cdigo JavaScript!
Formas NO recomendadas: mezclado en el body !
http://pastebin.com/sTM4Z8wS !
<html>
<head><title>Update Address</title></head>
<body>
<!-- BAD: embedding scripts directly in page, esp. in body -->
<script>
<!-- // BAD: "hide" script body in HTML comment
// (modern browsers may not see script at all)
function checkValid() {
// BAD: checkValid is global
if !(fieldsValid(getElementById('addr'))) {
// BAD: > and < may confuse browser's HTML parser
alert('>>> Please fix errors & resubmit. <<<');
}
// BAD: "hide" end of HTML comment (l.3) in JS comment: -->
</script>
<!-- BAD: using HTML attributes for JS event handlers -->
<form onsubmit="return checkValid()" id="addr" action="/update">
<input onchange="RP.filter_adult" type="checkbox"/>
<!-- BAD: URL using 'javascript:' -->
<a href="javascript:back()">Go Back</a>
</form>
</body>
</html>

GSyC!

9!

Dnde poner el cdigo


JavaScript?!
La forma recomendada es guardar el cdigo en fichero aparte y utilizar la
etiqueta HTML <script src="url"> para incluirlo en la seccin HEAD del
documento, no poniendo cdigo mezclado en el body!
Misma justificacin que para sacar el estilo a ficheros .css!
Helper Rails que genera la etiqueta: javascript_include_tag

'fichero_javascript'

Los elementos HTML que vayan a iteraccionar con JavaScript:!


O bien se aaden al DOM en una funcin JavaScript setup()
O bien estn siempre, pero invisibles, usando CSS para que tengan el atributo
hidden, y luego en una funcin JavaScript setup() al cargar la pgina se cambia
su estilo para que aparezcan!
De cualquiera de las dos formas, si el browser no tiene JavaScript activado no
mostrar los elementos HTML que requieren JavaScript, lo que es bueno

En Rails:!
1. El cdigo JavaScript que escribamos para nuestra aplicacin se pone en ficheros con
extensin .js en el directorio app/assets/javascripts
2. El helper de Rails javascript_include_tag 'application' puede incluirse en cualquier template, p.ej.
en app/views/layouts/application.html.haml
Este fichero JavaScript produce el efecto de que al desplegar en produccin la aplicacin, Rails concatenar y
comprimir (minifying) todos los .js del directorio app/assets/javascripts junto a la biblioteca jQuery, y pondr
un fichero nico para toda la aplicacin en el dir public

GSyC!

10!

El Document Object Model


(DOM)!

DOM!
DOM: Document Object Model!
World Wide Web Consortium Document Object Model: !
"a platform and language-neutral interface that will allow
programs and scripts to dynamically access and update the
content, structure and style of documents"!

O sea: una representacin estandarizada, en forma de


rbol, de los elementos de un documento HTML, XML o
XHTML!
Coloquialmente hablamos del DOM de una pgina!
Cada elemento DOM se define recursivamente pues
una de sus propiedades es un array de elementos hijo!
Por tanto, el nodo DOM que representa el elemento <html> incluye
todo el documento, con nodos hijos para <head> y <body>, y <body>
tiene nodos hijos <h1>, <div>, !
Cada elemento puede tener atributos: <img src=>, <a href=>,
<div class=>, <span id=>,
GSyC!

12!

DOM!
Inspeccin del DOM en
Firefox: !
Arrancando firebug, o!
Men Herramientas->
Desarrollador Web ->
Inspeccionar, !
y abajo a la derecha,
Vista 3D!
Si no lo ves, instala un
Firefox ms reciente, o
instala la extensin tilt!

GSyC!

13!

Acceso al DOM desde


JavaScript!

Llamando a funciones JavaScript del objeto global


(window) se puede recorrer y consultar el rbol DOM
de la pgina actual!
Llamando a funciones JavaScript se pueden aadir/
eliminar elementos al rbol DOM, modificar su
contenido y modificar/eliminar/aadir atributos (p.ej. id
o class) de elementos, lo que provoca que se redibuje
la parte afectada de la pgina
Cuando el browser carga una pgina, el HTML de la
pgina se parsea para generar su rbol DOM!
En un browser, el objeto global JavaScript est accesible en
la variable global window
En window.document est la raz del rbol DOM de la
pgina que est mostrando el browser, cambiando cada vez
que carga una nueva.! GSyC!

14!

JSAPI!
El objeto window ofrece algo ms que el DOM!
Ofrece propiedades y funciones para manipular el DOM y otras
especficas del browser, con el nombre de API JSAPI!
location, document, document.head, document.body,
document.write(string), alert(string), confirm
(string),
Ejemplo: prueba a asignar una url a window.location en la consola!

Las implementaciones de JavaScript son bastante similares en los


diferentes browsers!
Sin embargo, la API JSAPI no es tan uniforme entre browsers!
Existen varias bibliotecas que recubren JSAPI para homogeneizar:
YUI, Prototype, DoJo. La ms usada: jQuery!

GSyC!

15!

jQuery!
jQuery es una API que recubre JSAPI,
proporcionando uniformidad entre browsers!
Usaremos jQuery y no JSAPI!

jQuery extiende JSAPI con animaciones,


soporte para CSS, eventos y AJAX!
jQuery define una funcin global: jQuery()
$() es un alias de jQuery()

GSyC!

16!

Funcin global jQuery(),$()


jQuery / $() es una funcin sobregarcada
(overloaded). Dependiendo de los argumentos que se le
pasan hace una cosa u otra!
1. Pasndole un selector de CSS (un string) : !
Devuelve los elementos del DOM correspondientes al selector CSS que se
pasa como argumento, o null si no hay ninguno!
Si se le pasa en un 2 argumento un elemento, slo devuelve los
descendientes de ste !
Si devuelve varios elementos, se itera luego sobre ellos con .each para
acceder a cada elemento de la coleccin devuelta!
2. Pasndole HTML (un string):!
Crea el elemento correspondiente al argumento (que ha de tener
etiquetas HTML para no confundirse con un selector CSS del caso
anterior) y lo devuelve recubierto/envuelto en un objeto jQuery, pero no lo
inserta en el DOM (hay funciones para hacerlo)!
3. Pasndole un/unos elemento JSAPI del DOM (ej. document o window):!
Devuelve un objeto jQuery que recubre el elemento del argumento!
4. Pasndole una funcin como argumento!
Ejecuta la funcin una vez se ha cargado todo documento y el DOM est
preparado para ser manipulado. Equivalente a $(document).ready(f)
GSyC!

17!

Seleccin de elementos con


$('selector-CSS')
!

Ejemplos de selectores CSS:!


selector

qu selecciona !

h1
div#message
.red
div.red, h1
div#message h1
a.lnk
a.lnk:hover

cualquier elemento h1
*el* elemento div cuyo ID es message
cualquier elemento de clase red
los elementos div de clase red o cualquier h1
los elementos h1 dentro de los div de clase message
elementos de clase lnk
elementos a con clase lnk, cuando se pasa por
encima de ellos (hover)

http://www.w3.org/TR/selectors/#selectors!

Ejemplos de llamadas a $('selector-CSS'):

jQuery('#movies')
$('#movies')
$('h1.title')
GSyC!

18!

Propiedades y funciones de
elementos DOM recubiertos
por jQuery()/$()!
Hay 3 tipos de acciones que se pueden realizar sobre
un elemento o un conjunto de elementos devueltos por
jQuery('selector-CSS')/$('selector-CSS'):!
1. Cambiar su aspecto visual modificando la clase CSS del/de
los elementos!
2. Cambiar su contenido modificando el html del/de los
elementos o el texto plano del elemento!
3. Animar el/los elementos (mostrar/ocultar, desvanecerse/
reaparecer, )!

Consulta http://api.jquery.com/ !
A continuacin presentamos algunas funciones!
GSyC!

19!

Funciones para elementos


seleccionados por jQuery()
Notacin e.func(args)

es una funcin que se aplica a un elemento e devuelto por jQuery().


Ej: $('h1.title').func(args). Si no se indica otra cosa, devuelve el elemento tras
aplicrsele la funcin, por lo que se pueden llamar en cascada

.is(:cond')

Comprueba si el elemento est ':checked', ':selected', ':enabled',

':disabled'

.addClass('cssClass'), .removeClass('cssClass'), .hasClass()


.insertBefore(elem), .insertAfter(elem), .remove(),
.replaceWith(elem), .appendTo(elem)
.clone() Devuelve copia recursiva de los descencientes
.html(['htmlString']) Devuelve (o pone si hay argumento) el html
.text([texto']) Devuelve (o pone si hay argumento) el texto sin etiquetas html
(p.ej. a un <h1> texto </h1>

.val([val])

Devuelve o pone el valor del elemento: para textboxes el contenido, para


botones la etiqueta, para mens el texto de la opcin seleccionada

.attr('attr' [,nuevoValor]). Ej: $('img').attr('src'), 'http://google.com')


.find('selector-CSS') Devuelve elementos hijos seleccionados por selector-CSS. Ej:
$('#movies tr') == $('#movies').find('tr')

GSyC!

20!

Funciones para elementos


seleccionados por jQuery()

.css('propiedad') Devuelve propiedad CSS del elemento


.css({'propiedad':'valor',}) Pone propiedades CSS

al elemento

Animaciones:
.hide([duracion][,callback]), .show([duracion][,callback])
Muestra/oculta el elemento, tardando duracion ('fast', 'slow' o
milisegundos), y cuando termina llama a callback

.slideUp([duracion][,callback]), Desaparece
.slideDown([duracion][,callback]),
.slideToggle([duracion][,callback])
.fadeOut([duracion][,callback]), Desaparece
.fadeIn([duracion][,callback])
.fadeTo(duracion, target, callback)

como persiana

haciendo transparente

Target es un valor de transparencia entre 0.0 y 1.0


GSyC!

21!

Ejemplos!
Carga la pgina de rottenpotatoes en Firefox!
Activa firebug. Si no lo tienes an instlado, hazlo: https://
www.getfirebug.com/!
Selecciona el men Scripts de Firebug!
Selecciona el enlace Reload to see all sources!
Prueba ahora en la consola de Firebug las siguientes llamadas:!
this==window;
this.document;
this.document.getElementById('movies'); // Ejemplo de uso de API JSAPI
$('#movies'); // Selecciona toda la tabla con id CSS #movies usando jQuery
jQuery('tr'); // Selecciona todas las filas (<tr>) del documento
$('tr').hide() // En cada tr pone: <tr style="display: none;">. Puedes verlo en el men
// Herramientas->Web Developer Extension->View Source->View Generated Source

$('tr').show() // Cambia de nuevo el style para que aparezcan pintadas


h=$('h1.title') // Selecciona el elemento <h1> de clase CSS .title
h.slideUp('slow') // Y ahora lo anima ocultndolo lentamente
h.slideDown('slow') // Y ahora lo anima hacindolo aparecer lentamente
h.slideToggle(2500) // Si estaba oculto lo hace aparecer, y viceversa
theaders=$('#movies th') // los <th> dentro del elemento con id #movies
theaders.fadeTo(2000, 0.25) // los vuelve transparentes
$('tr').fadeTo(2000,0.25)
// y lo mismo con todos los <tr>
GSyC!

22!

Registro de manejadores de
eventos sobre elementos del
DOM!

Eventos y callbacks!
JSAPI permite registrar funciones JavaScript que el browser
llamar cuando ocurran eventos en la interfaz: pulsar botn,
mover ratn sobre elemento, enviar formulario,!

Les llamaremos gestores/manejadores de eventos o callbacks!


JSAPI define varios tipos de eventos, que jQuery extiende!
Algunos elementos tienen un comportamiento predefinido para algunos
eventos. Ej. click en un enlace carga la pgina de la url!

http://www.chipchapin.com/WebTools/JavaScript/example1-04.html!

En las callbacks podemos programar acciones jQuery que


modifican el DOM !
Al poderse modificar localmente el DOM, la interaccin es mucho
ms dinmica!

Los elementos HTML cambian localmente en el navegador como


consecuencia de acciones del usuario!
Comparado con tener que realizar una nueva peticin HTTP para que el
servidor devuelva una nueva pgina con los elementos HTML cambiados, la
experiencia del usuario es mucho ms fluida!

GSyC!

24!

Registro de manejadores para


eventos con jQuery!

Cmo registrar eventos!


elemento.tipo-evento(func([event]){})

Asocia a elemento func como un manejador/callback para tipo-evento. Cuando


ocurra tipo-evento sobre elemento, el browser llamar a func(event) pasando
en event el evento!
!

Tipos de Eventos!
Tipos de eventos aplicables a cualquier elemento:!
o
o
o
o

click, dblclick, mousedown/mouseup, mouseenter/mouseleave


keypress (event.which devuelve el cdigo ASCII dentro del manejador)!
focus/blur (el elemento obtiene/pierde el foco)!
focusin/focusout (el padre del elemento obtiene/pierde el foco)!

Eventos aplicables a elementos accionables por el usuario (forms,


checkboxes, radio buttons, text box, text field, mens):!
o
o
o

change !
select (event.which devuelve el texto seleccionado dentro del manejador)!
submit (disparado cuando se enva un formulario)!
!

25!

Ejecucin de callbacks!
El browser llama a un manejador de eventos/
callback cuando se produce el evento sobre el
elemento con el que se asoci el manejador!
Si hay registrado un manejador para el
elemento, o para alguno de sus elementos
ancestros, se ejecutan en cadena antes del
comportamiento predefinido (si existe):!

La cadena comienza en el elemento en el que se produce el


evento y termina en el objeto global (window)!
Si un manejador devuelve true, se ejecuta el siguiente en la
cadena!
Si el manejador devuelve false, se interrumpe la ejecucin en
cadena, y la del comportamiento predefinido, que es el ltimo!
GSyC!

26!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !

GSyC!

27!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!
http://pastebin.com/85fZTe4J
RP = {
setup: function() {
// construct new DOM elements
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
},
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
}
$(RP.setup);
// when document ready, run setup code
GSyC!

28!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!
RP = {
setup: function() {
// construct new DOM elements
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
Al cargar la pgina, y
},
por tanto este fichero,
hide_if_adult_row: function() {
pedimos
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text()))
{ que se
$(this).hide();
ejecute RP.setup
}
una vez est todo el
}
DOM generado
}
$(RP.setup);
// when document ready, run setup code

GSyC!

29!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!

Inserta el cdigo HTML


con

RP = {
que interaccionar
setup: function() {
// construct new DOM elements
JavaScript
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
},
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
}
$(RP.setup);
// when document ready, run setup code

GSyC!

30!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!

Registra callback para el

RP = {
evento change de
setup: function() {
// construct new DOM elements
la checkbox. Mejor change
$('<label for="filter" class="explanation">' +
que click: change se activa
'Restrict to movies suitable for children' +
tambin cuando se selecciona
'</label>' +
'<input type="checkbox" id="filter"/>'
por teclado la checkbox.
).insertBefore('#movies').change(RP.filter_adult);
},
En la callback
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
this es la checkbox JSAPI,
if ($(this).is(':checked')) {
que envolvemos con $(this)
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
},
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
}
$(RP.setup);
// when document ready, run setup code

GSyC!

31!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!
RP = {
setup: function() {
// construct new DOM elements
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
Itera sobre cada tr, y lo
$('#movies tbody tr').show();
pasa como this a la funcin
};
del argumento
},
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
}
$(RP.setup);
// when document ready, run setup code

GSyC!

32!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!
RP = {
setup: function() {
// construct new DOM elements
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
},
Muestra todos los
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
}
$(RP.setup);
// when document ready, run setup code

GSyC!

tr

33!

Ejemplo!
A continuacin aadimos a la pgina principal de
rottenpotatoes un botn para seleccionar pelculas aptas
para menores, sin interaccin con el servidor !
app/assets/javascripts/rp.js!
RP = {
setup: function() {
// construct new DOM elements
$('<label for="filter" class="explanation">' +
'Restrict to movies suitable for children' +
'</label>' +
'<input type="checkbox" id="filter"/>'
).insertBefore('#movies').change(RP.filter_adult);
},
filter_adult: function () {
// 'this' is JSAPI element that received event (checkbox)
if ($(this).is(':checked')) {
$('#movies tbody tr').each(RP.hide_if_adult_row);
} else {
$('#movies tbody tr').show();
};
},
hide_if_adult_row: function() {
if (! /^G|PG$/i.test($(this).find('td:nth-child(2)').text())) {
$(this).hide();
}
}
2 td del
}
$(RP.setup);
// when document ready, run setup code

GSyC!

tr pasado en this
34!

Comentarios sobre el Ejemplo!


La checkbox se aade desde la funcin setup(), y sta
slo se ejecuta si el browser tiene JavaScript!

Si la hubiramos puesto en el HTML, los browsers sin JavaScript la


mostraran pero no podran interactuar con ella!

Slo se ha aadido un objeto al mbito global, RP, que


define su propio mbito. Por eso todas las funciones se
tienen que llamar por su nombre: RP.fname
Implementacin alternativa menos intrusiva de la
ocultacin de pelis para mayores:!

Si en el servidor marcamos las filas de pelculas para adultos:!


%tr{:class => ('adult' unless movie.rating =~ /^G|PG$/)}

Entonces podramos eliminar RP.hide_if_adult_row()


Y substituir $('#movies tbody tr').each(RP.hide_if_adult_row);
por $('#movies tr.adult').hide();
De esta forma el cdigo JavaScript no se apoya en detalles de la
presentacin (Dependemos de que la calificacin est en la 2 columna
de la tabla)
GSyC!

35!

Eventos definidos y disparados


por el usuario!
Se pueden definir nuevos eventos utilizando esta otra
forma de registrar callbacks para un evento:!
elemento.bind('nuevo-evento', func(event){})

Y luego se puede llamar a estos eventos explcitamente


con trigger
Ejemplo que puedes probar en firebug, en la pgina de
rottenpotatoes p.ej.:!
$('#movies').bind('miEvento',
function(event, param1, param2){
alert(param1 + "\n" + param2);
});
$('#movies').trigger('miEvento',['Custom', 'Event']);
GSyC!

36!

AJAX!

Introduccin!
En 1998 Microsoft aade el objeto
XmlHttpRequest o xhr a su navegador
Internet Explorer 5 (IE5)!
En 2005 Google Maps es una de las
primeras aplicaciones que demuestra la
potencia de AJAX!

GSyC!

38!

Objeto XmlHttpRequest
(xhr)
El objeto JavaScript XmlHttpRequest, o xhr, que proporcionan
muchos browsers permite realizar RPCs asncronas: la llamada
retorna antes de obtenerse la respuesta del servidor!
Enva un mensaje de peticin HTTP desde JavaScript sin que sea
necesario redibujar toda una pgina al obtener el mensaje de
respuesta!

Genera un mensaje de peticin HTTP normal, al que le aade la cabecera


X-Requested-With: XMLHttpRequest!

Cuando llega la respuesta se llama a una callback que utiliza la


respuesta obtenida (p.ej. una vista parcial HTML, o XML, o JSON,
texto, o nada) para modificar el DOM de la misma pgina desde la
que se hace la peticin!
Al no recargar toda la pgina se tarda menos en mostrar la
respuesta y la interfaz ofrece una interaccin ms dinmica!
En jQuery se utiliza $.ajax para usar el objeto xhr
39!

Programacin basada en
eventos, tambin para xhr!

La programacin en JavaScript para interactuar con la interfaz grfica es


basada en eventos (event driven): registras callback asociada a un objeto y
a un evento, que es llamada por el runtime"
JavaScript tiene un nico thread que va ejecutando los manejadores
registrados segn van ocurriendo los eventos que los disparan!

Pseudocdigo del runtime de JavaScript:!

while (true) {
var event = waitForEvent();
event.callback();
redrawDOM();
}!

Por tanto, la llamada para realizar la peticin xhr no puede ser bloqueante y
quedarse esperando a recibir la respuesta: de hacerlo, bloqueara el bucle de
ejecucin de callbacks, congelando la interaccin con la interfaz!
Por ello la RPC que generamos con xhr retorna inmediatamente, antes de
obtener la respuesta!

Se le pasa una funcin como callback que es llamada por el browser cuando llega la respuesta!
La callback puede reemplazar, cambiar, animar, ya que re se redibuja el DOM en funcin de
los cambios de las callbacks que se van llamando!
GSyC!
40!

xhr en jQuery: $.ajax


Con $.ajax realiza en jQuery una llamada XHR!
Es no bloqueante: retorna inmediatamente, y despus, cuando
llegue la respuesta HTTP al browser, el runtime de JavaScript
llama a la callback registrada!
Ejemplo:
$.ajax({type
:
url
:
timeout:
success:
error

'GET',
'http://',
milisegundos,
fn_success,

: fn_error

});

GSyC!

// mtodo HTTP
//
//
//
//
//
//

si tarda ms se llama a error


funcin a la que llamar cuando
llegue la respuesta
funcin a la que
llamar si hay errores, p.ej.
timeout, servidor no arrancado

41!

Ejemplo de uso de $.ajax()


Vamos a modificar rottenpotatoes para que los detalles de una
pelcula se pidan a nuestro servidor Rails desde JavaScript,
usando AJAX, y se muestren en una ventana flotante, sin
abandonar la pgina index!

GSyC!

42!

Ejemplo de uso de $.ajax()


Vamos a modificar rottenpotatoes para que los detalles de una
pelcula se pidan a nuestro servidor Rails desde JavaScript,
usando AJAX, y se muestren en una ventana flotante, sin
abandonar la pgina index!
Desde una funcin JavaScript se usar xhr para realizar una
peticin HTTP a la URL de la accin MoviesController#show

Podramos crear otra accin pero reaprovecharemos sta accin del


controlador!

Esta accin devolver una vista parcial cuando detecte que la


peticin se hace usando xhr, o la vista normal en caso contrario!
Aadiremos un manejador de eventos a los enlaces que hay
ahora en la pgina index para ver ms detalles de cada
pelcula, y desde el manejador realizaremos la llamada xhr!
Aadiremos un nuevo div a la pgina index para rellenarlo
con los datos devueltos por la llamada xhr y le pondremos
propiedades CSS que hagan que aparezca en el centro de la
pantalla, "flotando"!
GSyC!

43!

Ejemplo de uso de $.ajax()


Vista parcial app/movies/_movie.html.haml que
devolver la accin MoviesController#show cuando
sea llamada usando xhr!
%p= movie.description
= link_to 'Edit Movie', edit_movie_path(movie)
= link_to 'Close', '', {:id => 'closeLink'}

http://pastebin.com/dW04wY9F

Accin del controlador


class MoviesController < ApplicationController

def show
id = params[:id] # retrieve movie ID from URI route
@movie = Movie.find(id) # look up movie by unique ID
render :partial => 'movie', :object => @movie and return if request.xhr?
# will render app/views/movies/show.<extension> by default
end

end

http://pastebin.com/CxxPBQpy

GSyC!

44!

Ejemplo de uso de $.ajax()


Vista parcial app/movies/_movie.html.haml que
devolver la accin MoviesController#show cuando
sea llamada usando xhr!
%p= movie.description
= link_to 'Edit Movie', edit_movie_path(movie)
= link_to 'Close', '', {:id => 'closeLink'}

El mtodo xhr? del objeto request del


controlador devuelve true si en el
mensaje de peticin HTTP se ha
includo la cabecera

Accin del controlador

class MoviesController < ApplicationController


X-Requested-With: XMLHttpRequest

def show
id = params[:id] # retrieve movie ID from URI route
@movie = Movie.find(id) # look up movie by unique ID
render :partial => 'movie', :object => @movie and return if request.xhr?
# will render app/views/movies/show.<extension> by default
end

end

GSyC!

45!

Ejemplo de uso de $.ajax()


Vista parcial app/movies/_movie.html.haml que
devolver la accin MoviesController#show cuando
sea llamada usando xhr!
%p= movie.description

Alternativas
que podra esperar el cliente JavaScript:!
= link_to 'Edit Movie', edit_movie_path(movie)

render= :json
=> @movies
and =>
return
if request.xhr // Llama a @movies.to_json
link_to 'Close',
'', {:id
'closeLink'}
render :xml
=> @movies and return if request.xhr // Llama a @movies.to_xml

:text => @movie.title and return if request.xhr


render :nothing => true and return if request.xhr
Accin del controlador
render

class MoviesController < ApplicationController

def show
id = params[:id] # retrieve movie ID from URI route
@movie = Movie.find(id) # look up movie by unique ID
render :partial => 'movie', :object => @movie and return if request.xhr?
# will render app/views/movies/show.<extension> by default
end

end

GSyC!

46!

Ejemplo de uso de $.ajax()


Vista parcial app/movies/_movie.html.haml que
devolver la accin MoviesController#show cuando
sea llamada usando xhr!
%p= movie.description
= link_to 'Edit Movie', edit_movie_path(movie)
= link_to 'Close', '', {:id => 'closeLink'}

Accin del controlador


class MoviesController < ApplicationController

def show
id = params[:id] # retrieve movie ID from URI route
@movie = Movie.find(id) # look up movie by unique ID
render :partial => 'movie', :object => @movie and return if request.xhr?
# will render app/views/movies/show.<extension> by default
end

end

Si no es una peticin xhr se sirve la vista normal


GSyC!

47!

Ejemplo de uso de $.ajax()


Nuevo fichero JavaScript app/assets/javascripts/rpajax.js
RPAjax = {
setup: function() {
// add invisible 'div' to end of page:
$('<div id="movieInfo"></div>').
hide().
appendTo($('body'));
$('#movies a').click(RPAjax.getMovieInfo);
},
getMovieInfo: function() {
$.ajax({type: 'GET',
url: $(this).attr('href'),
timeout: 5000,
success: RPAjax.showMovieInfo,
error: function() { alert('Error!'); }
});
return(false);
},
showMovieInfo: function(data) {
// center a floater 1/2 as wide and 1/4 as tall as screen
var oneFourth = Math.ceil($(window).width() / 4);
$('#movieInfo').
html(data).
css({'left': oneFourth, 'width': 2*oneFourth, 'top': 150}).
show();
// make the Close link in the hidden element work
$('#closeLink').click(RPAjax.hideMovieInfo);
return(false);
},
hideMovieInfo: function() {
$('#movieInfo').hide();
return(false);
},
}
http://pastebin.com/yhmPC4Ki
$(RPAjax.setup);

48!

Ejemplo de uso de $.ajax()


Nuevo fichero JavaScript app/assets/javascripts/rpajax.js
Aadimos nuevo <div>
al final de la pgina, con id
para poderle poner CSS

RPAjax = {
setup: function() {
// add invisible 'div' to end of page:
$('<div id="movieInfo"></div>').
hide().
appendTo($('body'));
$('#movies a').click(RPAjax.getMovieInfo);
},
getMovieInfo: function() {
$.ajax({type: 'GET',
url: $(this).attr('href'),
timeout: 5000,
success: RPAjax.showMovieInfo,
error: function() { alert('Error!'); }
});
return(false);
},
showMovieInfo: function(data) {
// center a floater 1/2 as wide and 1/4 as tall as screen
var oneFourth = Math.ceil($(window).width() / 4);
$('#movieInfo').
html(data).
css({'left': oneFourth, 'width': 2*oneFourth, 'top': 150}).
show();
// make the Close link in the hidden element work
$('#closeLink').click(RPAjax.hideMovieInfo);
return(false);
},
hideMovieInfo: function() {
$('#movieInfo').hide();
return(false);
},
}
$(RPAjax.setup);

Asociamos callback para


cuando se haga click
en cada uno de los elementos
<a> de la tabla #movies

49!

Ejemplo de uso de $.ajax()


Nuevo fichero JavaScript app/assets/javascripts/rpajax.js
RPAjax = {
setup: function() {
// add invisible 'div' to end of page:
$('<div id="movieInfo"></div>').
this es el elemento sobre el
hide().
appendTo($('body'));
que se ha disparado el evento: un
$('#movies a').click(RPAjax.getMovieInfo);
'#movies a' == <a href="url">
},
getMovieInfo: function() {
$.ajax({type: 'GET',
url: $(this).attr('href'),
timeout: 5000,
success: RPAjax.showMovieInfo,
error: function() { alert('Error!'); }
});
return(false);
},
showMovieInfo: function(data) {
// center a floater 1/2 as wide and 1/4 as tall as screen
var oneFourth = Math.ceil($(window).width() / 4);
$('#movieInfo').
html(data).
css({'left': oneFourth, 'width': 2*oneFourth, 'top': 150}).
show();
// make the Close link in the hidden element work
$('#closeLink').click(RPAjax.hideMovieInfo);
return(false);
},
hideMovieInfo: function() {
$('#movieInfo').hide();
return(false);
},
}
$(RPAjax.setup);
50!

La url la sacamos del actual enlace


en el HTML: <a href="url">
Paramos la cadena de ejecucin
de callbacks: no queremos que
se ejecute la accin por defecto
del link: pedir la url (otra vez!)

Ejemplo de uso de $.ajax()


Nuevo fichero JavaScript app/assets/javascripts/rpajax.js
RPAjax = {
setup: function() {
// add invisible 'div' to end of page:
$('<div id="movieInfo"></div>').
hide().
appendTo($('body'));
$('#movies a').click(RPAjax.getMovieInfo);
},
getMovieInfo: function() {
$.ajax({type: 'GET',
url: $(this).attr('href'),
timeout: 5000,
success: RPAjax.showMovieInfo,
error: function() { alert('Error!'); }
});
return(false);
},
showMovieInfo: function(data) {
// center a floater 1/2 as wide and 1/4 as tall as screen
var oneFourth = Math.ceil($(window).width() / 4);
Ponemos el HTML en el
$('#movieInfo').
<div id="movieInfo">
html(data).
css({'left': oneFourth, 'width': 2*oneFourth, 'top': 150}).
show();
Modificamos sus propiedades CSS
// make the Close link in the hidden element work
dndole dimensiones relativas
$('#closeLink').click(RPAjax.hideMovieInfo);
al tamao de la ventana, y mostramos
return(false);
el <div id="movieInfo">
},
hideMovieInfo: function() {
Asociamos callback al enlace close de la ventana
$('#movieInfo').hide();
return(false);
},
}
$(RPAjax.setup);
51!

HTML devuelto en la vista


parcial _movie.html.haml

Ejemplo de uso de $.ajax()


El resultado!

Aadimos coordenadas absolutas, color, borde,:!


app/assets/stylesheets/app

#movieInfo {
padding: 2ex;
position: absolute;
border: 2px double grey;
background: wheat;
}
http://pastebin.com/0WdxM2vp

Y el resultado
mejora visualmente
(ahora parece flotante):!
GSyC!

52!

Depuracin de aplicaciones
AJAX/Rails!
Puedes arrancar firebug y poner puntos
de parada (breakpoints) en el cdigo
JavaScript!
click en el nmero de lnea en la que quieras poner/
quitar un breakpoint!

Y en el depurador de rails puedes poner


puntos de parada en el cdigo Rails. !
Recuerda:!
1. Arrancar rails server debug
2. Insertar llamada a debugger en la lnea en la que quieras
interrumpir la ejecucin!

GSyC!

53!

Otros usos de JavaScript!

Usos de JavaScript!
1. Client-side JavaScript: !

2.

Aplicaciones tipo desktop en el lado del cliente, como Google Docs!

3.

Cdigo JavaScript que corre en el browser, asociado a la pgina Web!


Se usa JavaScript para mejorar la experiencia de usuario de aplicaciones SaaS
que siguen el patrn arquitectnico MVC!
Se combina JavaScrpt con HTML y CSS!
Es importante que no se mezcle JavaScript con el cdigo HTML!
Es importante que el servicio est disponible para cliente no-JavaScript!
Pueden operar offline!
El cdigo JavaScript en el browser usa un servidor delgado, slo para almacenar
en la BD, recibiendo objetos JSON que convierte en HTML (en el browser)!
Se programan normalmente con frameworks, como Ember.js, Backbone.js o
Angular de Google que soporta el patrn MVC!

Aplicaciones SaaS pero con JavaScript en el servidor!

Frameworks como node.js y extensiones: meteor, express!

GSyC!

55!

Ejemplo: Ember.js!
Ember.js

vs

Rails

Rails

GSyC!

56!

Usos de JavaScript!
1. Client-side JavaScript: !

2.

Aplicaciones tipo desktop en el lado del cliente, como Google Docs!

3.

Cdigo JavaScript que corre en el browser, asociado a la pgina Web!


Se usa JavaScript para mejorar la experiencia de usuario de aplicaciones SaaS
que siguen el patrn arquitectnico MVC!
Se combina JavaScrpt con HTML y CSS!
Es importante que no se mezcle JavaScript con el cdigo HTML!
Es importante que el servicio est disponible para cliente no-JavaScript!
Pueden operar offline!
El cdigo JavaScript en el browser usa un servidor delgado, slo para almacenar
en la BD, recibiendo objetos JSON que convierte en HTML (en el browser)!
Se programan normalmente con frameworks, como Ember.js, Backbone.js o
Angular de Google que soporta el patrn MVC!

Aplicaciones SaaS pero con JavaScript en el servidor!

Frameworks como node.js y extensiones: meteor, express!

GSyC!

57!

Ejemplo:!

Biblioteca JavaScript para programar servidores!


En lugar de lanzar un nuevo proceso en el servidor para cada peticin como
hacen por omisin Rails/Django, las peticiones se encolan y son atendidas por un
solo thread!
Ventaja: mayor rendimiento (peticiones/s) al no haber coste en tiempo de
ejecucin para crear procesos ni para cambiar de contexto entre ellos!
Inconveniente: ms difcil de programar, pues las llamadas han de ser no
bloqueantes, teniendo que registrar callbacks!
Ejemplo de servidor HTTP programado con node.js:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Hay frameworks sobre node.js: meteor, express,!


Yahoo Mojito es un framework hbrido: permite elegir entre generar HTML en el
servidor (node.js) o en el browser (recibe JSON y convierte a HTML en el
browser)!
GSyC!

58!

Usos de JavaScript!
1. Client-side JavaScript: !

2.

Cdigo JavaScript que corre en el browser, asociado a la pgina Web!


Se usa JavaScript para mejorar la experiencia de usuario de aplicaciones SaaS
que
el patrn
MVC!
Y siguen
an hay
ms arquitectnico
usos de JavaScript:
Se combina JavaScrpt con HTML y CSS!
n los mviles
y tabletas,
para
desarrollar
sla vez
(en JavaScript)
EsEimportante
que no
se mezcle
JavaScript
conuna
el cdigo
HTML!
y desplegar/ejecutar sobre diversas plataformas mviles como
Es importante que el servicio est disponible para cliente no-JavaScript!
Android, iPhone, BlackBerry,

Aplicaciones tipo desktop en el lado del cliente, como Google Docs!

Pueden operar
offline!
Frameworks:
Phonegap
+ jQuery
El cdigo JavaScript
en el
browserMobile
usa un servidor delgado, slo para almacenar
Titanium
en la BD, recibiendo
objetos JSON que convierte en HTML (en el browser)!
normalmente con frameworks, como Ember.js, Backbone.js o

Se programan
Angular de Google que soporta el patrn MVC!
En los mviles y tabletas, como plataforma alternativa a
3. Aplicaciones
SaaS pero con
JavaScript en el servidor!
Google/Android,
Apple/iOS:

Frameworks
como
node.js
y extensiones: meteor, express!
Firefox
OS (Firefox)
Open WebOS (HP)

GSyC!

59!

Precauciones!

Rendimiento de
JavaScript/AJAX!
Aparentemente JavaScript+AJAX hace interfaces ms
interactivas, ms dinmicas!
Pero cuidado: !

Hay que descargar jQuery y los ficheros .js, lo que puede hacer que el
usuario perciba que la pgina tarda ms en cargar inicialmente!
Sobre todo en mviles!
El rendimiento de JavaScript puede no ser alto !

Por qu no programar casi todo en el cliente y dejar para el


servidor el acceso a la BD y poco ms, como hacen Ember,
Backbone,?!

No es evidente cul es la mejor solucin!


Hay mucha actividad en la actualidad!
El cdigo JavaScript en el cliente podra hacer queries demasiado complejas,
lo que puede evitarse con APIs bien pensadas!
Por otro lado, el rendimiento de la aplicacin pasa a depender de la
plataforma en la que corre el browser, lo que ocurre menos si la funcionalidad
est sobre todo implementada en el servidor!
GSyC!

61!

Referencias!
Captulo 11 de Engineering Long-Lasting Software.
Armando Fox, David Patterson, beta edition, agosto 2012. !
API de jQuery: http://api.jquery.com/!
Selectores CSS3: http://www.w3.org/TR/selectors/#selectors!
How JavaScript works: introduction to JavaScript and
Browser DOM. Miko Hevery.
http://misko.hevery.com/2010/07/14/how-javascript-works/!

jQuery: Novice to Ninja. Earle Castledine, Craig Sharkie.


2nd ed. Site Point 2012 (Disponible en Safari)!
JavaScript. The Definitive Guide: Activate Your Web Pages
6th ed.. David Flanagan. O'Reilly 2011. (Disponible en
Safari). !

GSyC!

62!

You might also like