jQuery. уже в динамически вставляемом iframe

184

Мы используем jQuery Thickbox для динамического отображения iframe, когда кто-то нажимает на картинку. В этом iframe мы используем библиотеку galleria a javascript для отображения нескольких изображений.

Кажется, проблема в том, что $(document).readyiframe запускается слишком рано, а содержимое iframe даже еще не загружено, поэтому код галереи неправильно применяется к элементам DOM. $(document).readyкажется, использует родительское состояние готовности iframe, чтобы решить, готов ли iframe.

Если мы извлечем функцию, вызванную документом, готовым в отдельную функцию, и вызовем ее по истечении времени ожидания 100 мс. Это работает, но мы не можем рисковать в производстве с медленным компьютером.

$(document).ready(function() { setTimeout(ApplyGalleria, 100); });

Мой вопрос: к какому событию jQuery мы должны привязаться, чтобы иметь возможность выполнять наш код, когда готов динамический iframe, а не только его родитель?

EtienneT
источник
1
И вы подтверждаете, что Galleria работает, когда вы загружаете его напрямую, а не через iframe, правильно?
Джейсон Кили
Да, Galleria отлично работает, когда мы используем его прямо на обычной странице.
EtienneT

Ответы:

291

Я ответил на аналогичный вопрос (см. Обратный вызов Javascript после завершения загрузки IFRAME? ). Вы можете получить контроль над событием загрузки iframe с помощью следующего кода:

function callIframe(url, callback) {
    $(document.body).append('<IFRAME id="myId" ...>');
    $('iframe#myId').attr('src', url);

    $('iframe#myId').load(function() {
        callback(this);
    });
}

Имея дело с iframes, я нашел достаточно хорошим, чтобы использовать событие загрузки вместо события готовности документа.

Пир Луиджи
источник
17
Разве вы не должны устанавливать событие загрузки до вызова attr ('src')?
Шей Эрличмен
15
Нет, это не важно. Событие загрузки не будет срабатывать до следующего цикла событий как минимум.
Барум Ро
29
событие загрузки не будет работать для фреймов, которые используются для загрузки. как <iframe src = "my.pdf" />
Майк Старов
5
Проблема с загрузкой заключается в том, что она срабатывает, когда загружены все изображения и подкадры. JQuery как готовое событие было бы более полезным.
Том
30

Используя jQuery 1.3.2, у меня сработало следующее:

$('iframe').ready(function() {
  $('body', $('iframe').contents()).html('Hello World!');
});

ПЕРЕСМОТР :! На самом деле приведенный выше код иногда выглядит так, как будто он работает в Firefox, а не как в Opera.

Вместо этого я реализовал решение опроса для моих целей. Упрощенно это выглядит так:

$(function() {
  function manipIframe() {
    el = $('body', $('iframe').contents());
    if (el.length != 1) {
      setTimeout(manipIframe, 100);
      return;
    }
    el.html('Hello World!');
  }
  manipIframe();
});

Для этого не требуется код на вызываемых страницах iframe. Весь код находится и выполняется из родительского фрейма / окна.

Мар Эрлигссон
источник
Что такое функция movePreview, указанная в setTimeout ()?
cam8001
@ cam8001: это была опечатка - теперь исправлена.
Мар Örlygsson
Я также использовал решение для опроса. Другие решения, казалось, работали только с частичным успехом. Однако для меня мне нужно было проверить наличие функции javascript, а не только содержимое iframe, прежде чем я добился успеха. например. (typeof iframe.contentWindow.myfunc == 'function')
Джулиан
2
Это рекурсивное решение, которое занимает память для каждого вызова. Если iframe никогда не загружается, он будет вызываться все глубже и глубже, пока не закончится память. Я бы порекомендовал setIntervalдля опроса вместо.
eremzeit
15

В IFrames я обычно решаю эту проблему, помещая небольшой скрипт в самый конец блока:

<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
   fireOnReadyEvent();
   parent.IFrameLoaded();
//]]>
</script>
</body>

Эта работа большую часть времени для меня. Иногда самое простое и наивное решение является наиболее подходящим.

