iFrame src更改事件检测?


180

假设我无法控制iframe中的内容,是否有任何方法可以通过父页面检测到其中的src更改?可能是某种负载?

我的最后一招是如果iframe src与以前的相同,则进行1秒间隔测试,但是这样做会导致问题。

如果有帮助,我正在使用jQuery库。


3
请问src在iframe中点击一个链接时,性能变化?我不确定-如果我不得不猜测,是否会说“不”。有一些方法可以监视属性(在Firefox中至少为AFAIK),但是我不确定在这种情况下它是否会有用。
Pekka 2010年

我正在尝试实现这样的功能,并且可以确认srcDOM中的属性在FireFox或Safari的最新版本中没有更改。@Michiel在下面的回答与我到目前为止所取得的一样好。
Kaelin Colclasure 2014年

Answers:


200

您可能要使用该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
Nikolai

@Daniel Vassallo假设您希望在处理安全问题时采取这种做法,有人不能改写onLoad事件然后更改iframe内容吗?
Noam Shaish 2012年

15
我什么时候this.contentWindow.location.href得到Permission denied to access property 'href'
Mazatec

4
this.contentWindow.location.href将无法正常工作,因为您正在执行跨源请求。:(所有的浏览器现在限制。
纳文

2
更正:this.contentWindow.location适用于其他域。甚至this.contentWindow.location.href可用于其他域,但仅可用于编写。但是,阅读this.contentWindow.location.href仅限于同一领域。
wadim

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


2
不能完全按计划工作。它在初始页面加载时触发警报。根据您对此代码的计划(对我而言,我正在为表单提交页面上的对话框设置动画),此解决方案将不起作用。
stacigh 2015年

@stracigh,是正确的,该事件将在每次加载框架时触发,因此也会在首次加载初始页面时触发。如果您不想第一次触发代码,则必须以某种方式检测到这是您的第一帧加载并返回。
米歇尔

@FooBar,是的,它确实可以跨域工作,实际上这就是我使用它的目的。但是,如果该页面位于另一个域上,则无法使用javascript访问iFrame中的页面。
米歇尔2015年

这有效并检测何时加载内容,我需要一种方法来检测src何时更改。类似于beforeLoad(激活加载程序)
Andrea_86 '18

如@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上

由于原始问题已作为此问题的重复部分被关闭,因此在此处发布了答案。


如果我正在使用Web组件并且我的一个自定义组件具有src属性怎么办?我认为这会不小心捡到它。
路加福音

然后,您更改上的选项observe。这只是一个例子。
Xotic750 '16

@PaulRoub删除了jsfiddle链接(必须是复制/粘贴错误),并将代码作为摘要包含在内。
Xotic750 '16

4
但是在我的情况下,frame.contentDocument.URL更改(来自框架内部的链接)而不是src。我可以看吗?
httpete

不确定,最好写一个我认为的问题。
Xotic750 '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属性更改以及从iframe本身进行的所有URL更改。

在所有现代浏览器中测试。

我也用此代码做了要点。您也可以查看我的其他答案。它将对它的工作原理进行一些深入的介绍。


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

2

由于Jquery 3.0版,您可能会收到错误消息

TypeError:url.indexOf不是函数

通过执行可以轻松修复

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

1

这是在Commerce SagePayCommerce Paypoint Drupal模块中使用的方法,该方法主要document.location.href通过首先加载自己的iframe,然后是外部iframe与旧值进行比较。

因此,基本的想法是将空白页作为占位符加载,并带有其自己的JS代码和隐藏形式。然后父级JS代码将提交该隐藏表单,该表单#action指向外部iframe。重定向/提交完成后,仍在该页面上运行的JS代码可以跟踪您的document.location.href值更改。

这是iframe中使用的示例JS:

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

然后,在临时空白登录页面中,您需要包括将重定向到外部页面的表格。


这可能是一个hack,但是开销比变异观察器要少得多。在单页面应用程序中使用时,请注意不要创建任何引用,以防止垃圾回收此hack的剩余内容。
杰夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.