如何识别网页是在 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;
    

topself 都是 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.topframe iframe 的内容中运行时会返回 true【参考方案2】:

在与父级同源的 iframe 中时,window.frameElement 方法返回嵌入窗口的元素(例如 iframeobject)。否则,如果在***上下文中浏览,或者如果父框架和子框架具有不同的来源,它将评估为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.topwindow !== 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 中加载内容

不要同时在 Owl Carousel 中加载所有 iFrame

图像在 iframe 中加载后未从缓存中加载

如何使用 Jquery 在 iframe 中加载 url

将参数附加到单击后在 DOM 中加载的 iframe

Facebook 如何发布 json 并在 iframe 中加载响应?