Table des matières
Ajuster dynamiquement la hauteur des <iframe>
L'insertion d'un iframe pose souvent des problèmes de hauteur. Si l'on sait quelle sera la hauteur du contenu de l'iframe, il suffit d'indiquer celle-ci comme attribut (ou style) de la balise ouvrante. Lorsque la hauteur de l'iframe est inconnue, il est souhaitable que la taille de l'iframe s'ajuste automatiquement. On parle aussi de hauteur dynamique de l'iframe.
Au-delà de la simple insertion d'une page dans une autre, plusieurs paramètres rendent l'opération moins évidente qu'il peut y paraître :
- comportement différent des navigateurs (html, javascript, sécurité…),
- modification par l'internaute de la taille de la fenêtre, en cours de navigation,
- navigation dans l'iframe,
- page-hôtesse et pages-invitées appartenant à des domaines différents.
Ajustement naturel
Dans certains cas, l'ajustement de la taille accordée à l'iframe, dans la page qui l'héberge, se fait naturellement. Il n'est donc pas rare qu'une insertion d'iframe dans une page de test - réduite à sa plus simple expression - fonctionne correctement. On s'apercevra vite que le même code htlm placé dans une page réelle ne produit plus le résultat escompté. On voit apparaître les ascenseurs internes dont on espérait se débarasser. Pour que l'iframe prenne “naturellement” toute la place qui lui est nécessaire, il faut que tous les éléments parents de l'élement iframe aient leur dimension fixée (par css) à 100%. Une page minimale de type “<body><iframe></iframe></body>” remplit cette condition (sans css) puisque body prend naturellement 100% du port de restitution (fenêtre).
Un code simple tel que :
<iframe src="http://domaine.externe.tld/appli/iframed.html" scrolling="no" frameborder="0" style="height: 100%; width: 100%"></iframe>
effectuera l'ajustement automatique de la taille de l'iframe, sur certains navigateurs, à condition que l'iframe tienne dans son conteneur (fenêtre, bloc, etc.). Certains navigateurs appliqueront une taille par défaut les conduisant à tronquer le contenu de l'iframe.
Dans tous les cas, si le contenu de l'iframe excède la taille de la fenêtre (le plus grand conteneur possible), il sera irrémédiablement tronqué. Si l'attribut “scrolling” était fixé à “yes”, des ascenseurs internes seraient créés, mais tel n'est pas le but recherché.
On souhaite que la circulation dans le contenu de l'iframe se fasse au moyen ascenseurs de la fenêtre de visualisation, comme s'il s'agissait d'un contenu “natif” de la page affichée. Pour cela, il faut que l'iframe soit automatiquement affiché en “pleine page”, même si du contenu déborde de la fenêtre.
Forcer le redimentionnement
Le seul moyen de garantir que le contenu de l'iframe occupera toute la place qui lui est nécessaire sur la page-hôtesse est de forcer le redimensionnement de l'espace qui lui est accordé, une fois que l'on connaît l'espace nécessaire, donc après chargement du contenu de l'iframe.
Ce “redimentionnement” a posteriori ne peut être effectué que par un code javascript. Si le principe est simple, il faut envisager le cas où la source chargée dans l'iframe n'est pas dans le domaine de la page-hôtesse. En effet, afin d'éviter qu'un iframe que l'on ne contrôle pas vienne interagir avec le code javascript de la page-hôtesse, les navigateurs bloquent ce genre d'interacton (Same origin policy) ! Nous verrons qu'il existe des moyens de contourner cette limitation de sécurité.
Enfin, la simplicité du principe se heurte aux facéties d'implémentation des différents navigateurs Web, en particulier d'IE. Toute solution devra donc être testée sur les navigateurs pour lesquels on souhaite qualifier la page-hôtesse.
Même domaine
Lorsque la page-hôtesse et l'iframe partagent le même nom de domaine, il existe de nombreuses solutions permettant de résoudre ce problème récurrent de taille des iframes.
jquery-iframe-auto-height
S'appuyant sur la bibliothèque jQuery, jquery-iframe-auto-height propose une solution simple à mettre en œuvre. Dans l'exemple suivant, on se contente d'ajouter un code javasript à la suite de l'élément iframe que l'on veut redimensionner :
<iframe src="mon_iframe.html" class="hauteur-auto" scrolling="no" frameborder="0"></iframe> <script> $('iframe.hauteur-auto').iframeAutoHeight({minHeight: 400}); </script>
Dans cet exemple, le redimentionnement est limité aux iframes de la classe “hauteur-auto” et l'on impose une hauteur minimale, dans l'éventualité où la hauteur nécessaire au contenu de l'iframe serait inférieure à cette valeur.
Après s'être assuré que la bibliothèque jQuery et le plugin jquery-iframe-auto-height sont effectivement chargés par la page, on n'oubliera pas d'insérer un déclencheur de redimentionnement, dans la page-hôtesse, de préférence dans le head :
<script> $(document).ready(function () { $('iframe').iframeAutoHeight({debug: true}); }); </script>
Pour une description complète des options, paramètres et fonctionnalités, se reporter à la documentation de jquery-iframe-auto-height.
jQuery Auto Iframe Height
Source : tutoriel (blog - en).
Auto Iframe Height est un plugin jQuery plus rustique que le précédent mais plus simple à mettre en œuvre. Il suffit de charger jQuery et jquery.autoheight (dans le head) :
<script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="jquery.autoheight.js"></script>
On déclare ses iframes comme d'habitude. On n'a plus qu'à les rattacher à la classe autoHeight (mot réservé du plugin) :
<iframe id="mon_iframe" name="mon_titre" class="autoHeight" scrolling="auto" frameborder="0" src="http://mon.domaine.tld/iframe.php"></iframe>
Drupal
Sous Drupal, le module InsertFrame se charge de l'essentiel du travail.
Dans un champ de contenu correctement configuré, on se contente d'insérer l'url de l'iframe :
[[[http://mondomaine.tld/mon_application.html]]]
Se reporter à la documentation du module pour les précisions : syntaxe, paramètres et configuration du champ de saisie.
Bricolages divers
De multiples propositions en javascript “pur” fleurissent ici et là. Si elles présentent un intérêt pédagogique la plupart n'ont pas la robustesse souhaitée (cas d'utilisation, navigateurs, …).
Sans préjuger de la validité de toutes les solutions proposées, aucune de celles testées n'a donné de résultat satisfaisant. Ça doit pourtant être possible puisque nous avons trouvé une solution fonctionnelle pour les iframes inter-domaines (voir plus loin).
Domaines différents
Si l'interaction entre pages issues de domaines différents est une source de risque de sécurité, c'est parfois le comportement souhaité.
Pure Javascript
Sources : explication (blog -en).
Une technique n'utilisant que les fonctions de base de javascript (sans bibliothèque additionnelle) permet de contourner la difficulté. Elle mobilise une page purement technique, indépendante de tout contenu, qui va permettre la transmission d'information depuis la page-invitée vers la page-hôtesse: la page-messagère.
La page-hôtesse et la page-messagère doivent impérativement appartenir au même domaine. Suivant les règles de sécurité, la page-hôtesse peut communiquer avec la page-invitée (fille), la page-messagère peut communiquer avec la page-hôtesse (même domaine). Il ne reste plus qu'à permettre la communication entre la page-invitée et la page-messagère. Pour ce faire, il suffit d'inclure, par iframe, la page-messagère dans la page-invitée.
On a alors le schéma de communication suivant; hôtesse > invitée > messagère > hôtesse. La page invitée peut désormais transmettre des informations (comme sa taille) à sa page hôtesse, par l'intermédiaire de la page-messagère. Voici des exemples du code qu'il faudra insérer dans chacune des pages :
page-hôtesse (http://mon.domaine.tld/index.html)
<script> // redimensionner l'iframe function resizeIframe(height) { document.getElementById('id_mon_iframe').height = parseInt(height)+60; } </script> <iframe id='id_mon_iframe' src='http://domaine.externe.tld/application/framed.html'></iframe>
page-invitée (http://domaine.externe.tld/application/framed.html)
<body onload="iframeResizePipe()"> <iframe id="messagere" src='' height='0' width='0' frameborder='0'></iframe> <script type="text/javascript"> function iframeResizePipe() { var height = document.body.scrollHeight; var pipe = document.getElementById('messagere'); pipe.src = 'http://mon.domaine.tld/messagere.html?height='+height+'&cacheb='+Math.random(); } </script>
page-messagère (http://mon.domaine.tld/messagere.html)
<html> <body onload="parentIframeResize()"> <script> function parentIframeResize() { var height = getParam('height'); // la magie est là : la messagere peut donner un ordre à sa grand-mère car elles ont dans le même domaine parent.parent.resizeIframe(height); } function getParam( name ) { name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); var regexS = "[\\?&]"+name+"=([^&#]*)"; var regex = new RegExp( regexS ); var results = regex.exec( window.location.href ); if( results == null ) return ""; else return results[1]; } </script> </body> </html>
HMTL 4 avec easyXDM
Source : Resize iframe based on content (blog -en).
Si l'on utilise html 4, la bibliothèque easyXDM (Cross-Domain Messaging made easy) est là pour nous aider à contourner la limitation de l'interaction inter-domaines.
Comme dans le cas de domaines identiques, on devra adapter la page-hôtesse. Mais l'on devra également modifier le contenu de la page invitée dans l'iframe !
Dans le code source de la page page-hôtesse, l'élément iframe est remplacé par un objet javascript que l'on déclarera dans un élément script de l'élément head :
new easyXDM.Socket({ remote: "http://provider.easyxdm.net/example/resized_iframe.html", container: document.getElementById("container"), onMessage: function(message, origin){ this.container.getElementsByTagName("iframe")[0].style.height = message + "px"; } });
ici, “container” (ligne 3) est à remplacer par l'id de l'élément html dans lequel on souhaite insérer la page-invitée (par ex. <div id=“mon_iframe”></div>).
Dans la page-invitée, on ajoute le code javascript suivant, à la suite du contenu :
var socket = new easyXDM.Socket({ onReady: function(){ socket.postMessage(document.body.scrollHeight) } });
Afin d'assurer la navigation dans l'iframe ouvert, il convient de créer un iframe intermédiaire (voir exemple). La page-hôtesse invite cette page intermédiaire en lui fournissant comme paramètre l'url de la page d'accueil invitée (voir exemple). Enfin, toute page invitée affichée au cours de la navigation doit avoir été modifiée en conséquence (voir exemple).
Variante
Dans la page-hôtesse
var transport = new easyXDM.transport.BestAvailableTransport({ local: "../hash.html", remote: "http://provider.easyxdm.net/example/resized_iframe.html", container: document.getElementById("element_that_should_contain_the_frame"), onMessage: function(message, origin){ this.container.getElementsByTagName("iframe")[0].style.height = message + "px"; } });
Dans chaque page-invitée, à la suite du contenu :
var transport = new easyXDM.transport.BestAvailableTransport({}, function(){ transport.postMessage(document.body.scrollHeight); });
HMTL 5
Sources : la norme (w3c - en), le principe (wp - en), une explication fouillée (mozzila - en), un exemple sauter les premiers messages (blog - en).
Html 5 intègre l'API de communication inter-domaines par échange de messages entre fenêtres ou frames. S'appuyant sur le même principe que easyXDM, on retrouve la même chose à quelques variations près.
Dans la page invitée :
<script> function adjust_iframe_height(){ var actual_height = document.getElementById('element_id').scrollHeight; parent.postMessage(actual_height,"*"); //* aucune restriction de domaine sur les pages-hôtesses } </script> ... <body onload="adjust_iframe_height();"> ...
Ici, element_id sera replacé par l'id de l'élément de la page invitée dont on souhaite transmettre la hauteur 1).
Dans la page-hôtesse :
<script> // On essaie d'être compatible avec le max de navigateurs var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var eventer = window[eventMethod]; var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; // On écoute les messages envoyés par la frame/fenêtre fille eventer(messageEvent,function(e) { console.log('parent received message!: ',e.data); document.getElementById('mon_iframe_id').height = e.data + 'px'; },false); </script>
puis on insère un élément iframe, sans oublier de déclarer son id en cohérence avec le choix fait dans le code précédent (ici “mon_iframe_id”).