jQuery. вже в динамічно вставленій рамці


184

Ми використовуємо товщину jQuery для динамічного відображення рамки кадрів, коли хтось клацає на картинку. У цьому iframe ми використовуємо галерею JavaScript-бібліотеку для відображення кількох зображень.

Здається, проблема полягає в тому, що $(document).readyв iframe, здається, запускається занадто рано, а вміст iframe ще не завантажений, тому код галереї не застосовується належним чином до елементів DOM. $(document).readyздається, використовується стан готового для батьків iframe, щоб вирішити, чи готовий iframe.

Якщо ми витягнемо функцію, викликану документом, готовим в окрему функцію, і викличемо її через тайм-аут 100 мс. Це працює, але ми не можемо ризикувати у виробництві за допомогою повільного комп'ютера.

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

Моє запитання: до якої події jQuery ми повинні пов'язувати, щоб мати можливість виконувати наш код, коли динамічна рамка iframe готова, а не лише це батько?


1
І ви підтверджуєте, що galleria працює, коли ви завантажуєте її безпосередньо замість iframe, правильно?
Джейсон Кілі

Так, галерея прекрасно працює, коли ми використовуємо її безпосередньо на звичайній сторінці.
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
Ні, це не має значення. Подія завантаження не запуститься до наступного циклу події як мінімум.
Barum Rho

29
подія завантаження не буде працювати для iframes, які використовуються для завантаження. як <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. Весь код знаходиться і виконується з батьківського кадру / вікна.


Яким є fucntion movePreview, згаданий у setTimeout ()?
cam8001

@ cam8001: Це був помилковий помилок - тепер виправлено.
Már Örlygsson

Я також застосував рішення для опитування. Інші рішення, здавалося, працюють лише з частковим успіхом. Однак для мене мені потрібно було перевірити наявність функції javascript, а не лише вміст iframe, перш ніж я мав успіх. напр. (typeof iframe.contentWindow.myfunc == 'function')
Julian

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 (функція ...) у батьків не працюватиме для мене. Здавалося, функція зворотного виклику виконується ще до того, як dom iframe буде готовий. Використання цього методу спрацювало чудово!
w--

4

Знайшов рішення проблеми.

Коли ви натискаєте посилання на товсту скриньку, що відкриває рамку iframe, вона вставляє iframe з ідентифікатором TB_iframeContent.

Замість того, щоб покладатися на $(document).readyподію в коді iframe, я просто повинен прив’язати до події завантаження iframe у батьківському документі:

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

Цей код знаходиться у iframe, але прив'язується до події контролю в батьківському документі. Він працює у FireFox та IE.


9
Знайшли рішення? Схоже, Пір вже опублікував це. Незалежно від того, знайшли ви це самостійно чи ні, етикет повинен був би прийняти його відповідь, таким чином винагороджуючи час, який він витратив, відповідаючи на вас.
Sky Sanders

Різниця між рішенням Pier і цим (і тим, що мені не вистачало в коді) - це контекст, top.documentякий дозволяє мені мати код всередині iframe, щоб можна було визначити, коли завантажується iframe. (Хоча loadз цієї відповіді було застаріло, і його слід замінити on("load").)
Teepeemm

2

В основному те, що вже розмістили інші, але IMHO трохи чистіше:

$('<iframe/>', {
    src: 'https://example.com/',
    load: function() {
        alert("loaded")
    }
}).appendTo('body');

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 вище, я чекаю на завантаження, щоб додати iframe до дому, а потім перевіряю готовність внутрішнього документа iframe - який повинен бути міждоменним, оскільки він вказує на wikipedia - але Chrome, здається, повідомляє "завершеним". Потім метод виклику плагіну викликається, коли рамка дійсно готова. Зворотний виклик намагається перевірити стан готового внутрішнього документа ще раз - цього разу повідомляючи про запит між доменом (що правильно) - все одно, здається, він працює на те, що мені потрібно, і сподіваюся, що він допомагає іншим.

<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проблему, оскільки явно не вдається для кадрів iframe. Ось рішення не підтримувати це.

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

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.