iFrame src Änderungsereigniserkennung?

180

Angenommen, ich habe keine Kontrolle über den Inhalt im Iframe. Gibt es eine Möglichkeit, eine src-Änderung darin über die übergeordnete Seite zu erkennen? Eine Art Onload vielleicht?

Mein letzter Ausweg ist ein 1-Sekunden-Intervalltest, wenn der iframe src derselbe ist wie zuvor, aber diese hackige Lösung wäre scheiße.

Ich verwende die jQuery-Bibliothek, wenn es hilft.

Matrym
quelle
3
Wird sich die srcEigenschaft ändern, wenn auf einen Link im Iframe geklickt wird? Da bin ich mir nicht sicher - wenn ich raten müsste, würde ich "nein" sagen. Es gibt Möglichkeiten, Eigenschaften zu überwachen (zumindest in Firefox AFAIK), aber ich bin mir nicht sicher, ob dies in diesem Fall von Nutzen sein wird.
Pekka
Ich versuche, so etwas zu implementieren, und kann bestätigen, dass sich das srcAttribut im DOM in den neuesten Versionen von FireFox oder Safari nicht ändert. @ Michiels Antwort unten ist so gut, wie ich es bisher geschafft habe.
Kaelin Colclasure

Antworten:

198

Möglicherweise möchten Sie das onLoadEreignis wie im folgenden Beispiel verwenden:

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

Die Warnung wird angezeigt, wenn sich der Speicherort innerhalb des Iframes ändert. Es funktioniert in allen modernen Browsern, aber möglicherweise nicht in einigen sehr älteren Browsern wie IE5 und Early Opera. ( Quelle )

