Autant le dire tout de suite, ce genre de technique ne marchera pas pour IE6-7-8 mais pour le tiers restant du marché. En faisant un petit effort, IE8 peut être supporté, ce qui amènerait la couverture du marché de cette technique à 50%.

Certains sites peuvent avoir besoin de charger de très grosses images :

  • une galerie qui affiche les originaux ou de très grandes photos
  • un site de manipulation de photo qui joue avec l'original
  • ou plus bêtement une petite connection

En HTML il n’y a pas 36 solutions, on demande au browser via un tag <img> de charger les données ET de les afficher. N’importe quelle technique en JS passe par ce même tag pour aller chercher les données.

<img src="/path/to/img">

ou

var oImage = document.createElement('IMG');
oImage.src = '/path/to/img';

Si vous vouliez afficher un loading, vous mettiez éventuellement un spinner en background-image de l’élément, mais vous ne pouviez pas indiquer le temps de téléchargement de l’image. Une combinaison de plusieurs techniques peut cependant le permettre :

  • XHR (AJAX) pour aller chercher les données. Rien de neuf ici, seul IE6 ne le supporte pas.
  • l'event onprogress de XHR : c'est la nouveauté supportée par Chrome, Safari et FF3+. Il existe aussi dans un autre objet pour IE8, mais l'utilisation de l'objet XDomainRequest est un peu contraignante
  • la technique des images inline, qui utilise les uri et base64. Non supporté par IE6 et IE7.

On commence par la détection du support de la feature, qui va nous permettre d’afficher un spinner traditionnel en fallback :

function checkSupport() {
var oEl = new XMLHttpRequest();
if(!oEl) // if standard XHR does not exist, we're probably on IE6 that do not support event progress anyway
return false;
return ('onprogress' in oEl);
}

On crée notre connection XHR en écoutant l’event onprogress, et en calculant le pourcentage de téléchargement :

var xhr = new XMLHttpRequest();
xhr.onprogress = function( e ) {
if (e.lengthComputable) {
var iProgress = (e.loaded / e.total);
// Ici mise à jour de l'interface
}
};

Attention car l’event onprogress est appellé très souvent selon les browsers, donc si nécessaire il faut temporiser les updates de l’interface

Lorsque toute l’image est téléchargée côté client, on met à jour le src de notre image :

xhr.onload = function( e ) {
// ici le code pour arretter la barre de progression
oImage.src = 'data:image/jpg;base64,'+e.target.responseText;
};
// les listeners sont posés, on peut lancer la requete
xhr.open('GET', '/path/to/image?base64', true);
xhr.send();

Server side, il faut bien penser à encoder en base64 le contenu du fichier lorsque c’est demandé, et à renvoyer en header l’information de taille :

header('Cache-Control: max-age=86400');
header('Content-Type: image/jpg');
if(isset($_GET['base64'])) {
$out = base64_encode(file_get_contents($path));
header('Content-Length: '.strlen($out));
print $out;
} else {
header('Content-Length: '.filesize($path));
print file_get_contents($path);
}

Comme toutes les solutions qui essayent d’améliorer l’existant pour les browsers qui le supportent, celle ci coûte bien sur plus cher en temps de développement puis en support, donc c’est à vous d’évaluer le rapport coût/bénéfice. D’autant plus que si vous étendez le support jusqu’à IE8 vous avez la moitié du marché d’aujourd’hui, ce qui commence à vraiment valoir le coup.

EDIT : Il semble qu’il y ait une limite de taille et un comportement qui change selon les browsers en ce qui concerne la quantité de données que l’on peut mettre dans les urls. IE8 par exemple supporte 32Ko max, ce qui rend cette technique inutile pour des images autre que de la décoration. Je n’ai pas trouvé les limites pour Chrome et FF3, mais en essayant avec des images supérieures à 1Mo, elles s’affichent partiellement ou pas du tout selon le browser. Donc jusqu’à nouvel ordre, cette technique doit rester une expérimentation.