iFrame src 更改事件检测?

Posted

技术标签:

【中文标题】iFrame src 更改事件检测?【英文标题】:iFrame src change event detection? 【发布时间】:2011-01-26 14:35:37 【问题描述】:

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

如果 iframe src 与以前相同,我最后的手段是进行 1 秒的间隔测试,但这样做会很糟糕。

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

【问题讨论】:

点击 iframe 中的链接时,src 属性会改变吗?我不确定——如果我不得不猜测,是否会说“不”。 种方法可以监控属性(在 Firefox 中至少是 AFAIK),但我不确定在这种情况下它是否有用。 我正在尝试实现类似的东西,并且可以确认 DOM 中的 src 属性在 FireFox 或 Safari 的最新版本中没有发生变化。 @Michiel 下面的回答与我目前所能得到的一样好。 【参考方案1】:

您可能想要使用onLoad 事件,如下例所示:

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

只要 iframe 中的位置发生变化,就会弹出警报。它适用于所有现代浏览器,但可能不适用于一些非常旧的浏览器,如 IE5 和早期的 Opera。 (Source)

如果 iframe 显示的是与父级相同域中的页面,您将能够使用 contentWindow.location 访问该位置,如下例所示:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>

【讨论】:

你的意思是this.contentWindow.location.href 当我做 this.contentWindow.location.href 我得到 Permission denied to access property 'href' this.contentWindow.location.href 不起作用,因为您正在执行跨源请求。 :( 现在所有浏览器都限制了。 更正:this.contentWindow.location 可用于其他域。甚至this.contentWindow.location.href 也可用于其他域,但仅用于写作。但是阅读this.contentWindow.location.href 仅限于同一个域。 这是未定义不再有效的解决方案。【参考方案2】:

基于JQuery的答案

$('#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

【讨论】:

不能完全按计划工作。它在初始页面加载时触发警报。根据您对此代码的计划(对我而言,我在表单提交页面上为对话框设置动画)此解决方案将不起作用。 @sracigh,没错,每次框架加载时都会触发该事件,因此也是第一次在初始页面加载时触发。如果您不想在第一次触发您的代码,您必须以某种方式检测到这是您的第一个帧加载并返回。 @FooBar,是的,它确实可以跨域工作,事实上这就是我使用它的目的。但是当该页面位于另一个域时,您无法使用 javascript 访问 iFrame 中的页面。 这工作并检测何时加载内容,我需要一种方法来检测 src 何时发生变化。类似 beforeLoad(激活加载器) 如@stacigh 所示,它将在父页面加载时触发一次事件。如果您在该函数之外声明一个计数器并在内部读取它,那么您可以检测到之后的更改。你会在 onload 处理程序中增加计数器加上if (counter &gt; 1) // do something on iframe actual change (假设计数器在开始时设置为 0)【参考方案3】:

如果您无法控制页面并希望观察某种变化,那么现代方法是使用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);
&lt;iframe src="http://www.google.com"&gt;&lt;/iframe&gt;

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

Posted answer here as original question was closed as a duplicate of this one.

【讨论】:

如果我正在使用 Web 组件并且我的自定义组件之一具有 src 属性怎么办?我认为这会不小心捡起来。 然后更改observe 上的选项。这只是一个例子。 @PaulRoub 删除了 jsfiddle 链接(一定是复制/粘贴错误)并将代码包含为 sn-p。 但在我的情况下,frame.contentDocument.URL 更改(来自框架内的链接)而不是 src。我可以看吗? 不确定,我认为最好写一个问题。【参考方案4】:

注意: sn-p 仅在 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);
);
&lt;iframe id="mainframe" src=""&gt;&lt;/iframe&gt;

这将成功跟踪src 属性更改,以及在 iframe 本身内所做的任何 URL 更改。

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

我也使用此代码创建了gist。你也可以查看我的另一个answer。它有点深入了解它的工作原理。

【讨论】:

在 Chrome 95 中使用 *** 中的“运行代码 sn-p”按钮运行此代码 sn-p 会向我显示此错误:“消息”:“未捕获的安全错误:已阻止带有来源的帧 \ "null\" 访问跨域框架。", "filename": "stacksnippets.net/js", "lineno": 24, "colno": 30 【参考方案5】:

iframe 始终保留父页面,您应该使用它来检测您在 iframe 中的哪个页面:

HTML代码:

<iframe id="iframe" frameborder="0" scrolling="no" onload="resizeIframe(this)"  src="www.google.com"></iframe>

Js:

    function resizeIframe(obj) 
        alert(obj.contentWindow.location.pathname);
    

【讨论】:

【参考方案6】:

从 Jquery 3.0 版开始,您可能会遇到错误

TypeError: url.indexOf 不是函数

这很容易解决

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

【讨论】:

【参考方案7】:

这是在Commerce SagePay 和Commerce Paypoint Drupal 模块中使用的方法,它基本上通过先加载自己的 iframe,然后再加载外部 iframe,将 document.location.href 与旧值进行比较。

所以基本上这个想法是加载空白页面作为占位符,带有自己的 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 的剩余部分被垃圾收集。

以上是关于iFrame src 更改事件检测?的主要内容,如果未能解决你的问题,请参考以下文章

javascript-如何检测 iframe 中的滚动事件?

检测 AJAX 请求调用的 Iframe 的滚动事件

检测由AJAX请求调用的Iframe的滚动事件

带有 Iframe 事件和更改处理的 ReactJs

iframe 加载事件

在iframe上检测鼠标?