Wenn der Iframe eine Seite in derselben Domäne des übergeordneten Elements anzeigt , können Sie contentWindow.locationwie im folgenden Beispiel auf den Speicherort zugreifen :

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Daniel Vassallo
quelle
13
Du meinst this.contentWindow.location.href
Nikolai
@ Daniel Vassallo unter der Annahme, dass Sie diesen Ansatz wählen, wenn Sie Sicherheitsprobleme behandeln möchten, kann jemand das onLoad-Ereignis nicht einfach überschreiben und dann den Iframe-Inhalt ändern?
Noam Shaish
15
Wenn ich es this.contentWindow.location.hrefbekomme, Permission denied to access property 'href'
bekomme
4
this.contentWindow.location.href funktioniert nicht, da Sie eine Cross-Origin-Anfrage stellen. :( Alle Browser beschränken das jetzt.
Naveen
2
Korrektur: this.contentWindow.locationist für andere Domänen verfügbar. Auch this.contentWindow.location.hrefist für andere Domains verfügbar, aber nur zum Schreiben. Das Lesen this.contentWindow.location.hrefist jedoch auf dieselbe Domäne beschränkt.
Wadim
57

Antwort basierend auf JQuery <3

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

Wie von subharb erwähnt, muss dies ab JQuery 3.0 geändert werden in:

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

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

Michiel
quelle
2
Funktioniert nicht genau wie geplant. Es löst die Warnung beim ersten Laden der Seite aus. Abhängig von Ihren Plänen für diesen Code (für mich animiere ich den Dialog von der Seite beim Senden des Formulars) funktioniert diese Lösung nicht.
Stacigh
@stracigh, das ist richtig, das Ereignis wird jedes Mal ausgelöst, wenn der Frame geladen wird, also auch beim ersten Laden der ersten Seite. Wenn Sie nicht möchten, dass Ihr Code beim ersten Mal ausgelöst wird, müssen Sie irgendwie erkennen, dass dies Ihr erstes Frame-Load und Return ist.
Michiel
@FooBar, ja, es funktioniert domänenübergreifend, genau dafür habe ich es verwendet. Sie können die Seite im iFrame jedoch nicht mit Javascript aufrufen, wenn sich diese Seite in einer anderen Domain befindet.
Michiel
Dies funktioniert und erkennt, wann der Inhalt geladen wird. Ich brauche eine Möglichkeit, um zu erkennen, wann sich der Quellcode ändert. So etwas wie beforeLoad (um einen Loader zu aktivieren)
Andrea_86
Wie durch @stacigh angegeben, wird das Ereignis beim Laden der übergeordneten Seite einmal ausgelöst. Wenn Sie einen Zähler außerhalb dieser Funktion deklarieren und innerhalb lesen, können Sie die Änderungen danach erkennen. Sie würden den Zähler im Onload-Handler erhöhen und haben if (counter > 1) { // do something on iframe actual change }(vorausgesetzt, der Zähler wurde zu Beginn auf 0 gesetzt)
Alex
39

Wenn Sie keine Kontrolle über die Seite haben und auf Änderungen achten möchten, verwenden Sie die moderne Methode MutationObserver

Ein Beispiel für seine Verwendung, bei dem srcdarauf geachtet wird, dass sich das Attribut eines ändertiframe

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>

Ausgabe nach 3 Sekunden

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/ 

Auf jsFiddle

Die hier gepostete Antwort als ursprüngliche Frage wurde als Duplikat dieser Frage geschlossen.

Xotic750
quelle
Was passiert, wenn ich Webkomponenten verwende und eine meiner benutzerdefinierten Komponenten ein src-Attribut hat? Ich denke, das würde es versehentlich aufgreifen.
Luke
Dann ändern Sie die Optionen auf observe. Dies ist nur ein Beispiel.
Xotic750
@PaulRoub entfernte den jsfiddle-Link (muss ein Fehler beim Kopieren / Einfügen gewesen sein) und fügte den Code als Snippet hinzu.
Xotic750
4
In meinem Fall ändert sich jedoch die Datei frame.contentDocument.URL (über Links innerhalb des Frames) und nicht die src. Kann ich das sehen?
httpete
Ich bin mir nicht sicher, am besten schreibe ich eine Frage, denke ich.
Xotic750
11

Hinweis: Das Snippet funktioniert nur, wenn der Iframe denselben Ursprung hat.

Andere Antworten schlugen das loadEreignis vor, aber es wird ausgelöst, nachdem die neue Seite im Iframe geladen wurde. Möglicherweise müssen Sie sofort benachrichtigt werden, nachdem sich die URL geändert hat , nicht nachdem die neue Seite geladen wurde.

Hier ist eine einfache JavaScript-Lösung:

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>

Dadurch werden die srcAttributänderungen sowie alle URL-Änderungen, die im iframe selbst vorgenommen wurden, erfolgreich verfolgt.

In allen modernen Browsern getestet.

Ich habe auch mit diesem Code einen Kern gemacht . Sie können auch meine andere Antwort überprüfen . Es wird etwas ausführlicher darauf eingegangen, wie dies funktioniert.

Dodov
quelle
6

Der Iframe behält immer die übergeordnete Seite bei. Sie sollten dies verwenden, um festzustellen, auf welcher Seite Sie sich im Iframe befinden:

HTML Quelltext:

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

Seit Version 3.0 von Jquery wird möglicherweise eine Fehlermeldung angezeigt

TypeError: url.indexOf ist keine Funktion

Was leicht zu beheben ist

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

Hier ist die Methode, die in Commerce SagePay- und Commerce Paypoint Drupal-Modulen verwendet wird und die im Grunde genommen document.location.hrefmit dem alten Wert verglichen wird, indem zuerst ein eigener, dann ein externer Iframe geladen wird .

Grundsätzlich besteht die Idee darin, die leere Seite als Platzhalter mit eigenem JS-Code und verstecktem Formular zu laden. Der übergeordnete JS-Code sendet dann das versteckte Formular, in dem es #actionauf den externen Iframe verweist. Sobald die Umleitung / Übermittlung erfolgt ist, kann der JS-Code, der noch auf dieser Seite ausgeführt wird, Ihre document.location.hrefWertänderungen verfolgen .

Hier ist das in iframe verwendete Beispiel-JS:

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

Und hier ist JS, das auf der übergeordneten Seite verwendet wird:

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

Dann müssen Sie in eine temporäre leere Zielseite das Formular einfügen, das zur externen Seite umleitet.

Kenorb
quelle
Dies mag ein Hack sein, aber der Overhead ist erheblich geringer als bei Mutationsbeobachtern. Achten Sie bei der Verwendung in einer Anwendung mit nur einer Seite darauf, keine Referenzen zu erstellen, die verhindern, dass die Reste dieses Hacks durch Müll gesammelt werden.
Jeff