Есть ли работающее автоматическое изменение размера междоменного окна iframe?

111

Я попробовал несколько решений, но безуспешно. Мне интересно, есть ли решение, желательно с простым для понимания учебником.

J82
источник
2
Какие решения вы попробовали, но не увенчались успехом?
Измир Рамирес 08
1
Я пробовал этот, но он ужасает в браузерах webkit, как указано в статье: css-tricks.com/cross-domain-iframe-resizing Был еще один, но я не могу вспомнить URL-адрес.
J82 08

Ответы:

68

У вас есть три альтернативы:

1. Используйте iFrame-resizer.

Это простая библиотека для сохранения размеров iFrames в соответствии с их содержимым. Он использует API-интерфейсы PostMessage и MutationObserver с откатом для IE8-10. Он также имеет параметры для страницы содержимого, чтобы запросить содержащий iFrame определенный размер, а также может закрыть iFrame, когда вы закончите с ним.

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

2. Используйте Easy XDM (комбинация PostMessage + Flash).

Easy XDM использует набор уловок для обеспечения междоменной связи между различными окнами в ряде браузеров, и есть примеры его использования для изменения размера 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 работает с использованием PostMessage в современных браузерах и решения на основе Flash в качестве запасного варианта для старых браузеров.

См. Также эту ветку на Stackoverflow (есть и другие, это часто задаваемый вопрос). Кроме того, похоже , что Facebook использует аналогичный подход .

3. Общайтесь через сервер

Другой вариант - отправить высоту iframe на ваш сервер, а затем опросить с этого сервера с родительской веб-страницы с помощью JSONP (или, если возможно, использовать длинный опрос).

Янне Аукия
источник
1
Что касается PostMessage, если вы уже используете jQuery, вы также можете изучить фантастический плагин postMessage Бена
Алмана
8
iFrame-resizer требуется доступ к серверу, на котором размещено содержимое iframe. Таким образом, вы можете использовать его только для контролируемых вами доменов.
Hokascha
Хороший улов, @Hokascha. Проект заявляет о поддержке междоменных фреймов iframe, но чтение документации показывает, что для него по-прежнему требуется серверный доступ к встроенному домену.
StockB
1
Ни одна из этих «альтернатив» не является реальным решением этой проблемы. Запрашивающий хочет установить высоту iFrame для междоменных iFrames с веб-сайтов, которые они не контролируют. 1. Требуется доступ к серверу. 2. Требуется программное обеспечение, которое я считаю плохим. Easy XDM был создан примерно 10 лет назад. Последняя версия от 2019 года требует флеш-памяти . К счастью, Flash мертв, и никто не должен на него полагаться. 3. Требуется доступ к серверу.
redanimalwar
26

У меня есть решение для динамической установки высоты iframe в зависимости от его содержимого. Это работает для междоменного контента. Для этого необходимо выполнить несколько шагов.

  1. Предположим, вы добавили iframe на веб-страницу abc.com/page

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

  2. Затем вам нужно привязать событие окна "message" к веб-странице "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);        
    }
});

При загрузке iframe вы должны отправить сообщение содержимому окна iframe с сообщением "FrameHeight":

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. На главной странице, которая добавлена ​​в iframe здесь "xyz.pqr / contactpage", вы должны привязать событие окна "message", когда все сообщения будут получать из родительского окна "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 }, "*");       
    }
});
Судхир
источник
3
Работает плавно. Большое спасибо!
Alex Леонов
просто это самый технический метод, который я мог найти, отличная работа @sudhir, спасибо :)
java acm
14

Я сравнивал scrollWidth iframe до тех пор, пока он не изменил размер, в то время как я постепенно устанавливаю высоту IFrame. И у меня это сработало. Вы можете отрегулировать шаг по своему желанию.

   <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>
Трой Митчел
источник
4
вы, вероятно, захотите ограничить количество циклов, иначе «while» может превратиться в бесконечный цикл.
Кевин Зайферт
1
Это приводит к сбою моей страницы.
DDDD
Хорошее простое решение. Но в моем случае приращение размера ограничено размером экрана.
Зета
1

У меня есть сценарий, который помещается в iframe с его содержимым. Он также гарантирует, что iFrameResizer существует (внедряет его как скрипт), а затем изменяет размер.

Ниже я приведу упрощенный пример.

// /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/iframe-resizer@3.5.14/js/iframeResizer.contentWindow.min.js
        resizerScriptTag.src = 'https://unpkg.com/iframe-resizer@3.5.14/js/iframeResizer.min.js';
    } else {
        // Cool, the iFrameResizer exists so we can just load our iframe.
        loadIFrame();
    }    
}())

