87 votes

Redimensionnement des iframes inter-domaines

Comment redimensionner un iframe d'un autre domaine ?

-Editer

Faites défiler la page pour trouver des solutions ou lisez comment NE PAS le faire :D

Après de nombreuses heures de bidouillage de code, la conclusion est que tout ce qui se trouve à l'intérieur de l'iframe n'est pas accessible, même les barres de défilement qui s'affichent sur mon domaine. J'ai essayé de nombreuses techniques, sans succès.

Pour gagner du temps, n'empruntez pas cette voie, utilisez simplement sendMessages pour les communications entre domaines. Il y a des plug-ins pour HTML < 5 que j'utilise - Allez en bas pour un bel exemple :)


Ces derniers jours, j'ai essayé d'intégrer une iframe dans un site. Il s'agit d'une solution à court terme pendant que l'autre partie développe une API (cela pourrait prendre des mois...). Et parce qu'il s'agit d'une solution à court terme, nous ne voulons pas utiliser easyXDM - J'ai accès à l'autre domaine mais il est déjà assez difficile de leur demander d'ajouter l'en-tête p3p tel qu'il est......

3 iframes

La solution la plus proche que j'ai trouvée était les 3 iframes- mais cela fait perdre la tête à chrome et safari donc je ne peux pas l'utiliser.

ouvert en chrome

http://css-tricks.com/examples/iFrameResize/crossdomain.php#frameId=frame-one&height=1179

Mesurer les barres de défilement

J'ai trouvé un autre post sur la façon d'utiliser la hauteur de défilement pour essayer de redimensionner le formulaire en théorie, cela fonctionne bien mais je n'ai pas pu l'appliquer correctement en utilisant la hauteur de défilement des iframes .

document.body.scrollHeight

