iFrame src detección de eventos de cambio?

181

Suponiendo que no tengo control sobre el contenido en el iframe, ¿hay alguna forma de detectar un cambio de src en él a través de la página principal? ¿Algún tipo de carga tal vez?

Mi último recurso es hacer una prueba de intervalo de 1 segundo si el iframe src es el mismo que era antes, pero hacer esta solución hacky sería una mierda.

Estoy usando la biblioteca jQuery si me ayuda.

Matrym
fuente
3
¿Cambiará la srcpropiedad cuando se haga clic en un enlace en el iframe? No estoy seguro de eso, si tuviera que adivinar, si diría "no". No son formas a las propiedades del monitor (en Firefox por lo menos que yo sepa), pero no estoy seguro de si va a ser de alguna utilidad en este caso.
Pekka
Estoy tratando de implementar algo como esto, y puedo confirmar que el srcatributo en el DOM no cambia en las últimas versiones de FireFox o Safari. La respuesta de @ Michiel a continuación es tan buena como he podido llegar hasta ahora.
Kaelin Colclasure

Respuestas:

201

Es posible que desee utilizar el onLoadevento, como en el siguiente ejemplo:

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

La alerta aparecerá cuando cambie la ubicación dentro del iframe. Funciona en todos los navegadores modernos, pero puede que no funcione en algunos navegadores muy antiguos como IE5 y principios de Opera. ( Fuente )

Si el iframe muestra una página dentro del mismo dominio del padre , podrá acceder a la ubicación con contentWindow.location, como en el siguiente ejemplo:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Daniel Vassallo
fuente
13
Te refieres a esto.contentWindow.location.href
Nikolai
@Daniel Vassallo, suponiendo que tome este enfoque cuando desee manejar el problema de seguridad, ¿alguien puede anular el evento onLoad y luego cambiar el contenido del iframe?
Noam Shaish
15
Cuando lo hago this.contentWindow.location.hrefme salePermission denied to access property 'href'
Mazatec
44
this.contentWindow.location.href no funcionará porque estás haciendo una solicitud de origen cruzado. :( Todos los navegadores restringen eso ahora.
Naveen
2
Corrección: this.contentWindow.locationestá disponible para otros dominios. Even this.contentWindow.location.hrefestá disponible para otros dominios, pero solo para escritura. Sin embargo, la lectura this.contentWindow.location.hrefestá limitada al mismo dominio.
wadim
57

Respuesta basada en JQuery <3

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

Como se menciona en subharb, a partir de JQuery 3.0, esto debe cambiarse a:

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

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

Michiel
fuente
2
No funciona exactamente como se planeó. Se activa la alerta en la carga de la página inicial. Dependiendo de cuáles sean sus planes para este código (para mí, estoy animando el diálogo fuera de la página al enviar el formulario), esta solución no funcionará.
stacigh
@stracigh, eso es correcto, el evento se disparará cada vez que se cargue el marco, así que también la primera vez en la carga de la página inicial. Si no desea que su código se active la primera vez, debe detectar de alguna manera esta es su primera carga de trama y regresar.
Michiel
@FooBar, Sí, funciona en varios dominios, de hecho, para eso lo utilicé. Pero no puede acceder a la página en el iFrame con javascript cuando esa página está en otro dominio.
Michiel
esto funciona y detecta cuando se carga el contenido, necesito una forma de detectar cuando el src está cambiando. Algo como beforeLoad (para activar un cargador)
Andrea_86
Como lo indica @stacigh, activará el evento una vez en la carga de la página principal. Si declara un contador fuera de esa función y lo lee dentro, puede detectar los cambios después de eso. Incrementaría el contador dentro del controlador de carga más tener if (counter > 1) { // do something on iframe actual change }(suponiendo que el contador se haya establecido en 0 al inicio)
Alex
38

Si no tiene control sobre la página y desea ver algún tipo de cambio, entonces el método moderno es usar MutationObserver

Un ejemplo de su uso, observando el srccambio del atributo de 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>

Salida después de 3 segundos

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/ 

En jsFiddle

La respuesta publicada aquí como pregunta original se cerró como un duplicado de esta.

Xotic750
fuente
¿Qué sucede si estoy usando componentes web y uno de mis componentes personalizados tiene un atributo src? Creo que esto lo recogería accidentalmente.
Lucas
Luego cambia las opciones en observe. Esto es solo un ejemplo.
Xotic750
@PaulRoub eliminó el enlace jsfiddle (debe haber sido un error de copiar / pegar) e incluyó el código como un fragmento.
Xotic750
44
Pero en mi caso, el frame.contentDocument.URL cambia (desde los enlaces dentro del marco) no el src. ¿Puedo ver eso?
httpete
No estoy seguro, lo mejor es escribir una pregunta, creo.
Xotic750
11

Nota: El fragmento solo funcionaría si el iframe tiene el mismo origen.

Otras respuestas propusieron el loadevento, pero se dispara después de que se carga la nueva página en el iframe. Es posible que deba recibir una notificación inmediatamente después de que cambie la URL , no después de cargar la nueva página.

Aquí hay una solución simple de 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>

Esto rastreará con éxito los srccambios de atributos, así como cualquier cambio de URL realizado desde el propio iframe.

Probado en todos los navegadores modernos.

También hice una idea general con este código. Puedes consultar mi otra respuesta también. Se profundiza un poco en cómo funciona esto.

dodov
fuente
6

El iframe siempre mantiene la página principal, debe usar esto para detectar en qué página se encuentra en el iframe:

Código 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 FAMILIA
fuente
2

Desde la versión 3.0 de Jquery, puede obtener un error

TypeError: url.indexOf no es una función

Que se puede solucionar fácilmente haciendo

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

Aquí está el método que se usa en Commerce SagePay y en los módulos de Commerce Paypoint Drupal que básicamente se compara document.location.hrefcon el valor anterior cargando primero su propio iframe, luego externo.

Básicamente, la idea es cargar la página en blanco como marcador de posición con su propio código JS y forma oculta. Luego, el código JS principal enviará ese formulario oculto donde #actionapunta al iframe externo. Una vez que se produce la redirección / envío, el código JS que aún se ejecuta en esa página puede rastrear sus document.location.hrefcambios de valor.

Aquí hay un ejemplo de JS utilizado en iframe:

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

Y aquí se usa JS en la página principal:

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

Luego, en la página de destino temporal en blanco, debe incluir el formulario que redirigirá a la página externa.

kenorb
fuente
Esto puede ser un truco, pero la sobrecarga es considerablemente menor que el observador de mutaciones. Cuando se use en una aplicación de una sola página, tenga en cuenta no crear ninguna referencia que evite que se recojan los restos de este truco.
Jeff