Wykrywanie zdarzenia zmiany źródła iFrame?

181

Zakładając, że nie mam kontroli nad zawartością elementu iframe, czy jest jakiś sposób, żebym mógł wykryć w nim zmianę źródła za pośrednictwem strony nadrzędnej? Może jakiś rodzaj obciążenia?

Moją ostatnią deską ratunku jest wykonanie 1-sekundowego testu interwału, jeśli źródło iframe jest takie samo jak wcześniej, ale zrobienie tego hackerskiego rozwiązania byłoby do niczego.

Używam biblioteki jQuery, jeśli to pomaga.

Matrym
źródło
3
Czy srcwłaściwość zmieni się po kliknięciu linku w ramce iframe? Nie jestem tego pewien - gdybym miał zgadywać, czy powiedziałbym „nie”. Tam sposoby Właściwości monitora (w Firefoksie przynajmniej AFAIK), ale nie jestem pewien, czy to będzie od każdego zastosowania w niniejszej sprawie.
Pekka
Próbuję zaimplementować coś takiego i mogę potwierdzić, że srcatrybut w DOM nie zmienia się w najnowszych wersjach FireFox ani Safari. @ Odpowiedź Michiela poniżej jest tak dobra, jak udało mi się dotrzeć do tej pory.
Kaelin Colclasure

Odpowiedzi:

201

Możesz użyć onLoadzdarzenia, jak w poniższym przykładzie:

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

Alert wyskakuje za każdym razem, gdy zmieni się lokalizacja w ramce iframe. Działa we wszystkich nowoczesnych przeglądarkach, ale może nie działać w niektórych bardzo starszych przeglądarkach, takich jak IE5 i wczesna Opera. ( Źródło )

