jQuery. уже в динамически вставляемом iframe - PullRequest
178 голосов
/ 15 октября 2008

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

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

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

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

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

Ответы [ 9 ]

287 голосов
/ 15 октября 2008

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

28 голосов
/ 09 апреля 2009

Используя 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. Весь код находится и выполняется из родительского фрейма / окна.

14 голосов
/ 15 октября 2008

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

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

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

9 голосов
/ 30 апреля 2013

Следуя идее доктора Джокепу и Дэвида Мердока, я реализовал более полную версию. Для требуется 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);
    });
});
4 голосов
/ 15 октября 2008

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

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

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

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

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

1 голос
/ 01 августа 2017

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

$('<iframe/>', {
    src: 'https://example.com/',
    load: function() {
        alert("loaded")
    }
}).appendTo('body');
1 голос
/ 16 августа 2012

Я загружаю 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 голос
/ 12 октября 2009

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

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

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

0 голосов
/ 10 февраля 2016

Это была та самая проблема, с которой я столкнулся с нашим клиентом. Я создал небольшой плагин 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, похоже, сообщает "complete". Метод 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>
...