Détection d'événement de changement iFrame src?

181

En supposant que je n'ai aucun contrôle sur le contenu de l'iframe, y a-t-il un moyen de détecter un changement de src via la page parent? Une sorte de chargement peut-être?

Mon dernier recours est de faire un test d'intervalle d'une seconde si l'iframe src est le même qu'avant, mais faire cette solution de piratage serait nul.

J'utilise la bibliothèque jQuery si cela aide.

Matrym
la source
3
La srcpropriété changera-t-elle lorsqu'un lien dans l'iframe est cliqué? Je ne suis pas sûr de cela - si je devais deviner, je dirais "non". Il existe des moyens de surveiller les propriétés (dans Firefox au moins AFAIK) mais je ne suis pas sûr que cela soit utile dans ce cas.
Pekka
J'essaie de mettre en œuvre quelque chose comme ça et je peux confirmer que l' srcattribut dans le DOM ne change pas dans les dernières versions de FireFox ou de Safari. La réponse de @ Michiel ci-dessous est aussi bonne que j'ai pu aller jusqu'à présent.
Kaelin Colclasure le

Réponses:

201

Vous souhaiterez peut-être utiliser l' onLoadévénement, comme dans l'exemple suivant:

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

L'alerte apparaîtra chaque fois que l'emplacement dans l'iframe change. Il fonctionne dans tous les navigateurs modernes, mais peut ne pas fonctionner dans certains navigateurs très anciens comme IE5 et les premiers Opera. ( Source )

Si l'iframe affiche une page dans le même domaine du parent , vous pourrez accéder à l'emplacement avec contentWindow.location, comme dans l'exemple suivant:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Daniel Vassallo
la source
13
Vous voulez dire this.contentWindow.location.href
Nikolai
@Daniel Vassallo en supposant que vous preniez cette approche lorsque vous souhaitez gérer un problème de sécurité, quelqu'un ne peut-il pas simplement ignorer l'événement onLoad puis modifier le contenu de l'iframe?
Noam Shaish
15
Quand this.contentWindow.location.hrefj'obtiensPermission denied to access property 'href'
Mazatec
4
this.contentWindow.location.href ne fonctionnera pas si vous faites une demande d'origine croisée. :( Tous les navigateurs restreignent cela maintenant.
Naveen
2
Correction: this.contentWindow.locationest disponible pour d'autres domaines. Even this.contentWindow.location.hrefest disponible pour d'autres domaines, mais uniquement pour l'écriture. Cependant, la lecture this.contentWindow.location.hrefest limitée au même domaine.
wadim
57

Réponse basée sur JQuery <3

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

Comme mentionné par subharb, à partir de JQuery 3.0, cela doit être changé en:

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

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

Michiel
la source
2
Ne fonctionne pas exactement comme prévu. Il déclenche l'alerte lors du chargement initial de la page. En fonction de vos plans pour ce code (pour moi, j'anime la boîte de dialogue hors de la page lors de l'envoi du formulaire), cette solution ne fonctionnera pas.
stacigh
@stracigh, c'est correct, l'événement se déclenchera à chaque fois que le cadre se chargera, donc aussi la première fois lors du chargement initial de la page. Si vous ne voulez pas que votre code soit déclenché la première fois, vous devez en quelque sorte détecter qu'il s'agit de votre premier chargement et retour de trame.
Michiel
@FooBar, Oui, cela fonctionne dans plusieurs domaines, en fait c'est pour cela que je l'ai utilisé. Mais vous ne pouvez pas accéder à la page dans l'iFrame avec javascript lorsque cette page est sur un autre domaine.
Michiel
cela fonctionne et détecte lorsque le contenu est chargé, j'ai besoin d'un moyen de détecter le changement de src. Quelque chose comme beforeLoad (pour activer un chargeur)
Andrea_86
Comme indiqué par @stacigh, l'événement déclenchera une fois lors du chargement de la page parent. Si vous déclarez un compteur en dehors de cette fonction et que vous le lisez à l'intérieur, vous pouvez alors détecter les modifications par la suite. Vous incrémenteriez le compteur dans le gestionnaire onload plus have if (counter > 1) { // do something on iframe actual change }(en supposant que le compteur était défini sur 0 au début)
Alex
38

Si vous n'avez aucun contrôle sur la page et que vous souhaitez surveiller une sorte de changement, la méthode moderne consiste à utiliser MutationObserver

Un exemple de son utilisation, en regardant l' srcattribut à changer d'uniframe

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>

Sortie après 3 secondes

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/ 

Sur jsFiddle

La réponse publiée ici en tant que question d'origine a été fermée comme un double de celle-ci.

Xotic750
la source
Que faire si j'utilise des composants Web et que l'un de mes composants personnalisés possède un attribut src? Je pense que cela le ramasserait accidentellement.
Luke
Ensuite, vous modifiez les options sur observe. C'est juste un exemple.
Xotic750
@PaulRoub a supprimé le lien jsfiddle (il doit s'agir d'une erreur de copier / coller) et a inclus le code sous forme d'extrait.
Xotic750
4
Mais dans mon cas, le frame.contentDocument.URL change (à partir des liens à l'intérieur du cadre) pas le src. Puis-je regarder ça?
httpete
Pas sûr, mieux vaut écrire une question je pense.
Xotic750 du
11

Remarque: l'extrait de code ne fonctionnera que si l'iframe a la même origine.

D'autres réponses ont proposé l' loadévénement, mais il se déclenche après le chargement de la nouvelle page dans l'iframe. Vous devrez peut-être être averti immédiatement après la modification de l'URL , et non après le chargement de la nouvelle page.

Voici une solution JavaScript simple:

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>

Cela permettra de suivre avec succès les srcmodifications d'attribut, ainsi que toutes les modifications d'URL effectuées à partir de l'iframe lui-même.

Testé dans tous les navigateurs modernes.

J'ai également fait une idée de ce code. Vous pouvez également vérifier mon autre réponse . Cela explique un peu comment cela fonctionne.

dodov
la source
6

L'iframe conserve toujours la page parent, vous devez l'utiliser pour détecter dans quelle page vous vous trouvez dans l'iframe:

Code 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
la source
2

Depuis la version 3.0 de Jquery, vous pouvez obtenir une erreur

TypeError: url.indexOf n'est pas une fonction

Ce qui peut être facilement corrigé en faisant

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

Voici la méthode qui est utilisée dans les modules Commerce SagePay et Commerce Paypoint Drupal qui se compare essentiellement document.location.hrefà l'ancienne valeur en chargeant d'abord sa propre iframe, puis une externe.

Donc, fondamentalement, l'idée est de charger la page vierge comme un espace réservé avec son propre code JS et un formulaire caché. Ensuite, le code JS parent soumettra ce formulaire caché où il #actionpointe vers l'iframe externe. Une fois la redirection / soumission effectuée, le code JS qui est toujours en cours d'exécution sur cette page peut suivre vos document.location.hrefchangements de valeur.

Voici un exemple de JS utilisé dans iframe:

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

Et voici JS utilisé dans la page parent:

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

Ensuite, dans la page de destination vierge temporaire, vous devez inclure le formulaire qui redirigera vers la page externe.

Kenorb
la source
Cela peut être un hack mais la surcharge est considérablement inférieure à celle de l'observateur de mutation. Lorsqu'il est utilisé dans une application à page unique, veillez à ne pas créer de références qui empêcheront les restes de ce piratage d'être ramassés.
Jeff