Czy istnieje międzydomenowa automatyczna zmiana rozmiaru wysokości elementu iframe, która działa?

110

Wypróbowałem kilka rozwiązań, ale nie udało mi się. Zastanawiam się, czy istnieje rozwiązanie, najlepiej z łatwym do naśladowania samouczkiem.

J82
źródło
2
Jakie rozwiązania nie powiodły się?
Yzmir Ramirez
1
Wypróbowałem ten, ale w przeglądarkach webkitowych wariuje, jak podano w artykule: css-tricks.com/cross-domain-iframe-resizing Był inny, ale nie pamiętam adresu URL.
J82

Odpowiedzi:

68

Masz trzy możliwości:

1. Użyj zmiany rozmiaru iFrame

Jest to prosta biblioteka do utrzymywania rozmiaru iFrame zgodnie z zawartością. Używa interfejsów API PostMessage i MutationObserver, z rezerwami dla IE8-10. Zawiera również opcje dla strony zawartości, aby zażądać, aby zawierający element iFrame miał określony rozmiar, a także może zamknąć element iFrame, gdy skończysz.

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

2. Użyj Easy XDM (kombinacja PostMessage + Flash)

Easy XDM wykorzystuje zbiór sztuczek umożliwiających komunikację między domenami między różnymi oknami w wielu przeglądarkach, a są przykłady użycia go do zmiany rozmiaru elementu 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 działa przy użyciu PostMessage w nowoczesnych przeglądarkach i rozwiązania opartego na Flashu jako rezerwy dla starszych przeglądarek.

Zobacz także ten wątek na Stackoverflow (są też inne, to jest często zadawane pytanie). Wydaje się również, że Facebook stosuje podobne podejście .

3. Komunikuj się za pośrednictwem serwera

Inną opcją byłoby wysłanie wysokości iframe na twój serwer, a następnie sondowanie z tego serwera z nadrzędnej strony internetowej za pomocą JSONP (lub użyj długiej ankiety, jeśli to możliwe).

Janne Aukia
źródło
1
W przypadku PostMessage, jeśli już używasz jQuery, możesz również zajrzeć do fantastycznej wtyczki postMessage Bena Almana
rinogo
8
iFrame-Resizer potrzebuje dostępu do serwera, na którym znajduje się zawartość iframe. Możesz więc używać go tylko w domenach, które kontrolujesz.
Hokascha
Dobry chwyt, @Hokascha. Projekt twierdzi, że obsługuje międzydomenowe ramki iframe, ale przeczytanie dokumentów ujawnia, że ​​nadal wymaga on dostępu serwera do domeny osadzonej.
StockB
1
Żadna z tych „alternatyw” nie jest rzeczywistym rozwiązaniem tego problemu. Pytający chce ustawić wysokość iFrame dla międzydomenowych iFrame z witryn internetowych, nad którymi nie ma kontroli. 1. Wymaga dostępu do serwera. 2. Wymaga oprogramowania, które uważam za słabe. Easy XDM powstał jak 10 lat temu. Najnowsza wersja z 2019 roku wymaga flashowania . Na szczęście Flash jest martwy i nikt nie powinien na nim polegać. 3. Wymaga dostępu do serwera.
redanimalwar
26

Mam rozwiązanie umożliwiające dynamiczne ustawianie wysokości elementu iframe na podstawie jego zawartości. Działa to w przypadku treści między domenami. Aby to osiągnąć, należy wykonać kilka kroków.

  1. Załóżmy, że dodano element iframe na stronie internetowej „abc.com/page”

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

  2. Następnie musisz powiązać zdarzenie "wiadomość" systemu Windows pod stroną "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);        
    }
});

Podczas ładowania elementu iframe musisz wysłać wiadomość do zawartości okna iframe z komunikatem „FrameHeight”:

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. Na stronie głównej, która została dodana pod iframe tutaj "xyz.pqr / contactpage", musisz powiązać zdarzenie "message" z Windows, gdzie wszystkie wiadomości będą odbierane z okna nadrzędnego "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
źródło
3
Działa płynnie. Dzięki!
Alex Leonov
po prostu jest to najbardziej techniczna metoda, jaką mogłem znaleźć, świetna robota @sudhir, dziękuję :)
java acm
14

To, co zrobiłem, to porównanie elementu iframe scrollWidth, aż zmienił rozmiar, podczas gdy przyrostowo ustawiłem wysokość IFrame. I dla mnie zadziałało dobrze. Możesz dostosować przyrost według uznania.

   <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
źródło
4
prawdopodobnie chcesz dodać ograniczenie do liczby pętli, w przeciwnym razie „while” może przerodzić się w nieskończoną pętlę.
Kevin Seifert
1
To powoduje awarię mojej strony.
DDDD
Dobre proste rozwiązanie. Ale w moim przypadku przyrost rozmiaru jest ograniczony przez rozmiar ekranu.
Zeta
1

Mam skrypt, który znajduje się w ramce iframe wraz z zawartością. Sprawdza również, czy istnieje element iFrameResizer (wstawia go jako skrypt), a następnie dokonuje zmiany rozmiaru.

Poniżej przedstawię uproszczony przykład.

