Existe-t-il un redimensionneur automatique de hauteur iframe inter-domaines qui fonctionne?

110

J'ai essayé quelques solutions mais je n'ai pas réussi. Je me demande s'il existe une solution de préférence avec un tutoriel facile à suivre.

J82
la source
2
Quelles solutions avez-vous essayées qui n'ont pas abouti?
Yzmir Ramirez
1
J'ai essayé celui-ci mais ça panique dans les navigateurs webkit comme indiqué dans l'article: css-tricks.com/cross-domain-iframe-resizing Il y en avait un autre mais je ne me souviens plus de l'url.
J82

Réponses:

68

Vous avez trois alternatives:

1. Utilisez iFrame-resizer

Il s'agit d'une bibliothèque simple pour garder les iFrames dimensionnés en fonction de leur contenu. Il utilise les API PostMessage et MutationObserver, avec des solutions de secours pour IE8-10. Il a également des options pour que la page de contenu demande que l'iFrame contenant soit une certaine taille et peut également fermer l'iFrame lorsque vous en avez terminé.

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

2. Utilisez Easy XDM (combo PostMessage + Flash)

Easy XDM utilise une collection d'astuces pour activer la communication interdomaine entre différentes fenêtres dans un certain nombre de navigateurs, et il existe des exemples d'utilisation pour le redimensionnement iframe:

http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/

http://kinsey.no/blog/index.php/2010/02/19/resizing-iframes-using-easyxdm/

Easy XDM fonctionne en utilisant PostMessage sur les navigateurs modernes et une solution basée sur Flash comme solution de secours pour les navigateurs plus anciens.

Voir aussi ce fil sur Stackoverflow (il y en a aussi d'autres, c'est une question fréquemment posée). En outre, Facebook semble utiliser une approche similaire .

3. Communiquez via un serveur

Une autre option serait d'envoyer la hauteur de l'iframe à votre serveur, puis d'interroger ce serveur à partir de la page Web parente avec JSONP (ou d'utiliser un long sondage si possible).

Janne Aukia
la source
1
Pour PostMessage, si vous utilisez déjà jQuery, vous pouvez également consulter le fantastique plugin postMessage de Ben Alman: benalman.com/projects/jquery-postmessage-plugin
rinogo
8
iFrame-resizer a besoin d'accéder au serveur qui héberge le contenu iframe. Vous ne pouvez donc l'utiliser que pour les domaines que vous contrôlez.
Hokascha
Bonne prise, @Hokascha. Le projet revendique la prise en charge des iframes inter-domaines, mais la lecture de la documentation révèle qu'il nécessite toujours un accès serveur au domaine intégré.
StockB
1
Aucune de ces «alternatives» ne constitue de véritables solutions à ce problème. Le demandeur souhaite définir la hauteur iFrame des iFrames interdomaines à partir de sites Web sur lesquels il n'a pas de contrôle. 1. Nécessite un accès au serveur. 2. Nécessite un logiciel que je considère comme médiocre. Easy XDM a été créé il y a 10 ans. La dernière version, à partir de 2019, nécessite le flash . Flash est, heureusement, mort et personne ne devrait s'y fier. 3. Nécessite un accès au serveur.
redanimalwar
26

J'ai eu la solution pour définir la hauteur de l'iframe de manière dynamique en fonction de son contenu. Cela fonctionne pour le contenu interdomaine. Il y a quelques étapes à suivre pour y parvenir.

  1. Supposons que vous ayez ajouté un iframe dans la page Web "abc.com/page"

    <div> <iframe id="IframeId" src="http://xyz.pqr/contactpage" style="width:100%;" onload="setIframeHeight(this)"></iframe> </div>

  2. Ensuite, vous devez lier l'événement Windows "message" sous la page Web "abc.com/page"

window.addEventListener('message', function (event) {
//Here We have to check content of the message event  for safety purpose
//event data contains message sent from page added in iframe as shown in step 3
if (event.data.hasOwnProperty("FrameHeight")) {
        //Set height of the Iframe
        $("#IframeId").css("height", event.data.FrameHeight);        
    }
});

