L'AJAX ne se limite pas à l'utilisation de
l'objetXMLHttpRequest
, il existe bien d'autres manières de
communiquer avec un serveur. La balise<iframe>
fait partie des
diverses autres solutions possibles.
Vous avez probablement déjà entendu parler de cette balise et, comme beaucoup de monde, vous pensez probablement qu'elle est à éviter. Disons que, dans l'ensemble, oui, mais il existe certains cas où elle devient rudement efficace, notamment pour l'upload de fichiers !
Peut-être connaissez-vous l'élément HTML<iframe>
? Pour ceux qui ne le
connaissent pas, c'est un élément qui permet d'insérer une page Web dans une autre. Voici un petit rappel de la
syntaxe d'une iframe :
src="file.html" name="myFrame" id="myFrame"
Pour accéder au contenu de l'iframe, il faut d'abord accéder à
l'iframe elle-même et ensuite passer par la propriétécontentDocument
:
var frame = document.getElementById('myFrame').contentDocument
Une fois que l'on a accédé au contenu de l'iframe, c'est-à-dire à son document, on peut naviguer dans le DOM comme s'il s'agissait d'un document « normal » :
var frame_links = frame.getElementsByTagName('a').length;
Il y a deux techniques pour charger une page dans une iframe. La
première est de tout simplement changer l’attributsrc
de l'iframe via le
JavaScript, la deuxième est d'ouvrir un lien dans l'iframe. Cette action est rendue possible via
l'attributtarget
(standardisé en HTML5) que l'on peut utiliser sur un lien
ou sur un formulaire. C'est cette dernière technique que nous utiliserons pour la réalisation du système d'upload.
Ici, rien de compliqué, on change simplement l'URL de l'iframe en
changeant sa propriétésrc
. Cette technique est simple et permet de
transmettre des paramètres directement dans l'URL. Exemple :
document.getElementById('myFrame').src = 'request.php?nick=Thunderseb';
target
et un formulaire
L'intérêt d'utiliser un formulaire est que nous allons pouvoir envoyer des données via la méthode POST. L'utilisation de POST va nous permettre d'envoyer des fichiers, ce qui nous sera utile pour un upload de fichiers !
En fait, pour cette technique, il n'y a pas vraiment besoin du JavaScript, c'est du HTML pur :
id="myForm" method="post" action="request.php" target="myFrame"
<!-- formulaire -->
type="submit" value="Envoyer"
src="#" name="myFrame" id="myFrame"
L'attributtarget
indique au formulaire que son contenu doit être envoyé au
sein de l'iframe dont l'attributname
estmyFrame
(l'attributname
est
donc obligatoire ici !). De cette manière le contenu du formulaire y sera envoyé, et la page courante ne sera pas
rechargée.
Le JavaScript pourra être utilisé comme méthode
alternative pour envoyer le formulaire. Pour rappel, pour envoyer un formulaire, il faut utiliser la
méthodesubmit()
:
document.getElementById('myForm').submit();
load
Les iframes possèdent un
événementload
, déclenché une fois que le contenu de l'iframe est
chargé. À chaque contenu chargé,load
est déclenché. C'est un moyen
efficace pour savoir si le document est chargé, et ainsi pouvoir le récupérer. Voici un petit exemple :
src="file.html" name="myFrame" id="myFrame" onload="trigger()"
function trigger() {
var frame = document.getElementById('myFrame').contentDocument;
alert(frame.body.textContent);
}
Quand une page Web est chargée dans l'iframe, son contenu est affiché et les scripts sont exécutés. Il est également possible, depuis l'iframe, d'appeler une fonction présente dans la page « mère », c'est-à-dire la page qui contient l'iframe.
Pour appeler une fonction depuis l'iframe, il suffit d'utiliser :
window.top.window.nomDeLaFonction();
L'objetwindow.top
pointe vers la fenêtre « mère », ce qui nous permet
ici d'atteindre la page qui contient l'iframe.
Voici un exemple qui illustre ce mécanisme :
src="file.html" name="myFrame" id="myFrame"
function trigger() {
var frame = document.getElementById('myFrame').contentDocument;
alert('Page chargée !');
}
window.top.window.trigger(); // On appelle ici notre fonction de callback
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse molestie suscipit arcu.
Le chargement de données via une iframe a un gros avantage : il est possible de charger n'importe quoi comme données. Ça peut être une page Web complète, du texte brut ou même du JavaScript, comme le format JSON.
Si on reprend l'exemple vu précédemment, avec le callback, il est possible de récupérer facilement des données JavaScript, comme un objet. Dans ce cas, il suffit d'utiliser du PHP pour construire un objet qui sera transmis en paramètre de la fonction de callback, comme ceci :
<?php
$fakeArray = array('Sébastien', 'Laurence', 'Ludovic');
?>
window.top.window.trigger(['<?php echo implode("','", $fakeArray) ?>']);
Ici, un tableau JavaScript est
construit via le PHP et envoyé à la fonctiontrigger()
en tant que paramètre.
id="myForm" method="post" action="request.php" target="myFrame"
for="nick"Votre pseudo :
type="text" id="nick" name="nick"
type="button" value="Envoyer" onclick="sendForm();"
src="#" name="myFrame" id="myFrame"
function sendForm() {
var nick = document.getElementById("nick").value;
if (nick) { // Si c'est OK
document.getElementById("myForm").submit(); // On envoie le formulaire
}
}
function receiveData(data) {
alert('Votre pseudo est "' + data + '"');
}
Et maintenant la page PHP :
window.top.window.receiveData("<?php echo htmlentities($_POST['nick']); ?>");
Ce script ne fait que récupérer la
variable$_POST['nick']
, pour ensuite appeler la
fonctionreceiveData()
en lui passant le pseudo en paramètre. La fonction PHP
htmlentities()
permet d'éviter que l'utilisateur insère d'éventuelles balises HTML
potentiellement dangereuses telles que la balise<script>
. Alors,
certes, ici l'insertion de balise ne pose pas de problème puisque l'on affiche le pseudo dans une
fenêtrealert()
, mais mieux vaut prévenir que guérir, non ?
Par le biais d'un formulaire et d'une iframe, créer un système
d'upload n'est absolument pas compliqué. C'est même relativement simple ! Les
éléments<form>
possèdent un attributenctype
qui
doit absolument contenir la valeurmultipart/form-data
. Pour faire simple,
cette valeur indique que le formulaire est prévu pour envoyer de grandes quantités de données (les fichiers sont des
données volumineuses).
Notre formulaire d'upload peut donc être écrit comme ceci :
id="uploadForm" enctype="multipart/form-data" action="upload.php" target="uploadFrame" method="post"
for="uploadFile"Image :
id="uploadFile" name="uploadFile" type="file"
id="uploadSubmit" type="submit" value="Upload !"
Ensuite, on place l'iframe, ainsi
qu'un autre petit<div>
que nous utiliserons pour afficher le résultat
de l'upload :
id="uploadInfos"
id="uploadStatus"Aucun upload en cours
id="uploadFrame" name="uploadFrame"
Et pour finir, une dose de JavaScript :
function uploadEnd(error, path) {
if (error === 'OK') {
document.getElementById('uploadStatus').innerHTML = '<a href="' + path + '">Upload done !</a><br /><br /><a href="' + path + '"><img src="' + path + '" /></a>';
} else {
document.getElementById('uploadStatus').innerHTML = error;
}
}
document.getElementById('uploadForm').addEventListener('submit', function() {
document.getElementById('uploadStatus').innerHTML = 'Loading...';
});
Quelques explications s'imposent. Dès
que le formulaire est envoyé, la fonction anonyme de
l'événementsubmit
est exécutée. Celle-ci va remplacer le texte
du<div>
#uploadStatus
pour
indiquer que le chargement est en cours. Car, en fonction de la taille du fichier à envoyer, l'attente peut être
longue. L'argumenterror
contiendra soit « OK », soit une explication sur une
erreur éventuelle. L'argumentpath
contiendra l'URL du fichier venant d'être
uploadé. L'appel vers la fonctionuploadEnd()
sera fait via l'iframe, comme
nous le verrons plus loin.
Le JavaScript étant mis en place, il ne reste plus qu'à nous occuper
de la pageupload.php
qui va réceptionner le fichier uploadé. Il s'agit d'un
simple script d'upload :
<?php
$error = NULL;
$filename = NULL;
if (isset($_FILES['uploadFile']) && $_FILES['uploadFile']['error'] === 0) {
$filename = $_FILES['uploadFile']['name'];
$targetpath = getcwd() . '/' . $filename; // On stocke le chemin où enregistrer le fichier
// On déplace le fichier depuis le répertoire temporaire vers $targetpath
if (@move_uploaded_file($_FILES['uploadFile']['tmp_name'], $targetpath)) { // Si ça fonctionne
$error = 'OK';
} else { // Si ça ne fonctionne pas
$error = "Échec de l'enregistrement !";
}
} else {
$error = 'Aucun fichier réceptionné !';
}
// Et pour finir, on écrit l'appel vers la fonction uploadEnd :
?>
window.top.window.uploadEnd("<?php echo $error; ?>", "<?php echo $filename; ?>");
Avec ce script tout simple, il est donc possible de mettre en place un upload de fichiers sans « rechargement ». Il ne reste plus qu'à améliorer le système, notamment en sécurisant le script PHP (détecter le type MIME du fichier, pour n'autoriser que les images par exemple), ou en arrangeant le code JavaScript pour afficher à la suite les fichiers uploadés s'il y en a plusieurs…
Si vous souhaitez essayer ce script en ligne, sachez que nous avons mis une version en ligne, mais que celle-ci n'enregistre pas les fichiers sur le serveur et que cela implique donc que l'affichage de l'image n'est pas effectué. Vous êtes en revanche prévenus lorsqu'un fichier a fini d'être uploadé, ce qui est, somme toute, le but principal de notre script.
Essayer la version « light » !
L'utilisation d'une iframe est une technique AJAX assez répandue et facile à mettre en œuvre pour réaliser un upload de fichiers compatible avec tous les navigateurs.
Il suffit
d'utiliser l'événementload
sur une iframe pour savoir si la page qu'elle
contient vient d'être chargée. Il ne reste plus qu'à accéder à cette page et à récupérer ce qui nous intéresse.
Depuis une
iframe, il faut utiliserwindow.top
pour accéder à la page qui
contient l'iframe. C'est utile dans le cas d'un callback.