¿Cómo identificar si una página web se está cargando dentro de un iframe o directamente en la ventana del navegador?

598

Estoy escribiendo una aplicación de Facebook basada en iframe. Ahora quiero usar la misma página html para representar el sitio web normal, así como la página de lienzo dentro de Facebook. ¿Quiero saber si puedo determinar si la página se ha cargado dentro del iframe o directamente en el navegador?

akshat
fuente
2
Algunas buenas maneras (incluidos los comentarios): tommcfarlin.com/check-if-a-page-is-in-an-iframe
simhumileco

Respuestas:

1109

Los navegadores pueden bloquear el acceso window.topdebido a la misma política de origen . También se producen errores de IE. Aquí está el código de trabajo:

function inIframe () {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}

topy selfson ambos windowobjetos (junto con parent), por lo que está viendo si su ventana es la ventana superior.

Greg
fuente
44
Esto parece funcionar bien en Firefox. ¿Funciona también en otros navegadores?
akshat
55
Al tener una página con un iframe dentro de un iframe, para probar de mi hijo iframe si mi iframe principal estaba incrustado en la página, usé if (parent === top)
sglessard
77
@sglessard Si la página secundaria y la principal son de diferentes dominios, Firefox se quejará por la Política del mismo origen (www.w3.org/Security/wiki/Same_Origin_Policy) y el código no funcionará
Gaurang Jadia
18
Esto funciona en IE 11, 10 y 9 para mí. También funciona en FF y Opera, así como, por supuesto, Chrome. No me he encontrado con ningún problema.
jeremysawesome
44
Para aquellos que todavía tienen lo último en tecnología de 1995, window.self !== window.topregresa truecuando se ejecuta desde el contenido de frame, o iframe .
Jeromy French
84

Cuando se encuentra en un iframe en el mismo origen que el padre, el window.frameElementmétodo devuelve el elemento (por ejemplo, iframeor object) en el que está incrustada la ventana. De lo contrario, si navega en un contexto de nivel superior, o si el marco primario y el secundario tienen orígenes diferentes, se evaluará null.

window.frameElement
  ? 'embedded in iframe or object'
  : 'not embedded or cross-origin'

Este es un estándar HTML con soporte básico en todos los navegadores modernos.

Konstantin Smolyanin
fuente
2
Tenga en cuenta que intentar leer frameElementarrojará una excepción SecurityError en iframes de origen cruzado, de acuerdo con W3C ( WHATWG dice que debería devolver nulo en su lugar). Por lo tanto, es posible que desee envolverlo dentro de una try...catchdeclaración.
Oriol
55
entrar nully salir deiframe
OlehZiniak
44
La descripción en MDN dice que "si el documento en el que está incrustado tiene un origen diferente (como haber sido ubicado desde un dominio diferente), esto es nulo".
xeophin
Solo para aclarar cuál debería ser el retorno: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.
Art3mix
26

RoBorg es correcto, pero quería agregar una nota al margen.

En IE7 / IE8 cuando Microsoft agregó pestañas a su navegador, rompieron una cosa que causará estragos en su JS si no tiene cuidado.

Imagina este diseño de página:

MainPage.html
  IframedPage1.html   (named "foo")
  IframedPage2.html   (named "bar")
    IframedPage3.html (named "baz")

Ahora, en el marco "baz", hace clic en un enlace (sin destino, se carga en el marco "baz") funciona bien.

Si la página que se carga, llamémosla special.html, usa JS para verificar si "it" tiene un marco principal llamado "bar", devolverá verdadero (esperado).

