Outils pour utilisateurs

Outils du site


ajuster_la_hauteur_des_iframe

Différences

Ci-dessous, les différences entre deux révisions de la page.


ajuster_la_hauteur_des_iframe [2014/09/13 11:13] (Version actuelle) – créée - modification externe 127.0.0.1
Ligne 1: Ligne 1:
 +====== 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 : 
 +<code>
 +<iframe src="http://domaine.externe.tld/appli/iframed.html" 
 +scrolling="no" frameborder="0" style="height: 100%; width: 100%"></iframe>
 +</code>
 +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 =====
 +<note important>
 +Votre page va devoir faire appel à Javascript. Vous devrez donc prévoir une alternative.
 +</note>
 +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 ([[http://en.wikipedia.org/wiki/Same_origin_policy|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.
 +
 +<note>Ma priorité du moment allant aux iframes inter-domaines, le champ d'application intra-domaine a été survolé.</note>
 +=== jquery-iframe-auto-height ===
 +S'appuyant sur la bibliothèque jQuery, [[https://github.com/house9/jquery-iframe-auto-height|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 :
 +<code>
 +<iframe src="mon_iframe.html" class="hauteur-auto" scrolling="no" frameborder="0"></iframe>
 +<script>
 +  $('iframe.hauteur-auto').iframeAutoHeight({minHeight: 400});
 +</script>
 +</code>
 +
 +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// :
 +<code>
 +<script>
 +  $(document).ready(function () {
 +    $('iframe').iframeAutoHeight({debug: true});
 +  });
 +</script>
 +</code>
 +
 +Pour une description complète des options, paramètres et fonctionnalités, se reporter à la [[https://github.com/house9/jquery-iframe-auto-height#readme|documentation de jquery-iframe-auto-height]].
 +
 +=== jQuery Auto Iframe Height ===
 +Source : [[http://www.lost-in-code.com/programming/jquery-auto-iframe-height/|tutoriel]] (blog - en).
 +
 +[[http://www.lost-in-code.com/wp-content/uploads/2012/01/jquery.autoheight.js|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//) :
 +<code>
 +<script type="text/javascript" src="jquery.js"></script>
 +<script type="text/javascript" src="jquery.autoheight.js"></script>
 +</code>
 +On déclare ses iframes comme d'habitude. On n'a plus qu'à les rattacher à la classe //autoHeight// (mot réservé du plugin) : 
 +<code>
 +<iframe id="mon_iframe" name="mon_titre" class="autoHeight" scrolling="auto" frameborder="0" src="http://mon.domaine.tld/iframe.php"></iframe>
 +</code>
 +
 +=== Drupal ===
 +
 +Sous Drupal, le module [[https://drupal.org/project/insertFrame|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 :
 +<code>
 +[[[http://mondomaine.tld/mon_application.html]]]
 +</code>
 +
 +Se reporter à la [[https://drupal.org/node/602268|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é.
 +<note important>
 +Vous devrez non seulement modifier la page-hôtesse mais aussi le code de la page-invitée dans l'iframe.
 +</note>
 +=== Pure Javascript ===
 +Sources : [[http://stackoverflow.com/questions/153152/resizing-an-iframe-based-on-content|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)
 +<code>
 +<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>
 +
 +</code>
 +page-invitée (http://domaine.externe.tld/application/framed.html)
 +<code>
 +<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>
 +</code>
 +page-messagère (http://mon.domaine.tld/messagere.html)
 +<code>
 +<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>
 +</code>
 +
 +=== HMTL 4 avec easyXDM===
 +Source : [[http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/|Resize iframe based on content]] (blog -en).
 +
 +Si l'on utilise html 4, la bibliothèque [[http://easyxdm.net/|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// :
 +<code>
 +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";
 +    }
 +});
 +</code>
 +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 :
 +<code>
 +var socket = new easyXDM.Socket({
 +    onReady:  function(){
 +        socket.postMessage(document.body.scrollHeight)
 +   }
 +});
 +</code>
 +<note important>
 +Cette technique exige une mise en œuvre plus sophistiquée si l'on veut permettre la navigation dans l'iframe (pas simplement afficher une page).
 +</note>
 +
 +Afin d'assurer la navigation dans l'iframe ouvert, il convient de créer un iframe intermédiaire ([[http://provider.easyxdm.net/current/example/resize_intermediate.html|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 ([[http://consumer.easyxdm.net/current/example/resize_iframe.html|voir exemple]]). Enfin, toute page invitée affichée au cours de la navigation doit avoir été modifiée en conséquence ([[http://provider.easyxdm.net/current/example/resized_iframe_1.html|voir exemple]]).
 +== Variante ==
 +Dans la page-hôtesse
 +<code>
 +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";
 +    }
 +});
 +</code>
 +Dans chaque page-invitée, à la suite du contenu :
 +<code>
 +var transport = new easyXDM.transport.BestAvailableTransport({}, function(){
 +    transport.postMessage(document.body.scrollHeight);
 +});
 +</code>
 +
 +
 +=== HMTL 5===
 +Sources : [[http://www.w3.org/TR/webmessaging/#web-messaging|la norme]] (w3c - en), [[https://en.wikipedia.org/wiki/Cross-document_messaging|le principe]] (wp - en), [[https://developer.mozilla.org/en-US/docs/DOM/window.postMessage|une explication fouillée]] (mozzila - en), [[http://stackoverflow.com/questions/5908676/yet-another-cross-domain-iframe-resize-qa|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 :
 +
 +<code>
 +<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();">
 +     ...
 +</code>
 +
 +Ici, //element_id// sera replacé par l'id de l'élément de la page invitée dont on souhaite transmettre la hauteur ((Dans le cas le plus grossier et le plus courant, l'élément //body// est un bon candidat. Dans ce cas (élément unique dans un document valide), on peut adapter le code javascript pour récupérer la propriété de l'élément par son nom de balise, ce qui évite de mobiliser un attribut //id// que l'on ne maîtrise pas nécessairement (par ex., génération dynamique).)).
 +
 +Dans la page-hôtesse :
 +
 +<code>
 +<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>
 +</code>
 +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").