Lors du chargement d'iframe, vous devez envoyer un message au contenu de la fenêtre iframe avec le message "FrameHeight":

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. Sur la page principale qui a été ajoutée sous iframe ici "xyz.pqr / contactpage", vous devez lier l'événement "message" de Windows où tous les messages vont recevoir de la fenêtre parente de "abc.com/page"
window.addEventListener('message', function (event) {

    // Need to check for safety as we are going to process only our messages
    // So Check whether event with data(which contains any object) contains our message here its "FrameHeight"
   if (event.data == "FrameHeight") {

        //event.source contains parent page window object 
        //which we are going to use to send message back to main page here "abc.com/page"

        //parentSourceWindow = event.source;

        //Calculate the maximum height of the page
        var body = document.body, html = document.documentElement;
        var height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

       // Send height back to parent page "abc.com/page"
        event.source.postMessage({ "FrameHeight": height }, "*");       
    }
});
Sudhir
la source
3
Fonctionne en douceur. Merci des tas!
Alex Leonov
c'est simplement la méthode la plus technique que j'ai pu trouver, excellent travail @sudhir, merci :)
java acm
14

Ce que j'ai fait, c'est comparer l'iframe scrollWidth jusqu'à ce qu'il change de taille pendant que je règle incrémentalement la hauteur IFrame. Et cela a bien fonctionné pour moi. Vous pouvez ajuster l'incrément à ce que vous souhaitez.

   <script type="text/javascript">
    function AdjustIFrame(id) {
        var frame = document.getElementById(id);
        var maxW = frame.scrollWidth;
        var minW = maxW;
        var FrameH = 100; //IFrame starting height
        frame.style.height = FrameH + "px"

        while (minW == maxW) {
            FrameH = FrameH + 100; //Increment
            frame.style.height = FrameH + "px";
            minW = frame.scrollWidth;
        }
    }

   </script>


<iframe id="RefFrame" onload="AdjustIFrame('RefFrame');" class="RefFrame"
    src="http://www.YourUrl.com"></iframe>
Troy Mitchel
la source
4
vous voulez probablement ajouter un plafond sur le nombre de boucles, sinon le «while» peut dégénérer en une boucle infinie.
Kevin Seifert
1
Cela plante ma page.
DDDD
Bonne solution simple. Mais dans mon caes, l'incrément de taille est plafonné par la taille de l'écran.
Zeta
1

J'ai un script qui tombe dans l'iframe avec son contenu. Il s'assure également que iFrameResizer existe (il l'injecte en tant que script), puis effectue le redimensionnement.

Je vais vous présenter un exemple simplifié ci-dessous.

// /js/embed-iframe-content.js

(function(){
    // Note the id, we need to set this correctly on the script tag responsible for
    // requesting this file.
    var me = document.getElementById('my-iframe-content-loader-script-tag');

    function loadIFrame() {
        var ifrm = document.createElement('iframe');
        ifrm.id = 'my-iframe-identifier';
        ifrm.setAttribute('src', 'http://www.google.com');
        ifrm.style.width = '100%';
        ifrm.style.border = 0;
        // we initially hide the iframe to avoid seeing the iframe resizing
        ifrm.style.opacity = 0;
        ifrm.onload = function () {
            // this will resize our iframe
            iFrameResize({ log: true }, '#my-iframe-identifier');
            // make our iframe visible
            ifrm.style.opacity = 1;
        };

        me.insertAdjacentElement('afterend', ifrm);
    }

    if (!window.iFrameResize) {
        // We first need to ensure we inject the js required to resize our iframe.

        var resizerScriptTag = document.createElement('script');
        resizerScriptTag.type = 'text/javascript';

        // IMPORTANT: insert the script tag before attaching the onload and setting the src.
        me.insertAdjacentElement('afterend', ifrm);

        // IMPORTANT: attach the onload before setting the src.
        resizerScriptTag.onload = loadIFrame;

        // This a CDN resource to get the iFrameResizer code.
        // NOTE: You must have the below "coupled" script hosted by the content that
        // is loaded within the iframe:
        // https://unpkg.com/[email protected]/js/iframeResizer.contentWindow.min.js
        resizerScriptTag.src = 'https://unpkg.com/[email protected]/js/iframeResizer.min.js';
    } else {
        // Cool, the iFrameResizer exists so we can just load our iframe.
        loadIFrame();
    }    
}())

Ensuite, le contenu iframe peut être injecté n'importe où dans une autre page / site en utilisant le script comme suit:

<script
  id="my-iframe-content-loader-script-tag"
  type="text/javascript"
  src="/js/embed-iframe-content.js"
