iFrame src обнаружение события изменения?

181

Предполагая, что у меня нет контроля над содержимым в iframe, есть ли способ обнаружить изменение src в нем через родительскую страницу? Может, какая-то нагрузка?

Мое последнее средство - сделать 1-секундный интервальный тест, если iframe src такой же, как и раньше, но выполнение этого хакерского решения будет неудачным.

Я использую библиотеку jQuery, если это поможет.

Matrym
источник
3
Будет ли srcизменяться свойство при нажатии на ссылку в iframe? Я не уверен в этом - если бы мне пришлось угадывать, если бы сказал «нет». Там являются способы свойств монитора (в Firefox по крайней мере , AFAIK) , но я не уверен , будет ли какое - либо использование в данном случае.
Пекка
Я пытаюсь реализовать что-то вроде этого и могу подтвердить, что srcатрибут в DOM не изменяется в последних версиях FireFox или Safari. @ Ответ Мичиэля ниже настолько хорош, насколько я смог получить до сих пор.
Кольцо Келин

Ответы:

201

Вы можете использовать onLoadсобытие, как в следующем примере:

<iframe src="http://www.google.com/" onLoad="alert('Test');"></iframe>

Оповещение будет всплывать всякий раз, когда изменяется местоположение в iframe. Он работает во всех современных браузерах, но может не работать в некоторых очень старых браузерах, таких как IE5 и ранняя версия Opera. ( Источник )

Если iframe показывает страницу в том же домене родительского объекта , вы сможете получить доступ к местоположению с помощью contentWindow.location, как в следующем примере:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Даниэль Вассалло
источник
13
Вы имеете в виду this.contentWindow.location.href
Николай
@ Даниэль Вассалло, если вы примете этот подход, когда захотите решить проблему безопасности, не может ли кто-нибудь просто переопределить событие onLoad и затем изменить содержимое iframe?
Ноам Шайш
15
Когда я this.contentWindow.location.hrefполучуPermission denied to access property 'href'
Mazatec
4
this.contentWindow.location.href не сработает, потому что вы делаете перекрестный запрос источника. :( Все браузеры ограничивают это сейчас.
Naveen
2
Исправление: this.contentWindow.locationдоступно для других доменов. Даже this.contentWindow.location.hrefдоступно для других доменов, но только для записи. Однако чтение this.contentWindow.location.hrefограничено тем же доменом.
Вадим
57

Ответ основан на JQuery <3

$('#iframeid').load(function(){
    alert('frame has (re)loaded');
});

Как упоминалось в subharb, начиная с JQuery 3.0 это необходимо изменить на:

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});

https://jquery.com/upgrade-guide/3.0/#breaking-change-load-unload-and-error-removed

Михель
источник
3
Не работает точно так, как планировалось. Он запускает предупреждение при начальной загрузке страницы. В зависимости от ваших планов для этого кода (для меня, я оживляю диалог со страницы формы отправки), это решение не будет работать.
Stacigh
@stracigh, это правильно, событие будет срабатывать при каждой загрузке кадра, а также при первой загрузке страницы. Если вы не хотите, чтобы ваш код срабатывал в первый раз, вы должны каким-то образом обнаружить, что это ваш первый кадр загрузки и возврата.
Михель
@FooBar, да, это работает кросс-домен, на самом деле это то, для чего я его использовал. Но вы не можете получить доступ к странице в iFrame с помощью JavaScript, когда эта страница находится в другом домене.
Михель
это работает и определяет, когда загружается контент, мне нужен способ определить, когда src меняется. Что-то вроде beforeLoad (для активации загрузчика)
Andrea_86
Как указано @stacigh, оно будет запускать событие один раз при загрузке родительской страницы. Если вы объявляете счетчик вне этой функции и читаете его внутри, вы можете обнаружить изменения после этого. Вы бы увеличивать счетчик внутри OnLoad обработчика плюс есть if (counter > 1) { // do something on iframe actual change }(предполагается , что счетчик был установлен на 0 при старте)
Alex
38

Если у вас нет контроля над страницей и вы хотите наблюдать за изменениями, то современный метод заключается в использовании MutationObserver

Пример его использования, отслеживание изменения srcатрибутаiframe

new MutationObserver(function(mutations) {
  mutations.some(function(mutation) {
    if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
      console.log(mutation);
      console.log('Old src: ', mutation.oldValue);
      console.log('New src: ', mutation.target.src);
      return true;
    }

    return false;
  });
}).observe(document.body, {
  attributes: true,
  attributeFilter: ['src'],
  attributeOldValue: true,
  characterData: false,
  characterDataOldValue: false,
  childList: false,
  subtree: true
});