Затем содержимое iframe можно внедрить в любое место на другой странице / сайте с помощью сценария следующим образом:

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

Содержимое iframe будет вставлено ниже, где бы вы ни разместили тег скрипта.

Надеюсь, это кому-то поможет. 👍

ctrlplusb
источник
Хорошо, я добавил <script ... data-src="http://google.com">и заполнил им iframe src.
BananaAcid
На момент написания текущая версияiframe-resizer@4.2.10
BananaAcid
1
... расширен до полного используемого примера codeandbox.io/s/remote-embed-ifrm-qdb74
BananaAcid
@BananaAcid - ссылка на ваш код и ящик больше не работает
ctrlplusb
спасибо за упоминание, ссылки стали: codeandbox.io/s/remote-embed-ifrm-yz0xl . (примечание: codeandbox "фейки", использующие несколько страниц, могут зависать. Перезагрузка может помочь)
BananaAcid
1

Вот мое простое решение на этой странице. http://lab.ohshiftlabs.com/iframesize/

Вот как это работает;

введите описание изображения здесь

В основном, если вы можете редактировать страницу в другом домене, вы можете разместить другую страницу iframe, принадлежащую вашему серверу, которая сохраняет высоту для файлов cookie. С интервалом чтения файлов cookie при его обновлении обновите высоту iframe. Вот и все.

Скачать; http://lab.ohshiftlabs.com/iframesize/iframesizepost.zip

Изменить: 2019 декабрь

В приведенном выше решении в основном используется другой iframe внутри iframe. 3-й iframe принадлежит домену верхней страницы, который вы называете этой страницей со строкой запроса, которая сохраняет значение размера в cookie, внешняя страница проверяет этот запрос с некоторым интервалом. Но это не лучшее решение, поэтому вам следует следовать этому:

На верхней странице:

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

Здесь вы можете проверить, m.originоткуда он.

На странице фрейма:

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

Хотя, пожалуйста, не забывайте, что это не очень безопасный способ. Чтобы сделать его безопасным, обновите значение * (targetOrigin) с желаемым значением. Следуйте документации: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

Siniradam
источник
1
Эти ссылки теперь мертвы (404), и их содержание не суммировано в ответе. :(
StockB 05
1
ссылки еще мертвы. 404
Арслан Амер
0

Я нашел еще одно серверное решение для веб-разработчиков, использующее PHP для получения размера iframe.

Сначала используется серверный скрипт PHP для внешнего вызова через внутреннюю функцию: (например, file_get_contentswith, но curl и 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>

Вам нужно отобразить под div ( iframe_reserve) HTML-код, сгенерированный вызовом функции, с помощью простогоecho curl_get_file_contents("location url iframe","activation proxy")

После этого функция события тела при загрузке с помощью javascript принимает высоту iframe страницы просто с помощью простого управления содержимым div ( iframe_reserve)

Итак, я использовал divHeight = document.getElementById("iframe_reserve").clientHeight;высоту внешней страницы, которую мы собираемся вызвать после замаскирования контейнера div ( iframe_reserve). После этого загружаем iframe с его хорошей высотой, вот и все.

голова
источник
0

Я столкнулся с этой проблемой, когда работал над чем-то на работе (используя React). По сути, у нас есть внешний HTML-контент, который мы сохраняем в нашу таблицу документов в базе данных, а затем вставляем на страницу при определенных обстоятельствах, когда вы находитесь в наборе данных Documents.

Итак, учитывая nвстроенные строки, из которых до nможет содержать внешний html, нам нужно было разработать систему для автоматического изменения размера iframe каждой встроенной строки после полной загрузки содержимого в каждом. Покрутив колеса немного, я сделал это вот как:

  1. Установите messageпрослушиватель событий в индексе нашего приложения React, который проверяет конкретный ключ, который мы установим из iframe отправителя.
  2. В компоненте, который фактически отображает iframe, после вставки в него внешнего html я добавляю <script>тег, который будет ждать window.onloadсрабатывания iframe . Как только это сработает, мы postMessageотправляем сообщение в родительское окно с информацией об идентификаторе iframe, вычисленной высоте и т. Д.
  3. Если источник совпадает и ключ удовлетворяется в прослушивателе индекса, возьмите DOM idiframe, который мы передаем в MessageEventобъект
  4. Как только у нас есть iframe, просто установите высоту из значения, которое передается из 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"
                )
            }
        }
    }}
/>
Джон Дункан
источник