Transparencias de APIs estándar en Javascript

Anuncio
Tema 3, Programación en el cliente con Javascript
Parte III:
APIs estándar para la
Web
APIs estándar vs. librerías
• Los navegadores incorporan gran número de APIs estándar que
convierten a Javascript+HTML5+CSS en una potente plataforma
• Manipulación del HTML (DOM), Comunicación con el servidor (AJAX, websocketsen tiempo real-), Multithreading (web workers), Acceso a hardware
(geolocalización, batería), Media Capture API (captura de audio/video), Gráficos 2D/
3D, Bases de datos,...
• Es muy habitual el uso de librerías Javascript para simplificar la
programación y mejorar la productividad
• Ejemplos: jQuery, MooTools, YUI, ExtJS,...
• Razones:
• Algunos APIs estándar son complicados de usar
• Incompatibilidades con el estándar sobre todo con IE. Las librerías nos dan una
capa de abstracción que oculta estas incompatibilidades
• Hay algunas cosas que todavía no están en ningún API estándar (p.ej. widgets)
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Tema 3. Programación en el cliente con Javascript
Parte III: APIs estándar para la Web
1. Manejo del DOM
El DOM
• Como ya hemos visto, por cada etiqueta HTML existe un objeto
Javascript equivalente
• Es decir, el navegador mantiene en memoria un modelo orientado
a objetos del documento que refleja la estructura del HTML
• El DOM es un árbol. Cada “componente” del HTML es un nodo
• Los cambios en el DOM se reflejan en “tiempo real” en el HTML
<!DOCTYPE html>
<html>
<head>
<title>Ejemplo de DOM</title>
</head>
<body>
<!-- es un ejemplo un poco simple -->
<p style=“color:red”>Bienvenidos al <b>DOM</b></p>
</body>
</html>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Seleccionar nodos
• Por id
var noticias = document.getElementById("noticias")
• Por etiqueta
//Reducir el tamaño de todas las imágenes a la mitad
var imags = document.getElementsByTagName("img");
for(var i=0; i<imags.length; i++) {
imags[i].width /= 2;
imags[i].height /= 2;
}
• Usando selectores CSS
//Obtener el 1er nodo que cumple la condición
var primero = document.querySelector(".destacado");
//Obtenerlos todos
var nodos = document.querySelectorAll(".destacado");
//Cambiamos la clase. Nótese que es “className”, no “class”
for (var i=0; i<nodos.length; i++) {
nodos[i].className = "normal";
}
//selectores un poco más complicados
var camposTexto = document.querySelectorAll('input[type="text"]');
var filasPares = document. querySelectorAll("tr:nth-child(2n)")
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Modificar el HTML
• innerHTML: propiedad que nos permite leer/modificar el código
HTML que hay dentro de una etiqueta
• No es estándar en HTML4, pero sí en HTML5
• No se puede (debe) usar para editar tablas. Para eso existen
métodos alternativos (ver p.ej. http://msdn.microsoft.com/en-us/
library/ms532998(v=vs.85).aspx)
<input type="button" value="Pon texto" onclick="ponTexto()"/>
<div id="texto"></div>
<script type="text/javascript">
function ponTexto() {
var mensaje = prompt("Dame un texto y lo haré un párrafo")
var miDiv = document.getElementById("texto")
miDiv.innerHTML += "<p>" + mensaje + "</p>"
}
</script>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
API DOM estándar
• Muy potente, pero también algo tedioso de utilizar
• A diferencia de innerHTML, que permite manipular directamente el
texto, se manipulan los nodos del árbol, lo que a su vez “modifica”
el HTML
<input type="button" value="Añadir párrafo" id="boton"/>
<div id="texto"></div>
<script type="text/javascript">
document.getElementById("boton").onclick = function() {
var texto = prompt("Introduce un texto para convertirlo en párrafo");
/* Nótese que la etiqueta <p> es un nodo, y el texto que contiene es OTRO
nodo, de tipo textNode, hijo del nodo <p> */
var p = document.createElement("P");
var nodoTexto = document.createTextNode(texto);
p.appendChild(nodoTexto);
document.body.appendChild(p);
};
</script>
http://jsbin.com/Uwoduf/1/watch?html,js,output
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Tema 3. Programación en el cliente con Javascript
Parte III: APIs estándar para la Web
2. AJAX
AJAX
• Asynchronous Javascript And XML
• Combinación de tecnologías que permite:
• Hacer peticiones al servidor con javascript y recibir la respuesta sin recargar la
página ni cambiar de página
• Parsear la respuesta y convertirla en objetos Javascript
• Con esta información y el DOM se puede actualizar solo parte de la página con
datos procedentes del servidor
• El API existe desde hace bastantes años tanto en IE como en
Firefox, pero lo “pusieron de moda” en Google (suggest, Gmail,…)
• Se convirtió en una de las características distintivas de las aplicaciones “web 2.0”
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Esquema AJAX síncrono
 Función que hace AJAX