Тамас Чинеге
источник
4
+1 Это решение отлично работает для меня! Одним из замечательных дополнений является то, что вы можете достать до, parentчтобы получить копию jQuery и использовать parent.$(document).ready(function(){ parent.IFrameLoaded( ); });для инициализации iframe.
Дэвид Мердок
9

Следуя идее доктора Джокепу и Дэвида Мердока, я реализовал более полную версию. Это требует, чтобы jQuery и для родителя, и для iframe, и для iframe был под вашим контролем.

код iframe:

var iframe = window.frameElement;

if (iframe){
    iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow

    var parent = window.parent;
    $(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
        var parent$ = parent.jQuery;

        parent$(iframe).trigger("iframeloading");

        $(function(){
            parent$(iframe).trigger("iframeready");
        });

        $(window).load(function(){//kind of unnecessary, but here for completion
            parent$(iframe).trigger("iframeloaded");
        });

        $(window).unload(function(e){//not possible to prevent default
            parent$(iframe).trigger("iframeunloaded");
        });

        $(window).on("beforeunload",function(){
            parent$(iframe).trigger("iframebeforeunload");
        });
    });
}

родительский тестовый код:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});
Рикардо Фрейтас
источник
по любой причине использование $ (iframe) .ready (function ...) в родительском объекте не будет работать для меня. Казалось, что функция обратного вызова выполнялась до того, как iframe dom был готов. Использование этого метода отлично работает!
W--
4

Нашел решение проблемы.

Когда вы нажимаете на ссылку в виде «толстого ящика», которая открывает iframe, он вставляет iframe с идентификатором TB_iframeContent.

Вместо того чтобы полагаться на $(document).readyсобытие в коде iframe, мне просто нужно привязать событие загрузки iframe в родительском документе:

$('#TB_iframeContent', top.document).load(ApplyGalleria);

Этот код находится в iframe, но привязывается к событию элемента управления в родительском документе. Работает в FireFox и IE.

EtienneT
источник
9
Нашли решение? Похоже, Пирс уже опубликовал это. Независимо от того, нашли вы это самостоятельно или нет, этикет должен был принять его ответ, вознаградив тем самым время, которое он потратил на ответ.
Sky Sanders
Разница между решением Пьера и этим (и тем, чего мне не хватало в моем коде) заключается в контексте, top.documentкоторый позволяет мне иметь код внутри iframe, чтобы он мог определить, когда iframe загрузился. (Хотя loadпосле этого ответа устарела и должна быть заменена on("load").)
Teepeemm
2

В основном то, что другие уже опубликовали, но ИМХО немного чище:

$('<iframe/>', {
    src: 'https://example.com/',
    load: function() {
        alert("loaded")
    }
}).appendTo('body');
udondan
источник
1

Попробуй это,

<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>

Все, что вам нужно сделать, это создать функцию JavaScript testframe_loaded ().

Дэнни Джи
источник
1
Проблема с загрузкой заключается в том, что она срабатывает, когда загружены все изображения и подкадры. JQuery как готовое событие было бы более полезным.
Том
1

Я загружаю PDF с jQuery ajax в кеш браузера. Затем я создаю встроенный элемент с данными уже в кеше браузера. Я думаю, это будет работать и с iframe.


var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
    url: url,
    cache: true,
    mimeType: 'application/pdf',
    success: function () {
        // display cached data
        $(scroller).append('<embed type="application/pdf" src="' + url + '" />');
        // hide spinner
        $.mobile.hidePageLoadingMsg();
    }
});

Вы также должны правильно установить заголовки http.


HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
Павел Савара
источник
1
Обратите внимание, что он не очень хорошо работает в мобильных браузерах, где кэш может быть меньше, чем размер PDF.
Павел Савара
1

Это была именно та проблема, с которой я столкнулся с нашим клиентом. Я создал небольшой плагин jquery, который, кажется, работает для готовности iframe. Он использует опрос для проверки документа iframe readyState в сочетании с внутренним URL документа в сочетании с источником iframe, чтобы убедиться, что iframe фактически «готов».

Проблема с «onload» заключается в том, что вам нужен доступ к фактическому iframe, добавляемому в DOM, если вы этого не сделаете, вам нужно попытаться отловить загрузку iframe, которая, если она кешируется, может и не быть. Мне нужен был скрипт, который можно было вызывать в любое время и определять, готов ли iframe или нет.

