Académique Documents
Professionnel Documents
Culture Documents
Web storage
Introduccin
Tradicionalmente, la nica forma que tenamos, como desarrolladores, de guardar
informacin en el ordenador del cliente era a travs de cookies. Sin embargo, las
cookies tienen mala fama y algunas limitaciones:
Almacenamiento local, con esta tcnica los datos son permanentes y se pueden
compartir entre todos los documentos procedentes de mismo origen
(protocolo+dominio+puerto)
Nota: Los datos almacenados en el cliente son visibles para ste; por ejemplo, en Firebug puede verlos a
travs del panel DOM desactivando la opcin Mostrar slo propiedades propias (si estuviera
activada).
El objeto Storage
La nica propiedad de este objeto es length, que indica cuntos pares nombre/valor
hay en el almacen.
Podemos acceder a los elementos del almacn mediante la sintaxis de corchetes
indicando el nombre del par correspondiente (no el ndice). No obstante, tambin
disponemos de los siguientes mtodos:
key(ndice): Contiene el nombre del par que ocupa la posicin ndice dentro del
almacn.
El evento storage
Cada vez que se produce una modificacin en un almacn se genera un evento de tipo
storage, que en el caso del almacenamiento local se comunica a todas las ventanas
del navegador que contienen documentos procedentes de ese mismo origen, y que en el
caso del almacenamiento de sesin se comunica slo a los documentos de ese origen
cargados en la misma ventana que el que provoc el cambio.
Este evento se asigna al objeto window, y su funcin oyente recibe como argumento un
objeto de tipo Event ampliado con las siguientes propiedades:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
window.addEventListener('storage',mostrar);
function mostrar(evento){
var mensaje = '<p><strong>Nombre:</strong>
'+evento.key;
mensaje += '<strong> Valor actual:</strong>
'+evento.newValue;
mensaje += '<strong> Valor anterior:</strong>
'+evento.newValue;
if(evento.storageArea == window.localStorage){
mensaje += '<strong> Almacén:</strong>
local</p>';
}else{
mensaje += '<strong> Almacén:</strong>
sesión</p>';
}
document.body.insertAdjacentHTML('beforeend',mensaje);
}
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
function almacenar(){
var nombre=document.getElementById('nombre').value;
var valor=document.getElementById('valor').value;
window.localStorage.setItem(nombre,valor);
insertar(nombre,valor);
}
function insertar(nombre,valor){
var mensaje = '<p><strong>Nombre:</strong> '+ nombre;
mensaje += '<strong> Valor actual:</strong> '+ valor +'</p>';
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
document.body.insertAdjacentHTML('beforeend',mensaje);
}
document.addEventListener('readystatechange',cargarDatos);
function cargarDatos(evento){
if(document.readyState == 'complete'){
var i;
for(i=0;i<window.localStorage.length;i++){
insertar(window.localStorage.key(i),window.localStorage[window.localStorag
e.key(i)]);
}
}
}
</script>
</head>
<body>
<label for='nombre'>Nombre: </label>
<input type='text' id='nombre' />
<br />
<label for='valor'>Valor: </label>
<input type='text' id='valor' />
<br />
<button onclick='almacenar();'>Almacenar</button>
<br />
<button
onclick='window.localStorage.clear();insertar("BORRADO","TOTAL");'>Borrar
almacén</button>
</body>
</html>
1. Abra cada uno de los documentos anteriores en una pestaa del navegador y
compruebe que al introducir pares en indexLocal.html o borrar el almacn, los
cambios se reflejan en supervisor.html.
2. Cree varios pares, cierre estas dos pestaas de navegacin y vuelva a abrir otra
vez indexLocal.html. Comprobar que recupera los pares del almacn y los
muestra debajo del formulario.
3. A continuacin vamos a crear otro documento llamado indexSesion.html que
realice la misma operacin pero sobre el almacn de sesin y que contenga un
iFrame asociado al documento supervisor.html. Su cdigo sera el siguiente:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
function almacenar(){
var nombre=document.getElementById('nombre').value;
var valor=document.getElementById('valor').value;
window.sessionStorage.setItem(nombre,valor);
insertar(nombre,valor);
}
function insertar(nombre,valor){
var mensaje = '<p><strong>Nombre:</strong> '+ nombre;
mensaje += '<strong> Valor actual:</strong> '+ valor +'</p>';
document.body.insertAdjacentHTML('beforeend',mensaje);
}
document.addEventListener('readystatechange',cargarDatos);
function cargarDatos(evento){
024
025
026
027
if(document.readyState == 'complete'){
var i;
for(i=0;i<window.sessionStorage.length;i++){
insertar(window.sessionStorage.key(i),window.sessionStorage[window.session
Storage.key(i)]);
}
}
}
</script>
</head>
<body>
<label for='nombre'>Nombre: </label>
<input type='text' id='nombre' />
<br />
<label for='valor'>Valor: </label>
<input type='text' id='valor' />
<br />
<button onclick='almacenar();'>Almacenar</button>
<br />
<button
onclick='window.sessionStorage.clear();insertar("BORRADO","TOTAL");'>Borrar
almacén</button>
<br />
<iframe width="100%" src="supervisor.html"></iframe>
</body>
</html>
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
IndexedDB
Actualmente existen dos APIs dentro de HTML5 para manejar bases de datos en el lado
del cliente:
Web SQL: El desarrollo de esta API ha sido abandonado por el W3C, por lo
que su uso est desaconsejado. Sin embargo, algunos navegadores (como
Chrome, Safari, Opera y los navegadores para mviles) an lo soportan en sus
ltimas versiones. El motivo del abandono fue que Web SQL se basa
exclusivamente en SQLite, y esta aplicacin utiliza una variante del
lenguaje SQL que no cumple exactamente su estndar.
El procedimiento general para utilizar una base de datos IndexedDB consiste en:
1. Abrir la base de datos.
2. Si es la primera vez que abrimos la base de datos o es una versin nueva
tendremos que definir los objetos IDBObjectStore y los ndices que conforman
su estructura. Estos elementos no se podrn modificar a posteriori a menos, claro
est, que creemos una versin nueva de la base de datos.
3. Definir una transaccin para indicar sobre qu objetos IDBObjectStore
queremos actuar y cmo deseamos hacerlo (slo lectura, lectura y escritura, ).
4. Elegir el IDBObjectStore de la transaccin sobre el que queremos realizar una
peticin.
5. Realizar peticiones sobre el IDBObjectStore identificado en la transaccin, o
bien sobre cualquiera de sus ndices, o bien sobre sus cursores (que pueden
crearse a posteriori, tanto directamente sobre los propios IDBObjectStore como
sobre sus ndices).
La primera vez que intentemos abrir una base de datos se solicitar permiso al usuario y,
si no nos lo concede, se generar un evento de tipo error.
La versin debe ser un nmero entero. Si intentamos abrir una versin ms antigua que
la disponible en el ordenador del usuario se generarn un evento de tipo error y un
error de tipo VersionError; por el contrario, si es la primera vez que abrimos la
base de datos o queremos abrir una versin ms reciente que la disponible en el
ordenador del usuario se generar un evento de tipo upgradeneeded. Tras abrir la
base de datos se generar un evento de tipo success.
En el oyente de los eventos upgradeneeded y success, la referencia a la base de
datos est disponible a travs de la propiedad result del blanco del evento, es decir,
en evento.target.result.
Por ejemplo:
001
002
003
004
005
006
007
function abrirBaseDeDatos() {
var peticion = indexedDB.open('Notas');
peticion.addEventListener('upgradeneeded', actualizarBaseDeDatos);
peticion.addEventListener('error', function(){console.log('Error al abrir BD');});
peticion.addEventListener('success', exitoAbrir);
}
function exitoAbrir(evento) {
008
009
baseDeDatos = evento.target.result;
}
Nota: El objeto base de datos (IDBDatabase) posee la propiedad version, que nos indica cul es la
versin de la base de datos que hemos abierto. Si no indicamos una versin en el mtodo open se
abrir la ms reciente disponible en el ordenador del cliente y, si no existe ninguna, se crear con
el nmero de versin 1.
Otro mtodo importante del objeto indexedDB es deleteDatabase, que sirve para
solicitar que se borre una base de datos. Si esta solicitud consigue realizarse
correctamente se generar un evento success sobre ella. Para poder borrar una base
de datos es necesario que no tengamos ningna conexin con ella abierta (el mtodo
close() aplicado sobre una conexin a una base de datos sirve para cerrarla).
Por ejemplo:
001
002
003
004
005
006
007
008
009
010
011
012
function borrarBaseDeDatos() {
if(baseDeDatos != null){
//Para poder borrar debe estar cerrada
baseDeDatos.close();
}
var peticion = indexedDB.deleteDatabase('Notas');
peticion.addEventListener('success', exitoBorrar);
peticion.addEventListener('error', function(){console.log('Error borrando
BD');});
}
function exitoBorrar(){
console.log('Base de datos borrada');
}
"id",
autoIncrement:
La clave no tiene por qu ser numrica, pero s necesariamente nica para cada par (no
est permitido crear en un mismo IDBObjectStore dos pares con la misma clave). Si nos
interesa, tambin podemos indicar como clave cualquiera de las propiedades de los
objetos que queramos almacenar en el IDBObjectStore (siempre que sea nica para cada
objeto); por ejemplo, si disponemos de objetos con las propiedades dni, nombre y
telfono se podra elegir dni como valor de la propiedad keyPath.
La creacin de ndices se realiza a travs del mtodo createIndex de los
IDBObjectStore, que recibe 3 argumentos: el nombre que queremos asignar al ndice, el
nombre de la propiedad que deseamos usar como criterio del ndice, y un objeto en el
que se configuran las propiedades del ndice (por ejemplo, si debe ser nico (unique),
es decir, que no se admitan en el IDBObjectStore dos pares que compartan el mismo
valor en la propiedad declarada como criterio del ndice). Por ejemplo:
objetoStore.createIndex('titulo','titulo', {unique: false});
Otra propiedad muy til de los ndices es multiEntry; imagine que elegimos como
criterio del ndice una propiedad que es de tipo array, si configuramos multiEntry
como false se crear en el ndice una sola entrada para cada par, pero si lo
configuramos como true se crear una entrada para cada valor del array de cada par.
Al abrir una versin de la base de datos ms reciente que la disponible en el ordenador
del usuario cliente ya sabemos que se genera un evento upgradeneeded, pero es
esencial entender que en la nueva versin se importarn los IDBObjectStore e ndices
que pudiera contener la versin anterior, de modo que si deseamos introducir
modificaciones en ellos tendremos que borrarlos y volver a crearlos (en el oyente del
evento upgradeneeded), pero PRECAUCIN!: al borrar un IDBObjectStore se
eliminan tambin todos los pares que contiente. Los mtodos que permiten borrar
IDBObjectStores e ndices son:
Transacciones
Una transaccin es un conjunto de operaciones (peticiones o IDBRequest) de acceso o
modificacin de datos con las siguientes caractersticas:
Nota: Si una transaccin readwrite contiene varias peticiones concatenadas, los cambios no se
introducirn efectivamente en el objectStore hasta que se llegue a un oyente de evento success en
el que no se lance otra nueva peticin. En otras palabras: los cambios no se producen hasta que
no se culminan todas las peticiones de la transaccin.
Nota: Los eventos success no se propagan, pero los error s, pasando de la peticin, a la transaccin
y, por ltimo, al propio objeto de base de datos. Esto puede resultar muy til para definir un nico
oyente de evento error asociado a la base de datos y no tener que hacerlo individualmente en
cada peticin.
Para declarar una transaccin debemos utilizar el mtodo transaction() del objeto
base de datos, al que pasaremos como argumentos un array con los nombres de los
objetos IDBObjectStore sobre los que queremos actuar (o sencillamente una cadena con
10
el nombre en caso de que queramos actuar slo sobre uno), y una cadena indicando el
modo ('readonly' o 'readwrite'). Por ejemplo:
var transaccion = baseDeDatos.transaction(['nota'],'readonly');
Peticiones
Una vez declarada la transaccin y elegido el IDBObjectStore sobre el que queremos
actuar podremos realizar peticiones de lectura y escritura sobre el propio
IDBObjectStore, o slo de lectura sobre sus ndices, o abrir cursores en cualquiera de
ellos para leer rangos de pares (no escribirlos). Estas peticiones se realizarn de forma
asncrona, de modo que tendremos que asignarles oyentes de eventos success y/o
error
para
conocer
su
resultado
(a
travs
de
la
propiedad
evento.target.result).
El objeto IDBObjectStore nos ofrece los siguientes mtodos para realizar peticiones
sobre l:
001
002
003
004
clear(): Inicia una peticin que elimina todos los pares del IDBObjectStore y
todos sus ndices.
crear rangos de claves. Al concluir con xito la peticin podremos saber cuntos
pares son a travs de la propiedad result de la peticin
(evento.target.result).
ndices
Los ndices se crean sobre los IDBObjectStores para facilitarnos el acceso (slo lectura)
a los pares usando como criterio una propiedad distinta a su clave. Recuerde que los
ndices slo se pueden crear en el oyente del evento upgradeneeded.
Para hacer uso de un ndice tendremos que obtener una referencia a l a travs del
mtodo index(nombreDelIndice) del objeto IDBObjectStore (recuerde que la
referencia al objeto IDBObjectStore la obtenemos a su vez a travs del mtodo
objectStore(nombre) de la transaccin.
A travs de la referencia del ndice podremos ejecutar sus mtodos get() y count(),
que funcionan exactamente igual que en el objeto IDBObjectStore explicado en la
seccin anterior.
Rangos y cursores
Un rango es el conjunto de pares de un IDBObjectStores cuyas claves se encuentran
comprendidas dentro del intervalo que definamos, y un cursor es un objeto que nos
permite recorrer los pares que componen el rango.
Los rangos pueden ser de 4 tipos (coincidencia exacta, mayor que, menor que, o
comprendido entre) y se crean mediante los siguientes mtodos del objeto
IDBKeyRange:
rango de tipo mayor que, en el que los pares debern tener una clave mayor si el
lmite es abierto, o mayor o igual que si el lmite es cerrado, que la especificada
para ser incluidos en el cursor.
IDBKeyRange.bound(claveInferior,
claveSuperior,[abiertoInferior],
[abiertoSuperior]): Define un rango de tipo comprendido entre los valores
claveInferior y claveSuperior.
Tanto los objetos IDBObjectStore como sus ndices nos ofrecen el mtodo
openCursor(rango,[direccin]), que inicia una peticin para acceder (leer,
escribir y borrar) al primer par cuya clave pertenezca al rango que especifiquemos, es
decir, colocar el cursor sobre l.
Pese a que openCursor() simplemente coloca el puntero sobre el primer par, los
cursores estn diseados para facilitar la iteracin sobre conjuntos de pares. Cualquier
operacin que realicemos sobre el cursor (sus mtodos se explican a continuacin)
generar un evento (success o error) sobre la peticin. En otras palabras: los
oyentes de eventos success y error de la peticin devuelta por openCursor()
servirn tambin para atender las operaciones que realicemos sobre el cursor.
Si el cursor se aplica a un IDBObjectStore, el rango deber estar definido sobre la clave
principal, es decir, sobre el KeyPath; por el contrario, si el cursor se aplica sobre un
ndice, el rango deber estar definido sobre el criterio (o clave local) de ese ndice.
El argumento direccin del mtodo openCursor() admite los siguientes valores:
"next": Coloca el cursor en el par del rango cuya clave es menor e itera en
sentido creciente de par en par.
"prev": Coloca el cursor en el par del rango cuya clave es mayor e itera en
sentido decreciente de par en par.
13
001
002
003
004
var
var
var
var
transaccion = baseDeDatos.transaction(['nota'],'readonly');
objetoStore = transaccion.objectStore('nota');
rango=IDBKeyRange.bound('Desde','Hasta',false,false);
peticion = objetoStore.index('titulo').openCursor(rango,'nextunique');
key: Contiene la clave local del par sobre el que se encutra actualmente el
cursor. Si el cursor se ha abierto sobre un IDBObjectStore, la clave local
coincidir con la principal, es decir, con la configurada en el keyPath.
delete(): Inicia una peticin que elimina el par sobre el que se encuentra el
cursor. El cursor no cambiar de posicin a menos que a continuacin
ejecutemos el mtodo continue o advance.
Por ejemplo:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
14