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 > 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);
<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
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);
);
<iframe id="mainframe" src=""></iframe>
这将成功跟踪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 更改事件检测?的主要内容,如果未能解决你的问题,请参考以下文章