Jeśli element iframe wyświetla stronę w tej samej domenie podmiotu nadrzędnego , można uzyskać dostęp do lokalizacji za pomocą contentWindow.location, jak w poniższym przykładzie:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Daniel Vassallo
źródło
13
Masz na myśli this.contentWindow.location.href
Nikolai
@Daniel Vassallo zakładając, że podejmiesz takie podejście, gdy chcesz rozwiązać problem z bezpieczeństwem, czy ktoś nie może po prostu zastąpić zdarzenia onLoad, a następnie zmienić zawartość iframe?
Noam Shaish
15
Kiedy this.contentWindow.location.hrefdostanęPermission denied to access property 'href'
Mazatec
4
this.contentWindow.location.href nie zadziała, ponieważ wykonujesz żądanie cross origin. :( Wszystkie przeglądarki teraz to ograniczają.
Naveen,
2
Poprawka: this.contentWindow.locationjest dostępna dla innych domen. Nawet this.contentWindow.location.hrefjest dostępny dla innych domen, ale tylko do pisania. Jednak czytanie this.contentWindow.location.hrefjest ograniczone do tej samej dziedziny.
wadim
57

Odpowiedź oparta na JQuery <3

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

Jak wspomniano w subharb, od JQuery 3.0 należy to zmienić na:

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

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

Michiel
źródło
3
Nie działa dokładnie tak, jak planowano. Wywołuje alert przy początkowym ładowaniu strony. W zależności od tego, jakie masz plany dotyczące tego kodu (dla mnie animuję okno dialogowe poza stroną przy wysyłaniu formularza) to rozwiązanie nie zadziała.
Stacigh
@stracigh, to prawda, zdarzenie będzie wywoływane za każdym razem, gdy ładuje się ramka, a więc także przy pierwszym załadowaniu strony. Jeśli nie chcesz, aby Twój kod był uruchamiany za pierwszym razem, musisz w jakiś sposób wykryć, że jest to ładowanie i powrót pierwszej klatki.
Michiel
@FooBar, tak, działa w różnych domenach, w rzeczywistości do tego go używałem. Ale nie możesz uzyskać dostępu do strony w elemencie iframe za pomocą javascript, gdy ta strona znajduje się w innej domenie.
Michiel
to działa i wykryć, kiedy zawartość jest ładowana, potrzebuję sposobu, aby wykryć, kiedy zmienia się src. Coś jak wcześniej Załaduj (aby aktywować program ładujący)
Andrea_86
Jak wskazuje @stacigh, zdarzenie zostanie uruchomione po załadowaniu strony nadrzędnej. Jeśli zadeklarujesz licznik poza tą funkcją i przeczytasz go w środku, możesz wykryć późniejsze zmiany. Możesz zwiększyć licznik w module obsługi ładowania i mieć if (counter > 1) { // do something on iframe actual change }(zakładając, że licznik był ustawiony na 0 na początku)
Alex
38

Jeśli nie masz kontroli nad stroną i chcesz obserwować jakąś zmianę, użyj nowoczesnej metody MutationObserver

Przykład jego użycia, obserwacja zmiany srcatrybutuiframe

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>

Wyjście po 3 sekundach

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/ 

Na jsFiddle

Opublikowana odpowiedź tutaj, ponieważ oryginalne pytanie zostało zamknięte jako duplikat tego.

Xotic750
źródło
Co się stanie, jeśli używam składników sieci Web, a jeden z moich składników niestandardowych ma atrybut src? Myślę, że to przypadkowo podniosłoby to.
Luke
Następnie zmieniasz opcje na observe. To tylko przykład.
Xotic750
@PaulRoub usunął link jsfiddle (musiał to być błąd kopiowania / wklejania) i dołączył kod jako fragment.
Xotic750,
4
Ale w moim przypadku frame.contentDocument.URL zmienia się (z linków wewnątrz ramki), a nie src. Mogę to obejrzeć?
httpete,
Nie jestem pewien, chyba najlepiej napisać pytanie.
Xotic750
11

Uwaga: fragment działałby tylko wtedy, gdy element iframe ma to samo źródło.

Inne odpowiedzi proponowały loadzdarzenie, ale jest ono uruchamiane po załadowaniu nowej strony w elemencie iframe. Może być konieczne powiadomienie natychmiast po zmianie adresu URL , a nie po załadowaniu nowej strony.

Oto proste rozwiązanie 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>

Spowoduje to pomyślne śledzenie srczmian atrybutów, a także wszelkich zmian adresu URL dokonanych z poziomu samego elementu iframe.

Przetestowane we wszystkich nowoczesnych przeglądarkach.

Zrobiłem też sedno tego kodu. Możesz też sprawdzić moją drugą odpowiedź . Trochę dogłębnie wyjaśnia, jak to działa.

dodov
źródło
6

Element iframe zawsze zachowuje stronę nadrzędną, należy użyć tego, aby wykryć, na której stronie się znajdujesz:

Kod 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
źródło
2

Od wersji 3.0 Jquery możesz otrzymać błąd

TypeError: url.indexOf nie jest funkcją

Które można łatwo naprawić, wykonując

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});
subharb
źródło
1

Oto metoda używana w Commerce SagePay i modułach Commerce Paypoint Drupal, która w zasadzie porównuje document.location.hrefze starą wartością, najpierw ładując własną ramkę iframe, a następnie zewnętrzną.

Zasadniczo chodzi więc o załadowanie pustej strony jako symbolu zastępczego z własnym kodem JS i ukrytym formularzem. Następnie nadrzędny kod JS prześle ten ukryty formularz w miejscu #actionwskazującym na zewnętrzny element iframe. Gdy nastąpi przekierowanie / przesłanie, kod JS, który nadal działa na tej stronie, może śledzić document.location.hrefzmiany wartości.

Oto przykład JS użyty w iframe:

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

A oto JS użyty na stronie nadrzędnej:

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

Następnie w tymczasowo pustej stronie docelowej należy zamieścić formularz, który przekieruje na stronę zewnętrzną.

kenorb
źródło
To może być hack, ale narzut jest znacznie mniejszy niż w przypadku obserwatora mutacji. W przypadku użycia w aplikacji jednostronicowej należy pamiętać, aby nie tworzyć żadnych odniesień, które zapobiegną gromadzeniu resztek po tym hackowaniu.
Jeff