Cela utilise évidemment la hauteur des corps (il n'est pas possible d'accéder à ces propriétés - 100% est basé sur la zone d'affichage du client et non sur la hauteur du document du domaine X).

J'ai essayé d'utiliser Jquery pour obtenir la hauteur des iframes.

$('#frameId').Height()

$('#frameId').clientHeight

$('#frameId').scrollHeight

les valeurs de retour sont différentes dans chrome et ie - ou n'ont pas de sens du tout. Le problème est que tout ce qui se trouve à l'intérieur du cadre est refusé - même la barre de défilement...

Styles calculés

Mais si j'inspecte un élément de l'iframe dans Chrome, il me montre les dimensions du document à l'intérieur de l'iframe (en utilisant jquery x-domain pour obtenir iframe.heigh - accès refusé). Il n'y a rien dans le CSS calculé enter image description here

Maintenant, comment chrome calcule-t-il cela ? (edit- le navigateur re-rend la page en utilisant son moteur de rendu intégré pour calculer tous ces paramètres - mais ils ne sont attachés nulle part pour éviter la fraude inter-domaine donc )

HTML4

J'ai lu les spécifications de HTML4.x et il y est dit qu'il devrait y avoir des valeurs en lecture seule exposées via document.element mais l'accès est refusé via jquery.

Cadre Proxy

J'ai emprunté la voie de la transmission par proxy du site et du calcul, ce qui est correct, jusqu'à ce qu'un utilisateur se connecte via l'iframe et que le proxy reçoive une page de connexion au lieu du contenu réel. De plus, pour certains, appeler la page deux fois n'est pas acceptable.

http://www.codeproject.com/KB/aspnet/asproxy.aspx

http://www.johnchapman.name/aspnet-proxy-page-cross-domain-requests-from-ajax-and-javascript/

Rendu de la page

Je ne suis pas allé aussi loin, mais il existe des moteurs jscript qui regardent la source et rendent à nouveau la page en se basant sur le fichier source... mais il faudrait pirater ces jscripts... et ce n'est pas une situation idéale pour les entités commerciales... et certains font appel à des applets java ou à un rendu côté serveur.

http://en.wikipedia.org/wiki/Server-side_JavaScript

http://htmlunit.sourceforge.net/ <-java pas jscript

http://maxq.tigris.org/


EDIT 09-2013 UPDATE

Tout cela peut être fait avec des sockets HTML5. Mais easyXDM est une excellente solution de repli pour les pages de plaintes non HTML5.

Solution 1 Très bonne solution !

Utilisation d'easyXDM

Sur votre serveur, vous avez mis en place une page sous la forme de

<html>
<head>
<script src="scripts/easyXDM.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">

    var transport = new easyXDM.Socket(/** The configuration */{
    remote: "http://www.OTHERDOMAIN.com/resize_intermediate.html?url=testpages/resized_iframe_1.html",

    //ID of the element to attach the inline frame to
    container: "embedded",
    onMessage: function (message, origin) {
        var settings = message.split(",");
        //Use jquery on a masterpage.
        //$('iframe').height(settings[0]);
        //$('iframe').width(settings[1]);

        //The normal solution without jquery if not using any complex pages (default)
        this.container.getElementsByTagName("iframe")[0].style.height = settings[0];
        this.container.getElementsByTagName("iframe")[0].style.width = settings[1];
    }
});

</script>

</head>

<body>
    <div id="embedded"></div>
</body>

et sur le domaine de l'appelant, il suffit d'ajouter le html intermiedate_frame et easyXDM.js au même endroit. Comme un dossier parent - vous pouvez alors accéder à des répertoires relatifs ou à un dossier contenu juste pour vous.

OPTION 1

Si vous ne voulez pas ajouter des scripts à toutes les pages, regardez l'option 2 !

Il leur suffit alors d'ajouter un simple jscript à la fin de chacune des pages sur lesquelles le redimensionnement doit avoir lieu. Il n'est pas nécessaire d'inclure l'easyxdm dans chacune de ces pages.

 <script type="text/javascript">
            window.onload = function(){ parent.socket.postMessage( (parseInt(document.body.clientHeight)) + "," + ( document.body.clientWidth ) );  };
        </script>

J'ai modifié les paramètres qu'il envoie. Si vous voulez que la largeur fonctionne correctement, les pages de l'autre domaine doivent inclure la largeur de la page dans un style quelque part qui ressemble à ceci :

<style type="text/css">
            html, body {
                overflow: hidden;
                margin: 0px;
                padding: 0px;
                background-color: rgb(75,0,85);
                color:white;
                width:660px
            }
            a {
            color:white;
            visited:white;
            }
        </style>

Cela fonctionne très bien pour moi. Si la largeur n'est pas incluse, le cadre se comporte un peu bizarrement et essaie de deviner ce qu'il devrait être et ne se rétrécit pas si vous en avez besoin.

OPTION 2

Modifier la trame intermédiaire afin d'interroger les changements

Votre cadre intermédiaire devrait ressembler à quelque chose comme ceci

    <!doctype html>
<html>
    <head>

        <title>Frame</title>
        <script type="text/javascript" src="easyXDM.js">
        </script>
        <script type="text/javascript">
            var iframe;
            var socket = new easyXDM.Socket({
                //This is a fallback- not needed in many cases
                swf: "easyxdm.swf",
                onReady: function(){
                    iframe = document.createElement("iframe");
                    iframe.frameBorder = 0;
                    document.body.appendChild(iframe);
                    iframe.src = "THE HOST FRAME";
            iframe.onchange = messageBack();

                },
                onMessage: function(url, origin){
                    iframe.src = url;
                }
            });

            //Probe child.frame for dimensions.
            function messageBack(){
                socket.postMessage ( iframe.contentDocument.body.clientHeight + "," + iframe.contentDocument.body.clientWidth); 
            };

            //Poll for changes on children every 500ms.
            setInterval("messageBack()",500); 

        </script>
        <style type="text/css">
            html, body {
                overflow: hidden;
                margin: 0px;
                padding: 0px;
                width: 100%;
                height: 100%;
            }

            iframe {
                width: 100%;
                height: 100%;
                border: 0px;
            }
        </style>
    </head>
    <body>

    </body>
</html>

L'intervalle pourrait être rendu plus efficace en vérifiant si la taille a changé et en ne l'envoyant que si les dimensions changent, au lieu d'envoyer un message toutes les 500 ms. Si vous mettez en œuvre cette vérification, vous pouvez réduire l'intervalle d'envoi à 50 ms !


Fonctionne sur tous les navigateurs et est rapide. Superbes fonctions de débogage !

Excellent Work to  Sean Kinsey  who made the script!!!

Solution 2 (Fonctionne mais n'est pas génial)

Donc, en gros, si vous avez un accord mutuel avec l'autre domaine, vous pouvez ajouter une bibliothèque pour gérer le sendmessage. Si vous n'avez pas d'accès à l'autre domaine Continuez à chercher d'autres astuces, car je n'ai pas pu trouver ou justifier pleinement celles que j'ai trouvées.

Ainsi, l'autre domaine inclura ces éléments dans sa balise Head.

<script src="scripts/jquery-1.5.2.min.js" type="text/javascript"></script>
<script src="scripts/jquery.postmessage.min.js" type="text/javascript"></script>
<script src="scripts/club.js" type="text/javascript"></script>

Dans le club.js il y a juste quelques appels personnalisés que j'ai fait pour les appels de redimensionnement et contient

 $(document).ready(function () {   
    var parent_url = decodeURIComponent( document.location.hash.replace( /^#/, '' ) ),link;
//Add source url hash to each url to authorise callback when navigating inside the frame.Without this clicking any links will break the communication and no messages will be received
$('a[href]').each(function(){ 
     this.href = this.href + document.location.hash ;
});
//Get the dimensions and send back to calling page.
var h1 = document.body.scrollHeight;
var w1 = document.body.scrollWidth;
$.postMessage({ if_height: h1, if_width: w1 }, parent_url, parent );
  });

Et votre page fera tout le travail et aura un joli script...

  //This is almost like request.querystring used to get the iframe data
  function querySt(param, e) {
         gy = e.split("&");
         for (i = 0; i < gy.length; i++) {
             ft = gy[i].split("=");
             if (ft[0] == param) {
                 return ft[1];
             }
         }
     }

     $(function () {
         // Keep track of the iframe dimensions.
         var if_height;
         var if_width;
         // Pass the parent page URL into the Iframe in a meaningful way (this URL could be
         // passed via query string or hard coded into the child page, it depends on your needs).
         src = 'http://www.OTHERDOAMIN.co.uk/OTHERSTARTPAGE.htm' + '#' + encodeURIComponent(document.location.href),
         // Append the Iframe into the DOM.
         iframe = $('<iframe " src="' + src + '" width="100%" height="100%" scrolling="no" frameborder="0"><\/iframe>').appendTo('#iframe');

         // Setup a callback to handle the dispatched MessageEvent event. In cases where
         // window.postMessage is supported, the passed event will have .data, .origin and
         // .source properties. Otherwise, this will only have the .data property.
         $.receiveMessage(function (e) {
             // Get the height from the passsed data.
             //var h = Number(e.data.replace(/.*if_height=(\d+)(?:&|$)/, '$1'));
             var h = querySt("if_height", e.data);
             var w = querySt("if_width", e.data);

             if (!isNaN(h) && h > 0 && h !== if_height) {
                 // Height has changed, update the iframe.
                 iframe.height(if_height = h);
             }
             if (!isNaN(w) && w > 0 && w !== if_width) {
                 // Height has changed, update the iframe.
                 iframe.width(if_width = w);
             }
             //For debugging only really- can remove the next line if you want
             $('body').prepend("Recieved" + h + "hX" + w + "w .. ");
             // An optional origin URL (Ignored where window.postMessage is unsupported).
           //Here you must put the other domain.com name only! This is like an authentication to prevent spoofing and xss attacks! 
         }, 'http://www.OTHERDOMAIN.co.uk');
     });

OPTION 3

Il existe maintenant une petite bibliothèque JS pour gérer le redimensionnement des iFrames inter-domaines. Il faut toujours que l'iFrame contienne un peu de JavaScript, mais il ne s'agit que de 2,8k (765 octets compressés) de JS natif qui n'a pas de dépendances et qui ne fait rien tant qu'il n'est pas appelé par la page parent. Cela signifie que c'est un invité agréable sur les systèmes des autres.

Ce code utilise mutationObserver pour détecter les modifications du DOM et guette également les événements de redimensionnement, de sorte que l'iFrame reste à la taille du contenu. Fonctionne dans IE8+.

https://github.com/davidjbradshaw/iframe-resizer

29voto

Baker Humadi Points 121

Comme Sean l'a mentionné, vous pouvez utiliser postMessage. J'ai passé beaucoup de temps à essayer différentes manières de redimensionner les iframe avec cross-domain, mais sans succès jusqu'à ce que je tombe sur cet excellent article de blog de David Walsh : http://davidwalsh.name/window-iframe

Il s'agit d'une combinaison de mon code et de la solution de David. Ma solution est orientée spécifiquement vers le redimensionnement des iframes.

Dans la page enfant, trouvez la hauteur de la page et passez-la à la page parent (qui contient l'iframe). Remplacez element_id par l'identifiant de votre élément (html, body, etc.).

<script>
function adjust_iframe_height(){
    var actual_height = document.getElementById('element_id).scrollHeight;
    parent.postMessage(actual_height,"*"); 
    //* allows this to post to any parent iframe regardless of domain
}
</script>

<body onload="adjust_iframe_height();"> 
//call the function above after the content of the child loads

Sur la fenêtre parent, ajoutez ce code. Remplacez iframe_id par l'ID de votre iframe :

<script>
// Create IE + others compatible event handler
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

// Listen to message from child window
eventer(messageEvent,function(e) {
  console.log('parent received message!:  ',e.data);
  document.getElementById('iframe_id').height = e.data + 'px';
},false);
</script>

Si vous ouvrez la console, vous verrez la hauteur s'afficher dans le journal de la console. Cela vous aidera dans le débogage, c'est pourquoi je l'ai laissé là.

Le meilleur, Baker

10voto

Sean Kinsey Points 17117

Le problème, c'est qu'il n'y a pas d'autre moyen que d'utiliser la messagerie interdomaine pour cela, puisque vous devez obtenir la hauteur calculée d'un document dans un domaine, vers un document dans un autre domaine.

Donc, soit vous le faites en utilisant postMessage (fonctionne dans tous les navigateurs modérés), ou bien vous passez 5 minutes à adapter la méthode exemple de redimensionnement d'un iframe de easyXDM.

Il suffit à l'autre partie de copier quelques fichiers sur son domaine, et d'ajouter une seule ligne de code à son document

5voto

David Bradshaw Points 271

Après avoir examiné un grand nombre de solutions différentes, j'ai fini par écrire une petite bibliothèque simple pour prendre en compte un certain nombre de cas d'utilisation différents. J'avais besoin d'une solution qui prenne en charge plusieurs iFrames générées par l'utilisateur sur une page de portail, qui prenne en charge les redimensionnements du navigateur et qui permette le chargement du JavaScript de la page hôte après l'iFrame. J'ai également ajouté la prise en charge du redimensionnement en fonction de la largeur et d'une fonction de rappel, et j'ai autorisé le remplacement de la marge body.margin, car il est probable que vous souhaitiez la définir sur zéro.

https://github.com/davidjbradshaw/iframe-resizer

Le code iframe n'est qu'un petit JavaScript autonome, de sorte qu'il est un bon invité sur les pages d'autres personnes.

Le script est ensuite initialisé sur la page hôte avec les options disponibles suivantes.

iFrameResize({
    log                    : true,  // For development
    autoResize             : true,  // Trigering resize on events in iFrame
    contentWindowBodyMargin: 8,     // Set the default browser body margin style (in px)
    doHeight               : true,  // Calculates dynamic height
    doWidth                : false, // Calculates dynamic width
    enablePublicMethods    : true,  // Enable methods within iframe hosted page 
    interval               : 32,    // interval in ms to recalculate body height, 0 to disable refreshing
    scrolling              : false, // Enable the scrollbars in the iFrame
    callback               : function(messageData){ // Callback fn when message is received
        $('p#callback').html(
            '<b>Frame ID:</b> '    + messageData.iframe.id +
            ' <b>Height:</b> '     + messageData.height +
            ' <b>Width:</b> '      + messageData.width + 
            ' <b>Event type:</b> ' + messageData.type
        );
    }
});

0voto

Elton Morais Points 1

Au lieu d'utiliser scroll=no sur l'iframe, je le change en "auto". Ensuite, j'obtiens la taille de la fenêtre actuelle.

$(window).height();

et l'utiliser comme attribut de hauteur de l'iframe.

Eh bien, le résultat est...

Nous n'aurons jamais le scroll "page", seulement le scroll "iframe". Lorsque vous naviguez, peu importe qui est le scroll, mais l'important est qu'il n'y en ait qu'un seul.

Il y a le problème de l'utilisateur qui redimensionne simplement la fenêtre pendant qu'il navigue. Pour résoudre cela, j'utilise :

setInterval(getDocHeight, 1);

Pensez-vous que cette solution va provoquer des bugs ? Cela fonctionne pour moi, et sur l'iFrame j'avais un contenu dynamique généré par php. J'ai peur des futurs bugs avec ça...

0voto

Aujourd'hui, il n'y a que 4 solutions que je connais :

Seule la troisième peut résoudre de nombreux problèmes. Par exemple vous pouvez créer une iFrame réactive ; la fermer de l'intérieur ou vous pouvez communiquer avec lui . Mais pour cela, vous avez besoin de iFrame dans Iframe et de la solution de contournement "cookies tiers" (facultatif).

J'ai créé un article à ce sujet avec un exemple : iFrame cross-domain piloté par les événements

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X