></script>

Le contenu iframe sera injecté ci-dessous où que vous placiez la balise de script.

J'espère que cela est utile à quelqu'un. 👍

ctrlplusb
la source
Bien, j'ai ajouté <script ... data-src="http://google.com">et rempli l'iframe src avec.
BananaAcid
Au moment de l'écriture, la version actuelle est[email protected]
BananaAcid
1
... étendu à un exemple complet utilisable codesandbox.io/s/remote-embed-ifrm-qdb74
BananaAcid
@BananaAcid - votre lien codesandbox ne fonctionne plus
ctrlplusb
merci de l'avoir mentionné, les liens sont devenus: codesandbox.io/s/remote-embed-ifrm-yz0xl . (Remarque: codesandbox "fakes" utilisant plusieurs pages et pourrait se bloquer. Le rechargement pourrait aider)
BananaAcid
1

Voici ma solution simple sur cette page. http://lab.ohshiftlabs.com/iframesize/

Voici comment cela fonctionne;

entrez la description de l'image ici

Fondamentalement, si vous pouvez modifier la page sur un autre domaine, vous pouvez placer une autre page iframe qui appartient à votre serveur, ce qui économise de la hauteur aux cookies. Avec un intervalle de lecture des cookies lors de sa mise à jour, mettez à jour la hauteur de l'iframe. C'est tout.

Télécharger; http://lab.ohshiftlabs.com/iframesize/iframesizepost.zip

Edit: décembre 2019

La solution ci-dessus utilise essentiellement une autre iframe à l'intérieur d'un iframe 3ème iframe appartient au domaine de la page supérieure, que vous appelez cette page avec une chaîne de requête qui enregistre la valeur de la taille dans un cookie, la page externe vérifie cette requête avec un certain intervalle. Mais ce n'est pas une bonne solution, vous devez donc suivre celle-ci:

En haut de la page:

window.addEventListener("message", (m)=>{iframeResizingFunction(m)});

Ici, vous pouvez vérifier m.origind'où il vient.

Dans la page cadre:

window.parent.postMessage({ width: 640, height:480 }, "*")

Cependant, n'oubliez pas que ce n'est pas un moyen sûr. Pour le rendre sécurisé, mettez à jour la valeur * (targetOrigin) avec la valeur souhaitée. Veuillez suivre la documentation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

siniradam
la source
1
Ces liens sont maintenant morts (404) et le contenu n'est pas résumé dans la réponse. :(
StockB
1
les liens sont toujours morts. 404
Arslan Ameer
0

J'ai trouvé une autre solution côté serveur pour les développeurs Web en utilisant PHP pour obtenir la taille d'une iframe.

La première consiste à utiliser le script serveur PHP pour un appel externe via une fonction interne: (comme file_get_contentsavec mais curl et dom).

function curl_get_file_contents($url,$proxyActivation=false) {
    global $proxy;
    $c = curl_init();
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7");
    curl_setopt($c, CURLOPT_REFERER, $url);
    curl_setopt($c, CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
    if($proxyActivation) {
        curl_setopt($c, CURLOPT_PROXY, $proxy);
    }
    $contents = curl_exec($c);
    curl_close($c);
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    @$dom->loadHTML($contents);
    $form = $dom->getElementsByTagName("body")->item(0);
    if ($contents) //si on a du contenu
        return $dom->saveHTML();
    else
        return FALSE;
}
$url = "http://www.google.com"; //Exernal url test to iframe
<html>
    <head>
    <script type="text/javascript">

    </script>
    <style type="text/css">
    #iframe_reserve {
        width: 560px;
        height: 228px
    }
    </style>
    </head>

    <body>
        <div id="iframe_reserve"><?php echo curl_get_file_contents($url); ?></div>

        <iframe id="myiframe" src="http://www.google.com" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"  style="overflow:none; width:100%; display:none"></iframe>

        <script type="text/javascript">
            window.onload = function(){
            document.getElementById("iframe_reserve").style.display = "block";
            var divHeight = document.getElementById("iframe_reserve").clientHeight;
            document.getElementById("iframe_reserve").style.display = "none";
            document.getElementById("myiframe").style.display = "block";
            document.getElementById("myiframe").style.height = divHeight;
            alert(divHeight);
            };
        </script>
    </body>
</html>