Ž Petición HTTP
XMLHttpRequest
 Respuesta HTTP
 Cambios en el HTML
con innerHTML o equiv.
Javascript
Servidor web
Œ Evento que
dispara AJAX
Área dentro del HTML
HTML
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Código síncrono
• ACLARACION: El AJAX normalmente es asíncrono (¿si no, qué
significa la primera “A” del acrónimo?), aunque veremos primero este
caso por ser más simple
• Paso 1: Crear el objeto XMLHttpRequest
• Paso 2: Realizar la petición
• Método open: Prepararla (diferente según sea GET/POST y síncrona/asíncrona). En
caso de ser GET los parámetros van aquí
• Método send: Enviarla (en caso de ser POST los parámetros van aquí)
• Paso 3: Procesar la respuesta
• La propiedad status del objeto XMLHttpRequest contiene el código de estado del
servidor (en HTTP, OK es el 200)
• La respuesta del servidor la tenemos en la propiedad responseText
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Ejemplo de código síncrono
• Supongamos que en el servidor esperan un parámetro “cod” con un valor
que tenemos en la variable Javascript “codigo”.
var req = new XMLHttpRequest();
//preparar la petición. El tercer parámetro indica que no es asíncrona req.open('GET', 'http://www.miservidor.com/miprograma.php?cod=' + codigo, false);
//Enviar la petición. Con GET el argumento siempre es null //Aquí se bloquearía hasta que llegue la respuesta del servidor
req.send(null); //comprobar el código de estado
if(req.status == 200)
//responseText es la respuesta del servidor.
//Normalmente haremos algo más útil con lo que devuelve el servidor que un alert
alert(req.responseText)
versión con GET
//En la versión con POST sería todo igual excepto “open” y “send”
//con POST los argumentos no van en la URL, sino en el “send”
…
req.open('POST', 'http://www.miservidor.com/miprograma.php', false);
req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
req.send('cod='+codigo);
versión con POST
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Esquema AJAX
 Función que hace AJAX
Ž Petición HTTP
XMLHttpRequest
Función “Callback”
 Cambios en el HTML
con innerHTML o equiv.
 Respuesta HTTP
