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

Во-первых, я вижу, что этот вопрос задавался несколько раз, но ответы не кажутся удовлетворительными. Я ищу, чтобы иметь возможность вызвать script в любое время и определить, загружен ли iframe, и не ограничивать script требованием быть добавленным к самому тегу iframe в свойстве onload.

Вот некоторые предпосылки: я работал над ненавязчивым script, чтобы попытаться определить, загружены ли локальные фреймы в dom, это потому, что один из наших клиентов включает в себя формы на своем веб-сайте в iframe, и многие из них открываются в лайтбоксах, которые динамически добавляют iframes в dom в любое время. Я могу прикрепить к открытому событию лайтбокса, но его ударить или пропустить, могу ли я "поймать" iframe перед его загрузкой.

Позвольте мне объяснить немного больше.

В моем тестировании я определил, что событие onload будет срабатывать только один раз - и только если он привязан до фактического загрузки iframe. Например: на этой странице должно быть указано только "добавлено в тег iframe", и слушатель, который прикреплен после этого, не срабатывает - для меня это имеет смысл. (Я использую свойство iframe onload для простого примера).

https://jsfiddle.net/g1bkd3u1/2/

<script>
    function loaded () {
        alert ("added to iframe tag");
        $("#test").load(function(){
            alert("added after load finished");
        });
    };
</script>
<iframe onload="loaded()" id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>

Мой следующий подход состоял в проверке состояния готовности документа iframe, который работает почти во всех моих тестах, кроме chrome, который сообщает "complete" - я ожидал "Access Denied" для запроса перекрестного домена. Я в порядке с ошибкой в ​​перекрестном домене, потому что я могу игнорировать iframe, так как меня интересуют только локальные фреймы. Отчеты firefox "unintialized", с которыми я в порядке, потому что я знаю, что могу прикрепить событие onload.

Откройте Chrome: https://jsfiddle.net/g1bkd3u1/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    alert($("#test").contents()[0].readyState);
</script>

Я обнаружил, что, если я жду только 100 мс, тогда iframe, кажется, сообщает, как и ожидалось (исключение перекрестной безопасности - это то, что я хочу - но я не хочу ждать произвольной длины).

https://jsfiddle.net/g1bkd3u1/4/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    setTimeout(function () { 
        try {
            alert($("#test").contents()[0].readyState);
        } catch (ignore) {
            alert("cross domain request");
        }
    }, 100);
</script>

Мое текущее решение/решение состоит в том, чтобы добавить обработчик события onload, затем отделить iframe от dom, а затем вставить его обратно в dom в том же месте - теперь вызывается событие onload. Вот пример, ожидающий 3 секунды (надеясь, что у него достаточно времени для загрузки iframe), чтобы показать, что отсоединение и повторное присоединение приводят к срабатыванию события onload iframe.

https://jsfiddle.net/g1bkd3u1/5/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    setTimeout(function(){
        var placeholder = $("<span>");
        $("#test").load(function(){
            alert("I know the frame has loaded now");
        }).after(placeholder).detach().insertAfter(placeholder);
        placeholder.detach();
    }, 3000);
</script>

Пока это работает, мне остается задаться вопросом, есть ли более элегантные методы проверки загрузки iframe (ненавязчиво)?

Спасибо за ваше время.

Ответ 1

Сегодня я на самом деле столкнулся с ошибкой, когда мое удаление и повторная установка iframes ломает редактор wysiwyg на веб-сайте. Поэтому я создал начало небольшого плагина jQuery для проверки готовности iframe. Он не готов к производству, и я его не тестировал, но он должен обеспечить лучшую альтернативу снятию и повторной установке iframe - он использует опрос, если это необходимо, но следует удалить setInterval, когда iframe готов.

Его можно использовать как:

$("iframe").iready(function() { ... });

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

<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>

Пример ждет, пока окно не загрузится, чтобы динамически добавить iframe в DOM, затем предупреждает о его документе readyState - который в хромовых дисплеях "завершен" неверно. Ирилая функция должна быть вызвана, и попытка вывода документа readyState доказывает перекрестное доменное исключение - снова это не было тщательно протестировано, но работает для того, что мне нужно.

Ответ 2

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

IF, вы знаете или можете управлять содержимым загруженного документа в iFrame, тогда вы можете просто проверить/добавить элемент, который вы могли бы проверить наличие, чтобы затем обновить iframe документ. По крайней мере, вы знаете, что элементы, с которыми вы хотите работать, загружаются в документ.

В моем случае я вызвал функцию, которая сама проверила наличие моего известного элемента, который всегда будет найден после того, как элементы, которые мне нужно было обновить, уже были загружены - в случае, если он не был найден, он назвал себя снова через setTimeout().

function updateIframeContents() {
    if ($("#theIframe").contents().find('.SaveButton').length > 0) {
        // iframe DOM Manipulation
    } else {
        setTimeout(updateIframeContents, 250);
    }
}

updateIframeContents();