Vous devez afficher sous le div ( iframe_reserve) le html généré par l'appel de fonction en utilisant un simpleecho curl_get_file_contents("location url iframe","activation proxy")

Après cela, une fonction d'événement body onload avec javascript prend la hauteur de la page iframe juste avec un simple contrôle du contenu div ( iframe_reserve)

J'ai donc utilisé divHeight = document.getElementById("iframe_reserve").clientHeight;pour obtenir la hauteur de la page externe que nous allons appeler après avoir masqué le conteneur div ( iframe_reserve). Après cela, nous chargeons l'iframe avec sa bonne hauteur c'est tout.

headmax
la source
0

J'ai rencontré ce problème en travaillant sur quelque chose au travail (en utilisant React). Fondamentalement, nous avons du contenu html externe que nous sauvegardons dans notre table de documents dans la base de données, puis insérons sur la page dans certaines circonstances lorsque vous êtes dans l'ensemble de données Documents.

Ainsi, étant donné les ninlines, dont jusqu'à npouvait contenir du html externe, nous devions concevoir un système pour redimensionner automatiquement l'iframe de chaque inline une fois le contenu entièrement chargé dans chacun. Après avoir fait tourner mes roues pendant un moment, voici comment j'ai fini par le faire:

  1. Définissez un messageécouteur d'événements dans l'index de notre application React qui recherche une clé spécifique que nous définirons à partir de l'iframe de l'expéditeur.
  2. Dans le composant qui rend réellement les iframes, après y avoir inséré le html externe, j'ajoute une <script>balise qui attendra que l'iframe window.onloadse déclenche. Une fois que cela se déclenche, nous utilisons postMessagepour envoyer un message à la fenêtre parente avec des informations sur l'identifiant iframe, la hauteur calculée, etc.
  3. Si l'origine correspond et que la clé est satisfaite dans l'écouteur d'index, récupérez le DOM idde l'iframe que nous passons dans l' MessageEventobjet
  4. Une fois que nous avons le iframe, définissez simplement la hauteur à partir de la valeur transmise par l'iframe postMessage.
// index
if (window.postMessage) {
    window.addEventListener("message", (messageEvent) => {
        if (
            messageEvent.data.origin &&
            messageEvent.data.origin === "company-name-iframe"
        ) {
            const iframe = document.getElementById(messageEvent.data.id)
            // this is the only way to ensure that the height of the iframe container matches its body height
            iframe.style.height = `${messageEvent.data.height}px`
            // by default, the iframe will not expand to fill the width of its parent
            iframe.style.width = "100%"
            // the iframe should take precedence over all pointer events of its immediate parent
            // (you can still click around the iframe to segue, for example, but all content of the iframe
            // will act like it has been directly inserted into the DOM)
            iframe.style.pointerEvents = "all"
            // by default, iframes have an ugly web-1.0 border
            iframe.style.border = "none"
        }
    })
}
// in component that renders n iframes
<iframe
    id={`${props.id}-iframe`}
    src={(() => {
        const html = [`data:text/html,${encodeURIComponent(props.thirdLineData)}`]
        if (window.parent.postMessage) {
            html.push(
                `
                <script>
                window.onload = function(event) {
                    window.parent.postMessage(
                        {
                            height: document.body.scrollHeight,
                            id: "${props.id}-iframe",
                            origin: "company-name-iframe",
                        },
                        "${window.location.origin}"
                    );
                };
                </script>
                `
            )
        }

        return html.join("\n")
    })()}
    onLoad={(event) => {
        // if the browser does not enforce a cross-origin policy,
        // then just access the height directly instead
        try {
            const { target } = event
            const contentDocument = (
                target.contentDocument ||
                // Earlier versions of IE or IE8+ where !DOCTYPE is not specified
                target.contentWindow.document
            )
            if (contentDocument) {
                target.style.height = `${contentDocument.body.scrollHeight}px`
            }
        } catch (error) {
            const expectedError = (
                `Blocked a frame with origin "${window.location.origin}" ` +
                `from accessing a cross-origin frame.`
            )
            if (error.message !== expectedError) {
                /* eslint-disable no-console */
                console.err(
                    `An error (${error.message}) ocurred while trying to check to see ` +
                    "if the inner iframe is accessible or not depending " +
                    "on the browser cross-origin policy"
                )
            }
        }
    }}
/>
John Duncan
la source