如何识别网页是在 iframe 中加载还是直接加载到浏览器窗口中?
Posted
技术标签:
【中文标题】如何识别网页是在 iframe 中加载还是直接加载到浏览器窗口中?【英文标题】:How to identify if a webpage is being loaded inside an iframe or directly into the browser window? 【发布时间】:2008-11-28 15:45:25 【问题描述】:我正在编写一个基于 iframe 的 Facebook 应用程序。现在我想使用相同的 html 页面来呈现普通网站以及 facebook 中的画布页面。我想知道我是否可以确定页面是在 iframe 中加载还是直接在浏览器中加载?
【问题讨论】:
几个不错的方法(包括cmets):tommcfarlin.com/check-if-a-page-is-in-an-iframe 外部链接不是回答问题的好方法。特别是如果那些外部链接的内容可以减少到return window.location !== window.parent.location
。
【参考方案1】:
由于same origin policy,浏览器可以阻止对window.top
的访问。 IE错误也会发生。这是工作代码:
function inIframe ()
try
return window.self !== window.top;
catch (e)
return true;
top
和 self
都是 window
对象(以及 parent
),因此您可以查看您的窗口是否是顶部窗口。
【讨论】:
这似乎在 Firefox 中运行良好。它也适用于其他浏览器吗? 在 iframe 中有一个带有 iframe 的页面,从我的子 iframe 测试我的父 iframe 是否嵌入在页面中,我使用 if (parent === top) @sglessard 如果子页面和父页面来自不同的域,那么 Firefox 将抱怨同源策略 (www.w3.org/Security/wiki/Same_Origin_Policy) 并且代码将不起作用 这适用于我的 IE 11、10 和 9。它也适用于 FF 和 Opera,当然还有 Chrome。我没有遇到任何问题。 对于那些仍在使用 1995 年最新技术的人,window.self !== window.top
在 frame
、 或 iframe
的内容中运行时会返回 true
。【参考方案2】:
在与父级同源的 iframe 中时,window.frameElement
方法返回嵌入窗口的元素(例如 iframe
或 object
)。否则,如果在***上下文中浏览,或者如果父框架和子框架具有不同的来源,它将评估为null
。
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
这是一个HTML Standard,基本支持所有现代浏览器。
【讨论】:
注意,根据W3C(WHATWG 表示它应该返回 null),尝试读取frameElement
将在跨域 iframe 中引发 SecurityError 异常。因此,您可能希望将其包装在 try...catch
语句中。
在iframe
内外获取null
MDN 的描述说“如果嵌入它的文档具有不同的来源(例如来自不同的域),则它为空。”
只是为了澄清回报应该是什么:Returns the element (such as <iframe> or <object>) in which the window is embedded, or null if the element is either top-level or is embedded into a document with a different script origin; that is, in cross-origin situations.
在同域嵌套 iframe 上像魅力一样工作。非常感谢!【参考方案3】:
RoBorg 是正确的,但我想补充一点。
在 IE7/IE8 中,当微软在他们的浏览器中添加 Tabs 时,他们破坏了一件事情,如果你不小心就会对你的 JS 造成严重破坏。
想象一下这个页面布局:
MainPage.html
IframedPage1.html (named "foo")
IframedPage2.html (named "bar")
IframedPage3.html (named "baz")
现在在“baz”框架中单击一个链接(没有目标,在“baz”框架中加载)它可以正常工作。
如果页面被加载,我们称之为 special.html,使用 JS 检查“it”是否有一个名为“bar”的父框架,它将返回 true(预期)。
现在让我们说 special.html 页面在加载时会检查父框架(是否存在及其名称,如果它是“bar”,它会在 bar 框架中重新加载自身。例如
if(window.parent && window.parent.name == 'bar')
window.parent.location = self.location;
到目前为止一切顺利。现在问题来了。
可以说,不是像往常一样单击原始链接,而是在“baz”框架中加载 special.html 页面,而是单击它或选择在新选项卡中打开它。
当新标签加载时(根本没有父框架!)IE 将进入无限循环的页面加载!因为 IE在 javascript 中“复制”框架结构,使得新标签页确实有一个父标签,并且该父标签的名称为“bar”。
好消息是检查:
if(self == top)
//this returns true!
在那个新选项卡中确实返回 true,因此您可以测试这种奇怪的情况。
【讨论】:
此错误是否已修复?我无法在 IE8 中重现它。我总是得到正确的window.parent
。
不确定 - 我将再次针对 IE9 及更低版本运行我的测试套件并发布更新。
2021 年了,我还在 IE 的坟墓上跳舞。
可悲的是,我仍然支持 IE……尽管我肯定会庆祝我不再需要的那一天。 ;-)【参考方案4】:
我不确定这个示例如何适用于旧版 Web 浏览器,但我将它用于 IE、Firefox 和 Chrome 没有问题:
var iFrameDetection = (window === window.parent) ? false : true;
【讨论】:
或者干脆!(window===window.parent)
window !== window.parent
iFrameDetection
在弹出窗口的情况下返回误报。所以要正确检测 iframe,它应该是 var iFrameDetection = (window === window.parent || window.opener) ? false : true;
@CalculatorFeline 我更喜欢作者所写的明确声明,因为它乍一看更容易理解,因此代码错误更少。 Minificator 工具为我完成了这项工作,以便在部署时以最短的方式重写它。为什么我应该编写对人类来说可读性更差的缩小代码?
Ivan 建议使用!==
怎么样?【参考方案5】:
在 Firefox 6.0 扩展 (Addon-SDK 1.0) 的内容脚本中,接受的答案对我不起作用:Firefox 在以下各项中执行内容脚本:***窗口和所有 iframe。
在内容脚本中,我得到以下结果:
(window !== window.top) : false
(window.self !== window.top) : true
这个输出的奇怪之处在于,无论代码是在 iframe 中还是在***窗口中运行,它总是一样的。
另一方面,谷歌浏览器似乎只在***窗口中执行我的内容脚本一次,所以上述内容根本不起作用。
在两个浏览器的内容脚本中最终对我有用的是:
console.log(window.frames.length + ':' + parent.frames.length);
如果没有 iframe,它将打印 0:0
,在包含一个框架的***窗口中,它会打印 1:1
,而在文档的唯一 iframe 中,它会打印 0:1
。
这允许我的扩展程序在两个浏览器中确定是否存在任何 iframe,此外还可以在 Firefox 中确定它是否在其中一个 iframe 中运行。
【讨论】:
试试document.defaultView.self === document.defaultView.top
或window !== window.top
。在 Firefox 的附加 SDK 的内容脚本中,全局的 self
对象是一个用于与主脚本通信的对象。【参考方案6】:
if ( window !== window.parent )
// The page is in an iframe
else
// The page is not in an iframe
【讨论】:
除非您打开自己网站的 iframe。【参考方案7】:我正在使用这个:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
【讨论】:
你为什么这样做:.indexOf("HTMLIFrameElement")
?
.indexOf('foobarbaz') > -1 是一种检查“子字符串匹配”的方法(即可以在字符串中的某处找到“foobarbaz”吗?),但不使用正则表达式。它在逻辑上等同于 .match(/foobarbaz/)。它(用于:-)适用于更广泛的浏览器,并且仍然可能出于习惯或担心正则表达式机制的性能而使用。【参考方案8】:
以这个 javascript 函数为例说明如何完成此操作。
function isNoIframeOrIframeInMyHost()
// Validation: it must be loaded as the top page, or if it is loaded in an iframe
// then it must be embedded in my own domain.
// Info: IF top.location.href is not accessible THEN it is embedded in an iframe
// and the domains are different.
var myresult = true;
try
var tophref = top.location.href;
var tophostname = top.location.hostname.toString();
var myhref = location.href;
if (tophref === myhref)
myresult = true;
else if (tophostname !== "www.yourdomain.com")
myresult = false;
catch (error)
// error is a permission error that top.location.href is not accessible
// (which means parent domain <> iframe domain)!
myresult = false;
return myresult;
【讨论】:
【参考方案9】:目前最佳的旧版浏览器帧中断脚本
其他解决方案对我不起作用。这个适用于所有浏览器:
防御点击劫持的一种方法是在每个不应加框的页面中包含“框架破坏”脚本。即使在不支持 X-Frame-Options-Header 的旧版浏览器中,以下方法也可以防止网页被框架化。
在文档 HEAD 元素中,添加以下内容:
<style id="antiClickjack">bodydisplay:none !important;</style>
首先为样式元素本身应用一个 ID:
<script type="text/javascript">
if (self === top)
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
else
top.location = self.location;
</script>
这样,所有内容都可以在文档 HEAD 中,您的 API 中只需要一个方法/标签库。
参考:https://www.codemagi.com/blog/post/194
【讨论】:
框架不是唯一的威胁,反对呢(意思是使用 HTML5 对象标签替换 iframe 标签).. 我认为这不会起作用..【参考方案10】:我实际上曾经检查过 window.parent 并且它对我有用,但是最近 window 是一个循环对象,并且总是有一个父键,iframe 或没有 iframe。
因为 cmets 建议与 window.parent 作品进行比较。如果 iframe 与父网页完全相同,则不确定这是否可行。
window === window.parent;
【讨论】:
在主窗口上下文中的 chrome 中window.parent === window
是真的。所以你的答案是不正确的。并且这种比较可以用于检查(至少在chrome中,没有在其他浏览器中测试过)。
在 iFrame 的上下文中不起作用,但 ` if(window === window.parent) ` 可以。 我不会标记你,因为我不知道为什么它不起作用【参考方案11】:
由于您是在 facebook 应用程序的上下文中询问的,因此您可能需要考虑在发出初始请求时在服务器上检测这一点。如果从 iframe 调用 Facebook 将传递包括 fb_sig_user 键在内的一系列查询字符串数据。
由于您可能需要在您的应用中检查和使用这些数据,因此请使用它来确定要呈现的适当上下文。
【讨论】:
【参考方案12】:function amiLoadedInIFrame()
try
// Introduce a new propery in window.top
window.top.dummyAttribute = true;
// If window.dummyAttribute is there.. then window and window.top are same intances
return !window.dummyAttribute;
catch(e)
// Exception will be raised when the top is in different domain
return true;
【讨论】:
【参考方案13】:按照@magnoz 的说法,这是他的答案的代码实现。
constructor()
let windowLen = window.frames.length;
let parentLen = parent.frames.length;
if (windowLen == 0 && parentLen >= 1)
this.isInIframe = true
console.log('Is in Iframe!')
else
console.log('Is in main window!')
【讨论】:
【参考方案14】:var inIFrame = window.location !== window.parent.location;
【讨论】:
【参考方案15】:这是一段我用过几次的古老代码:
if (parent.location.href == self.location.href)
window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
【讨论】:
添加到 Eneko Alonso:这工作(parent.location == self.location)
@piotr_cz,它仅在同源策略允许访问parent.location
时才有效。否则抛出异常【参考方案16】:
如果您想知道用户是否从 facebook 页面选项卡或画布访问您的应用,请检查签名请求。如果你不明白,可能用户没有从 facebook 访问。 确保确认 signed_request 字段结构和字段内容。
使用 php-sdk,您可以获得这样的签名请求:
$signed_request = $facebook->getSignedRequest();
您可以在此处阅读有关签名请求的更多信息:
https://developers.facebook.com/docs/reference/php/facebook-getSignedRequest/
这里:
https://developers.facebook.com/docs/reference/login/signed-request/
【讨论】:
【参考方案17】:这对我来说是最简单的解决方案。
<p id="demofsdfsdfs"></p>
<script>
if(window.self !== window.top)
//run this code if in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "in frame";
else
//run code if not in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "no frame";
</script>
【讨论】:
【参考方案18】:if (window.frames.length != parent.frames.length) page loaded in iframe
但仅当您的页面中的 iframe 数量不同以及在 iframe 中加载您的页面时。在您的页面中不使用 iframe 以 100% 保证此代码的结果
【讨论】:
确定窗口是否在 iframe 内不是一个非常优雅的解决方案,也不能保证您也可以从 parent.frames 中读取。 @Percy 为什么不呢?window.frames
不是允许跨源属性吗?【参考方案19】:
在每个页面中编写这段 javascript
if (self == top)
window.location = "Home.aspx";
然后它会自动重定向到主页。
【讨论】:
如果框架不在,您的重定向将起作用。所以我将无法在您的网站中导航。其次,存在防止从父页面重定向的技术。以上是关于如何识别网页是在 iframe 中加载还是直接加载到浏览器窗口中?的主要内容,如果未能解决你的问题,请参考以下文章
内容安全策略框架祖先。 iframe 不会在 iOS10 中加载内容