Вот вопрос:

Святой Грааль для определения, загружен ли локальный iframe

и вот jsfiddle, который я в конце концов придумал.

https://jsfiddle.net/q0smjkh5/10/

В приведенном выше jsfiddle я жду, когда onload добавит iframe в dom, а затем проверяет состояние готовности внутреннего документа iframe - который должен быть междоменным, потому что он указывает на википедию - но Chrome, похоже, сообщает о «завершении». Затем метод iready плагина вызывается, когда iframe фактически готов. Обратный вызов пытается снова проверить состояние готовности внутреннего документа - на этот раз, сообщая о междоменном запросе (что правильно), - в любом случае кажется, что он работает для того, что мне нужно, и надеюсь, что это поможет другим.

<script>
  (function($, document, undefined) {
    $.fn["iready"] = function(callback) {
      var ifr = this.filter("iframe"),
          arg = arguments,
          src = this,
          clc = null, // collection
          lng = 50,   // length of time to wait between intervals
          ivl = -1,   // interval id
          chk = function(ifr) {
            try {
              var cnt = ifr.contents(),
                  doc = cnt[0],
                  src = ifr.attr("src"),
                  url = doc.URL;
              switch (doc.readyState) {
                case "complete":
                  if (!src || src === "about:blank") {
                    // we don't care about empty iframes
                    ifr.data("ready", "true");
                  } else if (!url || url === "about:blank") {
                    // empty document still needs loaded
                    ifr.data("ready", undefined);
                  } else {
                    // not an empty iframe and not an empty src
                    // should be loaded
                    ifr.data("ready", true);
                  }

                  break;
                case "interactive":
                  ifr.data("ready", "true");
                  break;
                case "loading":
                default:
                  // still loading
                  break;   
              }
            } catch (ignore) {
              // as far as we're concerned the iframe is ready
              // since we won't be able to access it cross domain
              ifr.data("ready", "true");
            }

            return ifr.data("ready") === "true";
          };

      if (ifr.length) {
        ifr.each(function() {
          if (!$(this).data("ready")) {
            // add to collection
            clc = (clc) ? clc.add($(this)) : $(this);
          }
        });
        if (clc) {
          ivl = setInterval(function() {
            var rd = true;
            clc.each(function() {
              if (!$(this).data("ready")) {
                if (!chk($(this))) {
                  rd = false;
                }
              }
            });

            if (rd) {
              clearInterval(ivl);
              clc = null;
              callback.apply(src, arg);
            }
          }, lng);
        } else {
          clc = null;
          callback.apply(src, arg);
        }
      } else {
        clc = null;
        callback.apply(this, arguments);
      }
      return this;
    };
  }(jQuery, document));
</script>
Джон Фрейник
источник
Хорошо
Хасан Тарек
0

Эта функция из этого ответа - лучший способ справиться с этим, поскольку $.readyявное сбой для iframes. Вот решение не поддерживать это.

loadСобытие также не срабатывает , если IFrame уже загружен. Очень расстраивает, что это остается проблемой в 2020 году!

function onIframeReady($i, successFn, errorFn) {
    try {
        const iCon = $i.first()[0].contentWindow,
        bl = "about:blank",
        compl = "complete";
        const callCallback = () => {
            try {
                const $con = $i.contents();
             if($con.length === 0) { // https://git.io/vV8yU
                throw new Error("iframe inaccessible");
             }


   successFn($con);
     } catch(e) { // accessing contents failed
        errorFn();
     }
  };
  const observeOnload = () => {
    $i.on("load.jqueryMark", () => {
        try {
            const src = $i.attr("src").trim(),
            href = iCon.location.href;
            if(href !== bl || src === bl || src === "") {
                $i.off("load.jqueryMark");
                callCallback();
            }
        } catch(e) {
            errorFn();
        }
    });
  };
  if(iCon.document.readyState === compl) {
    const src = $i.attr("src").trim(),
    href = iCon.location.href;
    if(href === bl && src !== bl && src !== "") {
        observeOnload();
    } else {
        callCallback();
    }
  } else {
    observeOnload();
  }
} catch(e) {
    errorFn();
}

}

Crashalot
источник