function online(event) {
statusElem.className = navigator.onLine ? 'online' : 'offline';
statusElem.innerHTML = navigator.onLine ? 'online' : 'offline';
state.innerHTML += '<li>New event: ' + event.type + '</li>';
}
window.addEventListener('online', online);
window.addEventListener('offline', online);
Principle: inclure un fichier "manifeste" dans chaque page HTML qui doit être cachée.
<html manifest="myCache.appcache"/>
Nouveau type MIME :
AddType text/cache-manifest .appcache
CACHE MANIFEST
clock.html
clock.css
clock.js
Tiré de cet exemple de la doc W3C sur la cache API.
La première ligne (CACHE MANIFEST) est obligatoire,
Chaque fichier qui doit être caché doit être inclu individuellement (pas de wildcards),
La page HTML elle-même est inclue par défaut...
Quand un navigateur trouve un fichier manifeste, il ajoute chaque fichier listé dans le cache, alors, il prendra dorénavant ces fichiers dans le cache...
Sauf si le fichier manifeste côté serveur est mis à jour (date changée).
Bonne pratique : ajouter un timestamp / commentaire dans le manifeste à chaque update.
Imaginez un formulaire login / password , ne doit pas être caché ou affiché en mode offline.
Le manifeste a des directives pour celà :
Note: wildcards autorisées dans NETWORK et FALLBACK, pas avec la directive CACHE .
CACHE MANIFEST
CACHE:
#images
/images/image1.png
/images/image2.png
#pages
/pages/page1.html
/pages/page2.html
#CSS
/style/style.css
#scripts
/js/script.js
# / = any resource that is not available in the cache
FALLBACK:
/ /offline.html
NETWORK:
login.html
... NETWORK: * FALLBACK: /login.html /offline.html
Cliquer l'image suivante pour un exemple réel...
Habituellement : 5 megas pour toute persistence client-side, infinie si l'application vient du Chrome AppStore, du Windows Store,
Parfois configurable (Opera), une "quota API" est en route,
Ce pirate (par Andrés Lucero) surveille vos données !
Il est possible de mettre à jour le cache par programme :
var appCache = window.applicationCache;
appCache.update(); // Attempt to update the user's cache.
if (appCache.status == window.applicationCache.UPDATEREADY) {
appCache.swapCache(); // The fetch was successful, swap in the new cache.
}
L'API est très complète et propose d'autres événements : onerror, onupdate, ondownloading, onprogress, etc.
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the new hotness.
window.applicationCache.swapCache();
if (confirm('Une nouvelle version du site est disponible : mise à jour du cache.')) {
window.location.reload();
}
} else {
// Manifest didn't changed. Nothing new to server.
}
}, false);
}, false);
La quota API est déjà disponible (seul Chrome l'implémente complètement pour le moment)
Un exemple de démonstration sur jsbin.com
function showFreeSpace() {
//the type can be either TEMPORARY or PERSISTENT
webkitStorageInfo.queryUsageAndQuota(webkitStorageInfo.TEMPORARY, onSuccess,
onError);
}
function onSuccess(usedSpace, remainingSpace) {
var displaySpace = document.querySelector("#space");
displaySpace.innerHTML = "Used space = " + usedSpace + ", remaining space = "
+ remainingSpace;
}
function onError(error) {
console.log('Error', error);
}
Lire : Managing HTML5 Offline Storage and using the quota API sur le site des développeurs Google.
Lire : "Working with quota on mobile browsers", article de HTML5rocks.com
Ce formulaire sauvegarde son contenu automatiquement. Essayez de recharger cette page !
Remarque :
avec certains navigateurs, cette page doit être accédé par
http:// pas par file://
// store data
localStorage.firstName = "Michel";
// retrieve data
var firstName = localStorage.firstName;
<head>
<script>
// store data
localStorage.lastName = "Buffa";
localStorage.firstName = "Michel";
function getData() {
// retrieve data
document.querySelector("#lastName").innerHTML = localStorage.lastName;
document.querySelector("#firstName").innerHTML = localStorage.firstName;
}
</script>
</head>
<body onload="getData()">
<h1>Data retrieved from localStorage</h1>
<ul>
<li>Last name : <span id="lastName"></span></li>
<li>First name : <span id="firstName"></span></li>
</ul>
With Chrome dev tools:


<type=file ... />
Select one or more files: <input type="file" id="input">
...
var selectedFile = document.getElementById('input').files[0];
var name = selectedFile.name;
var size = selectedFile.size;
var type = selectedFile.type;
var date = selectedFile.lastModifiedDate;
...
<!DOCTYPE html>
<html>
<head>
<script>
function displayFirstSelectedFile() {
var selectedFile = document.getElementById('input').files[0];
document.querySelector("#singleName").innerHTML = selectedFile.name;
document.querySelector("#singleSize").innerHTML = selectedFile.size + " bytes";
document.querySelector("#singleType").innerHTML = selectedFile.type;
}
</script>
</head>
<body>
Select one or more files: <input type="file" id="input">
<br/>
<button onclick="displayFirstSelectedFile()">Click me to see details about the
selected files</button>
<ul>
<li>First selected file name is: <span id="singleName"></span></li>
<li>First selected file name is: <span id="singleSize"></span></li>
<li>First selected file name is: <span id="singleType"></span></li>
</ul>
</body>
</html>
L'interface FileReader :
FileReader reader = new FileReader();
3 methodes:
reader.readAsText(File, opt_encoding)
.reader.readAsDataURL(File) reader.readAsArrayBuffer(File) Un data URL est un URL qui inclut le contenu et le type en même temps. Voici un exemple de carré rouge, sous forme de data URL. Copiez et collez le dans la barre d'adresse d'un navigateur, vous devriez voir le carré rouge.
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
<img width=50 src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red square" />
var reader = new FileReader();
reader.onload = function(e) {
// Render thumbnail. e.target.result is the data URL of the image
var span = document.createElement('span');
span.innerHTML = "<img class='thumb' src='" + e.target.result + "'/>";
document.getElementById('list').insertBefore(span, null);
};
// Read in the image file as a data URL.
reader.readAsDataURL(f);
var reader = new FileReader();
//read the file content.
reader.onload = function(e) {
alert('Text file content :\n\n' + e.target.result);
};
// Read the file as text
reader.readAsText(f);
// User selects file, read it as an ArrayBuffer and pass to the API.
var fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', function(e) {
var reader = new FileReader();
reader.onload = function(e) {
initSound(this.result);
};
// THIS IS THE INTERESTING PART!
reader.readAsArrayBuffer(this.files[0]);
}, false);
Ce code aussi avec l'exemple précédent (lecture d'un fichier binaire) :
// Load sound sample from a URL with XHR2 as an ArrayBuffer.
function loadSoundFile(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer'; // THIS IS NEW WITH HTML5!
xhr.onload = function(e) {
initSound(this.response); // this.response is an ArrayBuffer
// a binary file, native format
};
xhr.send();
}
<input id="file" type="file" />
<script>
var fileInput = document.querySelector('#file');
fileInput.onchange = function() {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.html'); // With FormData, POST is mandatory
xhr.onload = function() {
alert('Upload complete!');
};
var form = new FormData();
form.append('file', fileInput.files[0]);
// send the request
xhr.send(form);
};
</script>
loaded et total de l'évènement passé à upload.onprogress.
<progress id="progress"></progress>
<script>
var fileInput = document.querySelector('#file'),
progress = document.querySelector('#progress');
fileInput.onchange = function() {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.html');
xhr.upload.onprogress = function(e) {
progress.value = e.loaded;
progress.max = e.total;
};
xhr.onload = function() {
alert('Upload complete!');
};
var form = new FormData();
form.append('file', fileInput.files[0]);
xhr.send(form);
};
</script>
Faites un drag sur un élément !
<ol ondragstart="dragStartHandler(event)">
<li draggable="true" data-value="fruit-apple">Apples</li>
<li draggable="true" data-value="fruit-orange">Oranges</li>
<li draggable="true" data-value="fruit-pear">Pears</li>
</ol>
function dragStartHandler(event) {
console.log('dragstart event, target: ' + event.target);
// Copy in the drag'n'drop clipboard the value of the data*
// attribute of the target, with a type "Fruit".
event.dataTransfer.setData("Fruit",
event.target.dataset.value);
}
<div ondragover="return false" ondrop="dropHandler(event);">
Drop your favorite fruits below:
<ol id="droppedFruits"></ol>
</div>
function dropHandler(event) {
console.log('drop event, target: ' + event.target);
...
// get the data from the drag'n'drop clipboard, with a
// type="Fruit"
var data = event.dataTransfer.getData("Fruit");
...
}
drag'n'droppez un élément !
dropEffect et effectAllowed
de event.dataTransfer.Valeurs possibles: copy, move, link, etc. ou changer l'apparence du curseur (image custom par ex.)
Pour applications en provenance des "stores" (Chrome Store ou Windows Store), ou accès concurrents aux données par de multiples Workers.
C'est un object store JS,
API asynchrone, pas très compliquée mais non triviale.
La plupart des exemples sur le Web sont obsolètes et ne fonctionnent pas.
Voyons quelques exemples qui fonctionnent !
WebSQL est abandonné: spécification plus en maintenance.
FileSystem et FileWriter APIs: uniquement dans Chrome pour le moment, La spécification est stable mais peut encore changer.
Plus de détails dans :
Lire why there is no planned filesystem APIs in Firefox.
Mais Eric Bilderman (Google Chrome) a écrit une librairie JavaScript qui émule ces APIs à 99%, avec IndexedDB.
Mozilla propose au W3C une FileHandle API à la place...
Microsoft fait tout avec des blobs dans IndexedDB
Alors... pour FileWriter et FileSystem API il est urgent d'attendre un peu.
Vous avez besoin de transactions, de faire des recherches dans de gros volumes de données, alors utilisez IndexedDB,
Vous devez simplement gérer des paires clé/valeur, utilisez localStorage/sessionStorage.
Applications offline, accès rapide : aujourd'hui, utilisez la cache API, demain Service Worker, ou mettez vous à IndexedDB.
Vous rêvez de faire du SQL , utilisez WebSQL. Oops, non, pardon, c'est mort WebSQL...