![]() ![]() Méthode |
![]() ![]() Internet Explorer |
![]() ![]() Firefox |
![]() ![]() Opera |
![]() ![]() Google Chrome |
![]() ![]() Safari |
---|---|---|---|---|---|
iFrame Loading |
Oui |
Oui |
Oui |
Oui |
Oui |
Vous connaissez certainement
l'élément <iframe>
? Pour ceux qui ne le connaissent pas, c'est
un élément qui permet d'insérer une page Web dans une autre. Cet élément faisait partie des spécifications HTML,
mais a été délaissé dans XHTML en raison de sa mauvaise accessibilité. L'iframe fait néanmoins son come-back
dans HTML5. La technique habituelle de remplacement d'une iframe est de faire un include en PHP dans un <div>,
avec une overflow (propriété CSS pour ajouter des barres de défilement).
Voici un petit rappel de la syntaxe d'une iframe :
src="fichier.html" width="500" height="300" name="myFrame" id="myFrame"
Comme je l'ai dit, cet élément n'est plus présent dans les spécifications XHTML. Mais l'iframe, en AJAX, peut se révéler très utile pour y faire transiter des données. Le meilleur exemple est le cas de l'upload de fichier en AJAX : un upload de fichier sans recharger la page.
Avant de vous parler de l'upload, je vais d'abord introduire d'autres notions pour manipuler les iframes en récupérant leur contenu.
Une iframe peut être symbolisée comme un document dans un document. Pour accéder au document contenu dans l'iframe, il faut d'abord accéder à l'iframe elle-même, puis à son contenu.
Reprenons le code HTML d'insertion de l'iframe :
src="fichier.html" width="500" height="300" name="myFrame" id="myFrame"
Ce code basique représente une iframe qui contient le fichier fichier.html. Il est très important de nommer l'iframe (attribut name), car nous allons nous en servir pour accéder à cette iframe.
Il y a trois manières d'accéder à l'iframe :
Avec l'Id
Avec le nom
Avec l'objet global frames
On ne va pas utiliser l'Id car ça va vous obliger à utiliser contentDocument pour Firefox que IE ne gère pas.
Voici donc les deux techniques en action :
var oFrame = window.myFrame; // Avec le name
var oFrame = window.frames["myFrame"]; // Avec l'objet global
Une fois l'iframe atteinte, on peut accéder à son contenu, avec l'objet document :
var oFrame = window.myFrame.document; // Avec le name
var oFrame = window.frames["myFrame"].document; // Avec l'objet global
A partir d'ici, les méthodes habituelles peuvent être utilisées, comme si vous travailliez directement dans la page courante, et non dans l'iframe ;
var oFrame = window.frames["myFrame"].document.getElementsByTagName("a").length;
var oFrame = window.frames["myFrame"].document.getElementById("idElement").innerHTML;
Pour charger un fichier dans une iframe, il y a 3 façons :
En remplissant l'attribut src directement en HTML. Le fichier est alors chargé à l'ouverture de la page contenant l'iframe
En modifiant avec JavaScript l'attribut src
En ouvrant un lien dans l'iframe, au moyen de l'attribut target, lui aussi invalide XHTML.
Les deux dernières méthodes sont assez efficaces dans le cas d'une utilisation de type AJAX. Bien qu'invalide, la méthode avec target peut se révéler terriblement pratique !
Les iframes possèdent un événement onload, qui se déclenche quand le contenu de l'iframe vient d'être chargé. A chaque chargement de contenu, onload 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="fichier.html" name="idFrame" id="idFrame" width="200" height="150" onload="trigger();"
Et la fonction trigger :
function trigger() {
alert("Chargé");
}
Quand un document est chargé dans l'iframe, il est exécuté. Cela veut donc dire que si c'est un fichier HTML contenant du code JavaScript, il est possible de déclencher une fonction pour dire que le document est chargé. La fonction à exécuter, garante du bon chargement de la page, peut se trouver dans le fichier chargé dans l'iframe, ou bien dans la page Web qui contient l'iframe. Cela veut donc dire qu'un script contenu dans l'iframe peut appeler une fonction se trouvant dans la page Web hôte de l'iframe.
L'appel de la fonction peut alors s'écrire comme ceci :
window.top.window.nomDeLaFonction();
Voici un petit exemple.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
xmlns="http://www.w3.org/1999/xhtml"
Techniques AJAX - iFrame Loading - callback
type="text/javascript"
<!--
function callback() {
alert("Fichier chargé !");
}
//-->
src="iFrame_loading_callback.htm" name="idFrame" id="idFrame" width="200" height="150"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
xmlns="http://www.w3.org/1999/xhtml"
Techniques AJAX - iFrame Loading - callback
type="text/javascript"
<!--
window.top.window.callback();
//-->
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse molestie suscipit arcu.
Ces deux techniques (onload et le callback) peuvent donc être utilisées pour vérifier si le document est chargé. Dans le cas de la deuxième technique, elle peut-être utilisée pour transmettre des données sous forme d'objet JavaScript (objet JSON, String, array...).
Pour changer l'url du fichier à charger dans l'iframe, quoi de plus commode que d'utiliser src :
document.getElementById('idFrame').src = "autreFichier.html";
Ainsi, l'attribut src est changé, le la nouvelle page est chargée.
Comme iframe, target est délaissé en XHTML en raison de problèmes d'accessibilité. C'est un attribut, utilisé sur les éléments <a> et <form> qui sert à définir l'endroit où va s'ouvrir le lien (nouvelle fenêtre, fenêtre parente...). Cet attribut était aussi utilisé pour spécifier la frame dans laquelle un lien allait s'ouvrir, dans le cas des framesets (framesets délaissés depuis pas mal de temps déjà, replacés par les includes des langages dynamiques).
Nous, nous allons utiliser target pour charger un lien dans l'iframe. Cet attribut n'est pas vraiment intéressant pour "juste" charger une page. Nous nous en servirons avec l'élément <form>, dans le cas de l'upload de fichiers (pour dire d'envoyer les données du formulaire dans l'iframe).
Voici tout de même un exemple avec un lien :
src="#" name="idFrame" id="idFrame" width="200" height="150"
target="idFrame" href="http://www.google.fr"Charger Google
La récupération du contenu de l'iframe est assez simple. J'ai déjà montré comment accéder à l'iframe, et il n'y a plus qu'à ajouter quelques points supplémentaires.
Si vous chargez un document XML (ou HTML), vous pouvez récupérer vos données en utilisant les techniques de récupération DOM (getElementById, getElementsByTagName...).
En revanche, si vous voulez récupérer les données au format
texte, vous devez utiliser innerHTML.
Un document chargé dans l'iframe possède un objet body. Même si
c'est un fichier texte, un élément body est présent virtuellement : c'est lui qui englobe la totalité des
informations disponibles.
Ainsi, vous devez passer par le body pour utiliser innerHTML (car il est impossible de faire innerHTML directement sur l'objet document) :
var sContent = window.idFrame.document.body.innerHTML;
Si le fichier est un fichier HTML, tout ce qui est dans l'élément body est récupéré. En revanche, si c'est un fichier texte, le contenu est récupéré, mais est placé dans un élément <pre>. Pour utiliser ce contenu, il suffit de faire un substring pour éliminer les balises d'ouverture et de fermeture de l'élément <pre> :
var sContent = window.idFrame.document.body.innerHTML;
sContent = sContent.substring(5, sContent.length - 6);
Non rassurez-vous, Internet Explorer comprend très bien le code
ci-dessus. Mais l'inconvénient est de faire un substring, c'est-à-dire un calcul dont l'interpréteur
JavaScript se passerait bien. Internet Explorer implémente la méthode innerText qui permet de ne récupérer
que du texte : il n'y a donc pas d'élément <pre> à enlever.
Voici donc le code modifié pour
utiliser innerText si le navigateur gère cette méthode :
sContent = "";
if(document.body.innerText) { // IE gère innerText
sContent = window.idFrame.document.body.innerText;
} else {
sContent = window.idFrame.document.body.innerHTML;
sContent = sContent.substring(5, sContent.length - 6);
}
Nous l'avons vu précédemment, avec le système de callback (l'inverse de onload), on peut transmettre des objets JavaScript (array, String, objets JSON...).
Voilà un petit exemple qui charge un fichier texte (avec ou sans target), et qui l'affiche :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
xmlns="http://www.w3.org/1999/xhtml"
Techniques AJAX - iFrame Loading
type="text/javascript"
<!--
function getText() {
var sContent = "";
if (document.body.innerText) {
sContent = window.idFrame.document.body.innerText;
} else {
sContent = window.idFrame.document.body.innerHTML;
sContent = sContent.substring(5, sContent.length - 6);
}
document.getElementById("output").innerHTML = sContent;
}
//-->
src="#" name="idFrame" id="idFrame" width="200" height="150" onload="getText();" style="display: none;"
target="idFrame" href="iFrameLoading.txt"Charger le fichier avec target -
onclick="document.getElementById('idFrame').src = 'iFrameLoading.txt';"Charger le fichier sans target
id="output"
On va maintenant voir une utilisation vraiment pratique du
système d'iFrame Loading : l'upload de fichiers. Le fichier à uploader se trouve bien-sûr sur le disque dur,
on n'upload pas à partit d'une url (sinon c'est trop facile
).
Le problème de l'upload est qu'il faut utiliser l'encodage multipart/form-data pour envoyer le formulaire. S'il suffisait d'utiliser une bête requête POST, on aurait pu le faire avec XMLHttpRequest. Mais XHR ne permet pas de définir l'encodage.
Le principe consiste tout simplement à envoyer le formulaire dans une iframe. Le traitement du formulaire se fait alors via PHP et le résultat de l'upload est renvoyé par le biais de l'iframe, avec un système de callback.
Voici un code HTML très simple :
id="uploadForm" enctype="multipart/form-data" action="upload.php" target="uploadFrame" method="post"
id="uploadFile" name="uploadFile" type="file"
id="uploadSubmit" type="submit" value="Upload"
class="center" id="uploadStatus"No upload yet
id="uploadFrame" name="uploadFrame" src="#"
Grâce à ce code, une fois que l'utilisateur presse le bouton uploadSubmit, le formulaire est envoyé dans l'iframe uploadFrame pour être traité par le script contenu dans le fichier upload.php.
Le script PHP s'occupe juste de l'upload. Une fois le script d'upload exécuté, les variables $error et $filename sont écrites dans le code JavaScript qui va se charger d'appeler la fonction uploadEnd. Cette dernière fonction se chargera d'informer le client de statut de l'upload, et si il est réussi, lui retournera l'url du fichier uploadé ($filename).
<?php
$error = NULL;
$filename = NULL;
if(isset($_FILES['uploadFile']) && $_FILES['uploadFile']['error'] == 0) {
$targetpath = getcwd().'/'.$_FILES['uploadFile']['name'];
$filename = $_FILES['uploadFile']['name'];
if(@move_uploaded_file($_FILES['uploadFile']['tmp_name'], $targetpath)) {
$error = 'OK';
} else {
$error = 'Upload failed !';
}
} else {
$error = 'Input error !';
}
?>
type="text/javascript"
<!--
window.top.window.uploadEnd("<?php echo $error; ?>", "<?php echo $filename; ?>");
//-->
Voilà la fonction uploadEnd qui se charge d'afficher le résultat de l'upload :
function uploadEnd(sError, sPath) {
if(sError == 'OK') {
document.getElementById("uploadStatus").innerHTML = "<a href=\"" + sPath + "\">Upload done !</a>";
} else {
document.getElementById("uploadStatus").innerHTML = sError;
}
}
J'ai ajouté une fonction uploadRun qui est déclenchée quand le formulaire est envoyé (évènement onSubmit) et qui se charge de désactiver le bouton d'envoi, et qui affiche une image GIF de chargement. Quand l'upload est fini, la fonction uploadEnd se charge de réactiver le bouton d'envoi.
Les autres améliorations viennent plus dans le code PHP qui
doit être sécurisé, la taille et le type du fichier doivent être vérifiés...
Vous pouvez lire
ce tutoriel
qui porte principalement sur l'upload en PHP.
Voici ma page complète, avec les améliorations :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
xmlns="http://www.w3.org/1999/xhtml"
http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
Techniques AJAX - Upload de fichiers
type="text/css"
<!--
#uploadFrame { display: none; }
//-->
type="text/javascript"
<!--
function uploadInit() {
// Je pré-charge l'image
var oLoading = new Image();
oLoading.src = "loading.gif";
}
function uploadRun() {
document.getElementById("uploadStatus").innerHTML = "<img src=\"loading.gif\" alt=\"Upload is running...\" width=\"220\" height=\"19\" />";
document.getElementById("uploadSubmit").disabled = true;
return true;
}
function uploadEnd(sError, sPath) {
if(sError == 'OK') {
document.getElementById("uploadStatus").innerHTML = "<a href=\"" + sPath + "\" title=\"Go to " + sPath + "\">Upload done !</a>";
} else {
document.getElementById("uploadStatus").innerHTML = sError;
}
document.getElementById("uploadSubmit").disabled = false;
}
//-->
onload="uploadInit();"
id="uploadDiv"
id="uploadForm" enctype="multipart/form-data" action="upload.php" target="uploadFrame" onsubmit="uploadRun();" method="post"
class="center"
for="uploadPass"Password :
type="password" id="uploadPass" name="uploadPass"
class="center"
id="uploadFile" name="uploadFile" type="file"
id="uploadSubmit" type="submit" value="Upload"
class="center" id="uploadStatus"No upload yet.
id="uploadFrame" name="uploadFrame" src="#"
Cette page n'est pas valide XHTML, en raison de l'utilisation d'iframe et de l'attribut target. Il est possible de rendre la page valide, en utilisant JavaScript pour définir l'attribut target, et pour insérer l'iframe. Mais je ne suis pas vraiment pour ce système, c'est un peu se voiler la face en disant que la page est valide XHTML alors qu'en vérité, elle ne l'est pas (puisque target et <iframe> sont quand même utilisés).
Si on regarde bien l'exemple, on peut se rendre compte que JavaScript n'est pas l'élément essentiel du système d'upload. JavaScript ne sert qu'à détecter et à traiter de façon élégante les informations renvoyées pas le script d'upload. Cela veut donc dire qu'il est très facile de rendre le script d'upload utilisable si JavaScript est désactivé.
Si JavaScript est désactivé, un problème se pose : comment
informer l'utilisateur d'une erreur ou de la réussite de l'upload ?
Eh bien c'est très simple, on va
utiliser l'iframe ! Le truc est d'afficher l'iframe si JavaScript est désactivé. Pour ce faire, on fait
l'inverse : on masque l'iframe avec JavaScript (donc si JS est désactivé, l'iframe n'est pas masquée).
Voici donc l'instruction à ajouter dans la fonction uploadInit :
document.getElementById("uploadFrame").style.display = "none";
// On n'a pas besoin non plus du <div> pour affiche le statut.
// Pensez à le masquer pas défaut avec les CSS.
document.getElementById("uploadStatus").style.display = "block";
Ensuite, on modifie le script PHP pour qu'il écrive les informations. Ces informations seront alors affichées dans l'iframe.
Rajoutez ceci à votre page PHP :
<?php
if($error == 'OK') {
echo '<p><a href="'.$filename.'" title="Go to '.$filename.'">Upload done !</a></p>';
} else {
echo '<p>'.$Error.'</p>';
}
?>
Ainsi, ces données seront accessibles dans l'iframe. De plus,
ça peut vous être utile pour débugger votre script
.