Ahora digamos que la página special.html cuando se carga, comprueba el marco principal (para ver si existe y su nombre, y si es "bar", se recarga en el marco de la barra, p. Ej.

if(window.parent && window.parent.name == 'bar'){
  window.parent.location = self.location;
}

Hasta aquí todo bien. Ahora viene el error.

Digamos que en lugar de hacer clic en el enlace original como de costumbre y cargar la página special.html en el marco "baz", hizo clic con el botón central o eligió abrirlo en una pestaña nueva.

Cuando se carga esa nueva pestaña (¡ sin ningún marco principal en absoluto! ) IE entrará en un ciclo infinito de carga de página. porque IE "copia" la estructura del marco en JavaScript de modo que la nueva pestaña TIENE un padre, y ese padre TIENE el nombre "barra".

La buena noticia es que verificamos:

if(self == top){
  //this returns true!
}

en esa nueva pestaña se devuelve verdadero y, por lo tanto, puede probar esta extraña condición.

scunliffe
fuente
¿Se ha solucionado el error mientras tanto? No puedo reproducirlo en IE8. Siempre consigo lo correcto window.parent.
user123444555621
1
No estoy seguro: volveré a ejecutar mi conjunto de pruebas con IE9 y versiones posteriores y publicaré una actualización.
scunliffe
18

La respuesta aceptada no funcionó para mí dentro del script de contenido de una extensión de Firefox 6.0 (Addon-SDK 1.0): Firefox ejecuta el script de contenido en cada uno: la ventana de nivel superior y en todos los iframes.

Dentro del script de contenido obtengo los siguientes resultados:

 (window !== window.top) : false 
 (window.self !== window.top) : true

Lo extraño de esta salida es que siempre es igual independientemente de si el código se ejecuta dentro de un iframe o la ventana de nivel superior.

Por otro lado, Google Chrome parece ejecutar mi script de contenido solo una vez dentro de la ventana de nivel superior, por lo que lo anterior no funcionaría en absoluto.

Lo que finalmente funcionó para mí en un script de contenido en ambos navegadores es esto:

 console.log(window.frames.length + ':' + parent.frames.length);

Sin iframes, esto se imprime 0:0, en una ventana de nivel superior que contiene un marco que imprime 1:1, y en el único iframe de un documento que imprime0:1 .

Esto permite que mi extensión determine en ambos navegadores si hay presentes iframes, y adicionalmente en Firefox si se ejecuta dentro de uno de los iframes.

magnoz
fuente
1
Prueba document.defaultView.self === document.defaultView.topo window !== window.top. En el script de contenido del SDK de complemento de Firefox, el selfobjeto global es un objeto utilizado para comunicarse con el script principal.
Rob W
13

No estoy seguro de cómo funciona este ejemplo para navegadores web más antiguos, pero lo uso para IE, Firefox y Chrome sin el problema:

var iFrameDetection = (window === window.parent) ? false : true;
portal TheAnGeLs
fuente
66
O simplemente!(window===window.parent)
CalculatorFeline
11
window! == window.parent
Ivan Leonenko
5

Estoy usando esto:

var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
mikewolf78
fuente
¿Por qué haces esto .indexOf("HTMLIFrameElement")?
Lucas
.indexOf ('foobarbaz')> -1 es una forma de verificar la "coincidencia de subcadena" (es decir, ¿se puede encontrar 'foobarbaz' en algún lugar dentro de la cadena?), pero sin usar expresiones regulares. Es lógicamente equivalente a .match (/ foobarbaz /). (Solía ​​:-) funcionar en una gama más amplia de navegadores, y aún puede usarse por costumbre o por temor al rendimiento de la maquinaria de expresión regular.
Chuck Kollars el
2

Use esta función de JavaScript como ejemplo sobre cómo lograr esto.

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;
}
Rolf
fuente
2

Mejor secuencia de comandos de ruptura de marcos de navegador heredados

Las otras soluciones no funcionaron para mí. Este funciona en todos los navegadores:

Una forma de defenderse contra el clickjacking es incluir una secuencia de comandos "frame-breaker" en cada página que no se debe enmarcar. La siguiente metodología evitará que una página web se enmarque incluso en navegadores heredados, que no admiten el encabezado X-Frame-Options-Header.

En el elemento HEAD del documento, agregue lo siguiente:

<style id="antiClickjack">body{display:none !important;}</style>

Primero aplique una ID al elemento de estilo en sí:

<script type="text/javascript">
   if (self === top) {
       var antiClickjack = document.getElementById("antiClickjack");
       antiClickjack.parentNode.removeChild(antiClickjack);
   } else {
       top.location = self.location;
   }
</script>

De esta forma, todo puede estar en el documento HEAD y solo necesita un método / taglib en su API.

Referencia: https://www.codemagi.com/blog/post/194

Albert Olivé
fuente
1
encuadre no es la única amenaza, ¿qué hay de la objeción (es decir, el uso de etiqueta de objeto HTML 5 que sustituye iframe etiqueta) .. creo que esto no va a funcionar en contra de eso ..
Raymond Nijland
2
if ( window.location !== window.parent.location ) 
{
      // The page is in an iframe   
} 
else 
{     
      // The page is not in an iframe   
}
Guillermo
fuente
1

Dado que está preguntando en el contexto de una aplicación de Facebook, puede considerar detectar esto en el servidor cuando se realiza la solicitud inicial. Facebook pasará un conjunto de datos de cadena de consulta, incluida la clave fb_sig_user, si se llama desde un iframe.

Dado que probablemente necesite verificar y usar estos datos de todos modos en su aplicación, úselos para determinar el contexto apropiado para representar.

HectorMac
fuente
1

De hecho, solía verificar window.parent y funcionó para mí, pero últimamente window es un objeto cíclico y siempre tiene una clave principal, iframe o no iframe.

Como los comentarios sugieren una comparación difícil con window.parent funciona. No estoy seguro de si esto funcionará si el iframe es exactamente la misma página web que el padre.

window === window.parent;
Jabran Saeed
fuente
en Chrome en el contexto de la ventana principal window.parent === windowes cierto. Entonces tu respuesta es incorrecta. Y esta comparación podría usarse para verificar (al menos en Chrome, no lo probé en otros navegadores).
MarkosyanArtur
NO FUNCIONA en el contexto de iFrame, pero `if (window === window.parent) {` sí. No te estoy marcando porque no sé por qué no funciona
www-0av-Com
-1

Es un código antiguo que he usado varias veces:

if (parent.location.href == self.location.href) {
    window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
Error 601
fuente
Para agregar a Eneko Alonso: esto funciona(parent.location == self.location)
piotr_cz
@piotr_cz, solo funciona si la política del mismo origen permite el acceso parent.location. De lo contrario, se produce una excepción
Dan
-1

Si desea saber si el usuario está accediendo a su aplicación desde la pestaña de la página de Facebook o el lienzo, verifique la Solicitud firmada. Si no lo obtienes, probablemente el usuario no esté accediendo desde Facebook. Para asegurarse, confirme la estructura y el contenido de los camposigned_request.

Con php-sdk puede obtener la solicitud firmada de esta manera:

$signed_request = $facebook->getSignedRequest();

Puede leer más sobre la solicitud firmada aquí:

https://developers.facebook.com/docs/reference/php/facebook-getSignedRequest/

y aquí:

https://developers.facebook.com/docs/reference/login/signed-request/

Joao Belchior
fuente
-1

Esto terminó siendo la solución más simple para mí.

    <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>
Alex Roseland
fuente
-4
if (window.frames.length != parent.frames.length) { page loaded in iframe }

Pero solo si el número de iframes difiere en su página y en la página que lo está cargando en iframe. No haga iframe en su página para tener el 100% de garantía del resultado de este código

Vova Popov
fuente
No es una solución muy elegante para determinar si la ventana está dentro de un iframe o no, y tampoco hay garantía de que pueda leer desde parent.frames.
Percy
-4

Escribe este javascript en cada página

if (self == top)
  { window.location = "Home.aspx"; }

Luego, redirigirá automáticamente a la página de inicio.

shibin
fuente
44
Su redirección funcionará cuando no esté si marco. Por lo tanto, no podría navegar en su sitio. En segundo lugar, existen técnicas para evitar el redireccionamiento desde la página principal.
Marius Balčytis