setTimeout(function() {
  document.getElementsByTagName('iframe')[0].src = 'http://jsfiddle.net/';
}, 3000);
<iframe src="http://www.google.com"></iframe>

Выход через 3 секунды

MutationRecord {oldValue: "http://www.google.com", attributeNamespace: null, attributeName: "src", nextSibling: null, previousSibling: null…}
Old src:  http://www.google.com
New src:  http://jsfiddle.net/ 

На jsFiddle

Отправленный ответ здесь как оригинальный вопрос был закрыт как дубликат этого.

Xotic750
источник
Что если я использую веб-компоненты и один из моих пользовательских компонентов имеет атрибут src? Я думаю, что это случайно подняло бы это.
Лука
Затем вы меняете параметры на observe. Это всего лишь пример.
Xotic750
@PaulRoub удалил ссылку jsfiddle (должна быть ошибка копирования / вставки) и включил код в виде фрагмента кода.
Xotic750
4
Но в моем случае frame.contentDocument.URL изменяется (из ссылок внутри фрейма) не src. Могу ли я посмотреть это?
httpte
Не уверен, лучше написать вопрос, я думаю.
Xotic750 22.09.16
11

Примечание . Фрагмент будет работать только в том случае, если iframe имеет одинаковое происхождение.

Другие ответы предлагали loadсобытие, но оно срабатывает после загрузки новой страницы в iframe. Вам может потребоваться уведомление сразу после изменения URL-адреса , а не после загрузки новой страницы.

Вот простое решение JavaScript:

function iframeURLChange(iframe, callback) {
    var unloadHandler = function () {
        // Timeout needed because the URL changes immediately after
        // the `unload` event is dispatched.
        setTimeout(function () {
            callback(iframe.contentWindow.location.href);
        }, 0);
    };

    function attachUnload() {
        // Remove the unloadHandler in case it was already attached.
        // Otherwise, the change will be dispatched twice.
        iframe.contentWindow.removeEventListener("unload", unloadHandler);
        iframe.contentWindow.addEventListener("unload", unloadHandler);
    }

    iframe.addEventListener("load", attachUnload);
    attachUnload();
}

iframeURLChange(document.getElementById("mainframe"), function (newURL) {
    console.log("URL changed:", newURL);
});
<iframe id="mainframe" src=""></iframe>

Это будет успешно отслеживать srcизменения атрибутов, а также любые изменения URL, сделанные внутри самого iframe.

Проверено во всех современных браузерах.

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

Додов
источник
6

Iframe всегда сохраняет родительскую страницу, вы должны использовать это, чтобы определить, на какой странице вы находитесь в iframe:

HTML-код:

<iframe id="iframe" frameborder="0" scrolling="no" onload="resizeIframe(this)" width="100%" src="www.google.com"></iframe>

Js:

    function resizeIframe(obj) {
        alert(obj.contentWindow.location.pathname);
    }
Tarik FAMIL
источник
2

Начиная с версии 3.0 Jquery вы можете получить ошибку

Ошибка типа: url.indexOf не является функцией

Который можно легко исправить, выполнив

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});
subharb
источник
1

Вот метод, который используется в модулях Commerce SagePay и Commerce Paypoint Drupal, который в основном сравнивается document.location.hrefсо старым значением, сначала загружая его собственный iframe, а затем внешний.

В общем, идея состоит в том, чтобы загрузить пустую страницу как заполнитель со своим собственным JS-кодом и скрытой формой. Затем родительский JS-код отправит эту скрытую форму, где он #actionуказывает на внешний iframe. Как только происходит перенаправление / отправка, код JS, который все еще работает на этой странице, может отслеживать document.location.hrefизменения вашего значения.

Вот пример JS, используемый в iframe:

;(function($) {
  Drupal.behaviors.commercePayPointIFrame = {
    attach: function (context, settings) {
      if (top.location != location) {
        $('html').hide();
        top.location.href = document.location.href;
      }
    }
  }
})(jQuery);

А вот JS используется на родительской странице:

;(function($) {
  /**
   * Automatically submit the hidden form that points to the iframe.
   */
  Drupal.behaviors.commercePayPoint = {
    attach: function (context, settings) {
      $('div.payment-redirect-form form', context).submit();
      $('div.payment-redirect-form #edit-submit', context).hide();
      $('div.payment-redirect-form .checkout-help', context).hide();
    }
  }
})(jQuery);

Затем во временную пустую целевую страницу необходимо включить форму, которая будет перенаправлять на внешнюю страницу.

kenorb
источник
Это может быть взломом, но накладные расходы значительно меньше, чем у наблюдателя мутаций. При использовании в одностраничном приложении будьте внимательны и не создавайте никаких ссылок, которые будут препятствовать сборке мусора остатками этого хака.
Джефф