Servidor web
Javascript
Œ Evento que
dispara AJAX
Área dentro del HTML
HTML
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Código asíncrono
• El Javascript puede seguir haciendo “otras cosas” mientras se recibe la
respuesta del servidor.
• En el caso síncrono el usuario podría pensar que el navegador se ha bloqueado o se ha
colgado el script, si la respuesta del servidor tarda mucho
• Hay que poner el tercer parámetro del método open a true (la petición sí
es asíncrona)
• El servidor nos avisará “llamando a la función que le designemos” (función
“callback”)
• En la propiedad onreadystatechange del XMLHttpRequest “apuntaremos” a la función
• El callback no se llamará una sola vez, sino varias. El servidor nos informa en la propiedad
readyState de XMLHttpRequest de si la respuesta ha empezado a llegar (solo cabeceras
HTTP: readyState==2), está cargándose (readyState==3) o completa (readyState==4).
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Ejemplo de código asíncrono
• Supongamos que en el servidor esperan un parámetro “cod” con un
valor numérico que tenemos en la variable Javascript “codigo”.
var req = new XMLHttpRequest();
//preparar la petición. El tercer parámetro indica que sí es asíncrona req.open('GET', 'http://www.miservidor.com/miprograma.php?cod=' +codigo, true);
//decir qué función hace de “callback”. Esto no se debe hacer antes del open
req.onreadystatechange = mi_callback;
req.send(null);
//ya podemos seguir con otras cosas, este send no se bloquea
//en algún sitio del javascript debe estar definida esta función
function mi_callback() {
if (req.readyState == 4) { //también valdría this.readyState, aunque esto no parece if(req.status == 200) //estar documentado (idem this.status, this.responseText)
alert(req.responseText); }
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
responseText
• responseText es la información que nos envía el servidor, en un
String.
• El servidor nos tendrá que enviar la información en un formato
adecuado que podamos parsear desde Javascript
• Por ejemplo, en un chat nos podría mandar los nuevos mensajes recibidos
simplemente poniendo un mensaje en cada línea y separando sus datos (hora,
usuario, texto) con algún carácter especial
• Ya veremos cómo se hace desde el servidor para generar esta información, por el
momento supongamos que lo hace
10:00:05#pepito#”hola a todos…”
10:00:15#jorgito#”hola pepito, cuánto tiempo sin saber de
ti! :)”
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
¿Pero dónde está XML?
• En la idea de AJAX original, la información se enviaría en XML,
que permite estructurar los datos de manera más “elegante” que un
formato “casero” ad-hoc
<chat>
<mensaje>
<hora>10:00:05</hora>
<login>Pepito</login>
<texto>Hola a todos…</texto>
</mensaje>
…
</chat>
• Problema: aunque el API DOM de Javascript permite parsear XML,
resulta tedioso de usar
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
JSON y AJAX
• AJAX es mucho más fácil de tratar que XML, gracias a
JSON.parse o a eval
[
{"hora":"10:00:05", "login":"pepito", "texto":"hola a todos…"},
{"hora":"10:00:15", "login":"jorgito", "texto":"hola pepito, cuánto
tiempo sin saber de ti! :)"}
]
mensajes = JSON.parse(req.responseText)
for(i=0; i<mensajes.length; i++) {
cout.log("A las " + mensajes[i].hora + " " + mensajes[i].login +
" dijo: " + mensajes[i].texto);
}
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Restricciones de seguridad
• Política de seguridad del “mismo origen”: un XMLHttpRequest
solo puede hacer una petición AJAX al mismo host del que vino la
página en la que está definido
• Resumiendo, una página que tenéis en localhost y que tiene Javascript no puede
hacer peticiones AJAX a Google, por ejemplo
• El “cross-domain AJAX” (CORS) permite romper esta política bajo
ciertas circunstancias
• Si el servidor al que le haces la petición la permite (enviando la cabecera AccessControl-Allow-Origin), el navegador también dejará que se haga
HTTP/1.1 200 OK Server: Apache/2.0.61 Access-Control-Allow-Origin: * Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
AJAX “nivel 2”
• “Versión 2.0” de XMLHttpRequest que permite
• Intercambiar datos binarios con el servidor. Por ejemplo, esto puede servir para
subir imágenes o archivos en general
• Acceder a ciertos eventos, por ejemplo para ir monitorizando el progreso en el
envío/recepción de datos
• Solo funciona en navegadores modernos (aunque no en IE9,
consultar caniuse.com u otro recurso equivalente
• API
• FormData representa los campos de un formulario, incluyendo type=”file”.
Enviando el FormData enviamos también el archivo
• Hay varios eventos como “progress”, “load”,”error” o “abort”
• La gestión de los eventos se hace con el estándar W3C de event listeners, sobre el
objeto XMLHttpRequest
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Ejemplo de AJAX Level 2
<script>
function verProgreso(e) {
var progreso= document.getElementById("progreso");
progreso.innerHTML = Math.round((e.loaded / e.total)*100)+"%";
}
function uploadAJAX() {
var fdata = new FormData(document.getElementById("formu"))
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", verProgreso, false)
xhr.open("POST", "http://loquesea.com/upload", true)
xhr.onreadystatechange = function() {
if (this.readyState==4)
alert(this.responseText)}
xhr.send(fdata)
}
</script>
...
<form enctype="multipart/form-data" id="formu">
Elegir archivo: <input type="file" name="archivo"/> <br/>
<input type="button" value="enviar" onclick="uploadAJAX()"/>
</form>
<div id="progreso"></div>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Tema 3. Programación en el cliente con Javascript
Parte III: APIs estándar para la Web
3. Animaciones
Cambiar estilo dinámicamente
• Recordar que en el atributo style de cualquier etiqueta se pueden
especificar propiedades CSS
• La propiedad Javascript equivalente style es un objeto cuyas
propiedades son equivalentes a propiedades CSS
• Los “-” de las propiedades CSS se sustituyen por su versión camelCase. Por ejemplo
la propiedad CSS background-color se convierte en backgroundColor
• Los valores leídos se obtienen como Strings aunque sean números
http://jsbin.com/aJoWaPI/2/watch?html,output
<p id="texto" style="font-size:12px">Hola</p>
<input type="button" value="Aumentar" id="boton">
<script>
function aumentar(idElemento) {
elemento = document.getElementById(idElemento)
return function() {
elemento.style.fontSize =
parseInt(elemento.style.fontSize) + 2 + "px";
}
}
document.getElementById("boton").onclick = aumentar('texto')
</script>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Animaciones
• Por desgracia, el siguiente código no funcionará nada bien
• No se ven los pasos intermedios ¿por qué? (pista: no es que la animación vaya
demasiado rápido)
<p id="texto" style="font-size:1px">Hola</p>
<input type="button" value="Animar" id="boton">
<script>
function animar(idElemento) {
elemento = document.getElementById(idElemento)
return function() {
//Vamos incrementando el tamaño del texto de 2 a 100
for(i=2;i<100;i++) {
elemento.style.fontSize = i + “px”;
}
}
}
document.getElementById("boton").onclick = animar('texto')
</script>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Solución: temporizadores
• Permiten ejecutar código transcurrido un cierto intervalo de tiempo.
Mientras Javascript no está ejecutándose se puede actualizar la
pantalla
• Tipos
• timeout: 1 sola vez, pasados X ms
• interval: indefinidamente, cada X ms
• Animación:
• Implementamos una función que ejecute un solo “frame” de la animación
• Cada X ms (tiempo entre frames) llamamos a esta función (con un interval). O bien
la llamamos transcurridos X ms y hacemos que se vuelva a llamar a sí misma de
nuevo transcurridos X ms (con un timeout)
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Animación con temporizadores
• Implementamos una función que ejecute un solo “frame” de la
animación
• Animación en sí
• Cada X ms (tiempo entre frames) llamamos a esta función (con un interval).
• O bien la llamamos transcurridos X ms y hacemos que se vuelva a llamar a sí
misma de nuevo transcurridos X ms (con un timeout)
<div id="texto" style="position:relative">Hola</div>
<script>
function animar(id){
var elemento = document.getElementById(id);
var pos = 0;
var timer = setInterval(function() {
if (pos < 200) {
elemento.style.left = pos + "px";
pos++;
}
else
clearInterval(timer);
}, 20);
}
animar('texto')
</script>
http://jsbin.com/IHOVUqA/2/watch?html,js,output
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Problemas de los temporizadores
• Resolución limitada
• En el propio estándar HTML5 se
especifican 4 ms
• No hay multithreading
• Cuando se dispara un timeout si el
navegador está ocupado lo pondrá
“en cola”
• 2 interval se pueden acumular y
ejecutarse juntos, si cuando se está
ejecutando uno se dispara el
siguiente.
• El navegador intentará no poner en la cola
más de 1 vez el mismo interval para evitar que
se acumulen demasiados.
• Solución: requestAnimationFrame
http://ejohn.org/blog/how-javascript-timers-work/
Ver por ejemplo: http://msdn.microsoft.com/es-ES/library/ie/hh920765.aspx
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Transiciones CSS
• En CSS3 se pueden hacer animaciones simples, cambiando
gradualmente el valor de alguna/s propiedad/es
• Todavía está en proceso de estandarización. Solo funciona en versiones recientes
de Chrome/Safari, Opera y Firefox (4.0)
• (!) En cada navegador estas propiedades CSS tienen un nombre distinto
• Ventaja: no tienen problemas de temporización (¡suaaaaves! :) ) y
están aceleradas por hardware (OK en móviles).
• Limitación: solo para efectos sencillos
p {
background-color:white;
}
/* cuando se pase el ratón por encima, el color de fondo cambiará gradualmente a
amarillo, en 2 segundos */
p:hover {
background-color: yellow;
-webkit-transition: background-color 2s; /* sintaxis chrome/safari */
}
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Mientras el estándar se
“estabiliza”...
• Cada navegador usa sus propios nombres de propiedades, así
aunque el estándar cambiara se nos asegura que la implementación
propia de cada navegador no lo hará
• Pero eso nos fuerza a usar la sintaxis “oficial” más la de cada uno de
los navegadores por separado
p {
background-color:white;
}
p:hover {
background-color: yellow;
-webkit-transition: background-color 2s;
-moz-transition: background-color 2s;
-o-transition: background-color 2s;
transition: background-color 2s;
}
/*
/*
/*
/*
Chrome/safari */
Firefox */
Opera */
estándar */
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Mezclando CSS3 con Javascript
• Usando las propiedades Javascript equivalentes a las de CSS
<div id="box" style="background-color:yellow; width:500px;
height:100px; overflow:hidden;">
Esto debería encogerse hasta desaparecer al hacer click
</div>
<input type="button" value="animar" onclick="transicion()">
<script type="text/javascript">
function transicion() {
var box = document.getElementById('box');
//en CSS se pueden transicionar varias propiedades
//simultáneamente, separándolas por comas
box.style.webkitTransition = 'width 3s, height 3s';
box.style.width = '0px';
box.style.height = '0px';
}
</script>
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Tema 3. Programación en el cliente con Javascript
Parte III: APIs estándar para la Web
4. Local Storage
Local Storage API
• Permite compartir variables entre páginas
• Características:
• Se almacenan pares “clave=valor”
• Aunque la especificación no restringe el tipo para el valor, por el momento todos los
navegadores lo almacenan como String
• Esto quiere decir que al recuperarlo tendremos que convertirlo al tipo original.
• Un enfoque muy típico es usar JSON, así podemos guardar objetos
• Convertir de objeto a cadena: JSON.stringify(objeto)
• de cadena a objeto: JSON.parse(cadena)
• Hay dos tipos de almacenamiento
• Objeto localStorage: el ámbito del dato es el sitio web. Se conserva aunque se
cierre el navegador
• Objeto sessionStorage: el ámbito es la ventana (o solapa, si tenemos varias
abiertas). Se conserva hasta que ésta se cierre
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
API básico de Local Storage
• getItem(clave)
• setItem(clave,valor)
• length: propiedad de solo lectura que indica cuántos pares clave/
valor hay almacenados
• key(i): devuelve el nombre de la clave i-ésima (para poder
recuperar su valor con getItem)
• clear(): eliminar todos los datos
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Ejemplo
function guardarNombre() {
nombre = prompt("¿cómo te llamas?")
localStorage.setItem("usuario", nombre)
//esta sintaxis es equivalente a lo anterior
localStorage.usuario = nombre
//y esta también
localStorage["usuario"] = nombre
edad = prompt("¿Cuántos años tienes?")
localStorage.setItem("edad", edad)
}
function mostrarNombre() {
alert("Me acuerdo de ti, " + localStorage.usuario +
" vas a cumplir " + parseInt(localStorage.edad) + 1 !! +"años")
}
function mostrarTodosLosDatos() {
datos=""
for(var i=0; i<localStorage.length; i++) {
clave = localStorage.key(i)
datos = datos + clave + "=" + localStorage[clave] + '\n'
}
alert("LocalStorage contiene " + datos)
}
Aplicaciones Distribuídas en Internet 2013-­‐14 / Univ. Alicante
Descargar