// /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();
    }    
}())

Następnie zawartość iframe można wstawić w dowolnym miejscu na innej stronie / witrynie za pomocą następującego skryptu:

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

Treść iframe zostanie wstawiona poniżej, gdziekolwiek umieścisz tag script.

Mam nadzieję, że to komuś pomoże. 👍

ctrlplusb
źródło
Świetnie, dodałem <script ... data-src="http://google.com">i wypełniłem nim iframe src.
BananaAcid
W chwili pisania aktualna wersja to[email protected]
BananaAcid
1
... rozszerzony do pełnego użytecznych przykładowych kodówandbox.io
remote
@BananaAcid - Twój kod link do skrzynki nie działa
ctrlplusb
dzięki za wzmiankę o tym, linki stały się : codeandbox.io/s/remote-embed-ifrm-yz0xl . (uwaga: kodyandbox „fałszują” zawartość wielu stron i mogą się zawiesić. Ponowne
załadowanie
1

Oto moje proste rozwiązanie na tej stronie. http://lab.ohshiftlabs.com/iframesize/

Oto jak to działa;

wprowadź opis obrazu tutaj

Zasadniczo, jeśli możesz edytować stronę w innej domenie, możesz umieścić inną stronę iframe należącą do twojego serwera, która zapisuje wysokość do plików cookie. Z częstotliwością odczytywania plików cookie podczas aktualizacji zaktualizuj wysokość elementu iframe. To wszystko.

Pobieranie; http://lab.ohshiftlabs.com/iframesize/iframesizepost.zip

Edycja: grudzień 2019

Powyższe rozwiązanie zasadniczo wykorzystuje inny element iframe wewnątrz elementu iframe. 3. element iframe należy do domeny głównej strony, którą nazywasz ciągiem zapytania, który zapisuje wartość rozmiaru w pliku cookie, zewnętrzna strona sprawdza to zapytanie z pewnymi odstępami czasu. Ale to nie jest dobre rozwiązanie, więc powinieneś postępować zgodnie z tym:

Na górze strony:

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

Tutaj możesz sprawdzić, m.originskąd pochodzi.

Na stronie z ramką:

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

Chociaż nie zapominaj, że nie jest to tak bezpieczny sposób. Aby była bezpieczna, zaktualizuj * wartość (targetOrigin) z żądaną wartością. Skorzystaj z dokumentacji: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

siniradam
źródło
1
Te linki są teraz martwe (404), a treść nie jest podsumowana w odpowiedzi. :(
StockB,
1
linki są nadal martwe. 404
Arslan Ameer
0

Znalazłem inne rozwiązanie po stronie serwera dla twórców stron internetowych, które używa PHP do uzyskania rozmiaru elementu iframe.

Pierwszy to użycie skryptu serwera PHP do wywołania zewnętrznego za pośrednictwem funkcji wewnętrznej: (jak file_get_contentswith but curl i 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>

Musisz wyświetlić pod div ( iframe_reserve) kod HTML wygenerowany przez wywołanie funkcji za pomocą prostegoecho curl_get_file_contents("location url iframe","activation proxy")

Po wykonaniu tej czynności funkcja zdarzenia body ładowana za pomocą javascript pobiera wysokość elementu iframe strony tylko za pomocą prostej kontroli zawartości div ( iframe_reserve)

Więc użyłem divHeight = document.getElementById("iframe_reserve").clientHeight;wysokości zewnętrznej strony, którą wywołamy po zamaskowaniu kontenera div ( iframe_reserve). Po tym ładujemy iframe o dobrej wysokości i to wszystko.

headmax
źródło
0

Napotkałem ten problem podczas pracy nad czymś w pracy (używając React). Zasadniczo mamy zewnętrzną zawartość HTML, którą zapisujemy w naszej tabeli dokumentów w bazie danych, a następnie wstawiamy na stronę w określonych okolicznościach, gdy jesteś w zestawie danych Dokumenty.

Tak więc, biorąc pod uwagę elementy nśródliniowe, z których maksymalnie nmoże zawierać zewnętrzny kod HTML, musieliśmy opracować system, który będzie automatycznie zmieniał rozmiar elementu iframe każdego elementu wbudowanego po całkowitym załadowaniu zawartości każdego z nich. Po krótkim zakręceniu kołami skończyło się na tym:

  1. Ustaw messagedetektor zdarzeń w indeksie naszej aplikacji React, który sprawdza określony klucz, który ustawimy z elementu iframe nadawcy.
  2. W komponencie, który faktycznie renderuje elementy iframe, po wstawieniu do niego zewnętrznego <script>kodu HTML, dołączam tag, który będzie czekał na uruchomienie elementu iframe window.onload. Po postMessageuruchomieniu wysyłamy wiadomość do okna nadrzędnego z informacjami o identyfikatorze iframe, obliczonej wysokości itp.
  3. Jeśli źródło jest zgodne i klucz jest spełniony w odbiorniku indeksu, pobierz DOM idelementu iframe, który przekazujemy w MessageEventobiekcie
  4. Gdy już mamy iframe, po prostu ustaw wysokość na podstawie wartości, która jest przekazywana z elementu 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
źródło