Jak ustalić, czy strona internetowa jest ładowana w ramce iframe, czy bezpośrednio w oknie przeglądarki?

596

Piszę aplikację facebook opartą na iframe. Teraz chcę użyć tej samej strony HTML do renderowania normalnej strony internetowej, a także strony kanwy na Facebooku. Chcę wiedzieć, czy mogę ustalić, czy strona została załadowana w ramce iframe, czy bezpośrednio w przeglądarce?

akshat
źródło
2
Kilka fajnych sposobów (w tym komentarze): tommcfarlin.com/check-if-a-page-is-in-an-iframe
simhumileco 20.01.2019

Odpowiedzi:

1106

Przeglądarki mogą blokować dostęp z window.toppowodu tej samej zasady pochodzenia . Występują również błędy IE. Oto działający kod:

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

topi selfoba są windowobiektami (wraz z parent), więc widzisz, czy twoje okno jest oknem górnym.

Greg
źródło
4
Wydaje się, że działa to dobrze w przeglądarce Firefox. Czy działa również w innych przeglądarkach?
akshat
5
Posiadanie strony z elementem iframe w ramce iframe w celu przetestowania elementu iframe mojego dziecka, czy element iframe mojego rodzica został osadzony na stronie, użyłem if (rodzic === góra)
sglessard
7
@sglessard Jeśli strona podrzędna i rodzic pochodzą z różnych domen, Firefox będzie narzekał na zasady tego samego pochodzenia (www.w3.org/Security/wiki/Same_Origin_Policy), a kod nie będzie działać
Gaurang Jadia
18
Działa to w IE 11, 10 i 9 dla mnie. Działa również w FF i Operze, a także, oczywiście, w Chrome. Nie napotkałem z tym żadnych problemów.
jeremysawesome
4
Dla tych, którzy nadal kołysanie najpóźniej w 1995 tech, window.self !== window.toppowraca truepo uruchomieniu od wewnątrz treści frame, lub iframe .
Jeromy French
84

W ramce iframe tego samego źródła co element nadrzędny window.frameElementmetoda zwraca element (np. iframeLub object), w którym osadzone jest okno. W przeciwnym razie, jeśli przeglądasz w kontekście najwyższego poziomu lub jeśli ramka nadrzędna i podrzędna mają różne źródła, będzie to oceniać null.

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

Jest to standard HTML z podstawową obsługą we wszystkich nowoczesnych przeglądarkach.

Konstantin Smolianin
źródło
2
Uwaga: próba odczytu frameElementspowoduje zgłoszenie wyjątku SecurityError w elementach iframe pochodzących z różnych źródeł, zgodnie z W3C ( WHATWG twierdzi, że zamiast tego powinien zwrócić null). Możesz więc zawinąć go w try...catchinstrukcję.
Oriol
5
dostać się nulldo i pozaiframe
OlehZiniak
4
Opis w MDN mówi, że „jeśli dokument, w którym jest osadzony, ma inne pochodzenie (na przykład znajduje się w innej domenie), jest to nieważne”.
xeophin
Aby wyjaśnić, jaki powinien być zwrot: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 ma rację, ale chciałem dodać notatkę dodatkową.

W IE7 / IE8, kiedy Microsoft dodał Tabs do swojej przeglądarki, złamali jedną rzecz, która spowoduje spustoszenie w JS, jeśli nie będziesz ostrożny.

Wyobraź sobie ten układ strony:

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

Teraz w ramce „baz” klikasz link (bez celu, ładuje się w ramce „baz”), działa dobrze.

Jeśli strona, która zostanie załadowana, nazwijmy ją special.html, używa JS, aby sprawdzić, czy „it” ma ramkę nadrzędną o nazwie „bar”, zwróci wartość true (oczekiwana).

Powiedzmy teraz, że strona special.html podczas ładowania sprawdza ramkę nadrzędną (pod kątem istnienia i jej nazwy, a jeśli jest to „pasek”, ładuje się ponownie w ramce paska, np.

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

Na razie w porządku. Teraz nadchodzi błąd.

Powiedzmy, że zamiast klikać oryginalny link jak zwykle i ładować stronę special.html w ramce „baz”, kliknąłeś ją środkowo lub zdecydowałeś się otworzyć w nowej karcie.

Gdy ta nowa karta zostanie załadowana ( bez żadnych ramek nadrzędnych! ) IE przejdzie do niekończącej się pętli ładowania strony! ponieważ IE „kopiuje” strukturę ramki w JavaScript, tak że nowa karta MUSI mieć element nadrzędny, a ten element nadrzędny MA NAZWĘ „pasek”.

Dobrą wiadomością jest to, że sprawdzanie:

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

w tej nowej karcie zwraca wartość true, a zatem można przetestować ten dziwny warunek.

scunliffe
źródło
Czy w międzyczasie naprawiono błąd? Nie mogę go odtworzyć w IE8. Zawsze dostaję prawidłowy window.parent.
user123444555621
1
Nie jestem pewien - uruchomię ponownie mój pakiet testowy przeciwko IE9 i poniżej i opublikuję aktualizację.
scunliffe,
18

Akceptowana odpowiedź nie działała dla mnie w skrypcie zawartości rozszerzenia Firefox 6.0 (Addon-SDK 1.0): Firefox wykonuje skrypt zawartości w każdym: oknie najwyższego poziomu i we wszystkich elementach iframe.

Wewnątrz skryptu zawartości otrzymuję następujące wyniki:

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

Dziwną rzeczą w tym wyniku jest to, że zawsze jest taki sam, niezależnie od tego, czy kod jest uruchamiany w ramce iframe, czy w oknie najwyższego poziomu.

Z drugiej strony Google Chrome wydaje się, że mój skrypt zawartości wykonuje się tylko raz w oknie najwyższego poziomu, więc powyższe w ogóle nie działałoby.

To, co w końcu zadziałało dla mnie w skrypcie zawartości w obu przeglądarkach, to:

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

Bez iframe drukuje się 0:0w oknie najwyższego poziomu zawierającym jedną klatkę 1:1, a także w jedynym iframe dokumentu, który drukuje 0:1.

Dzięki temu moje rozszerzenie może określić w obu przeglądarkach, czy są obecne iframe, a dodatkowo w Firefoksie, czy jest uruchomione w jednym z iframe.

magnoz
źródło
1
Spróbuj document.defaultView.self === document.defaultView.toplub window !== window.top. W skrypcie zawartości dodatku SDK dla Firefoksa selfobiekt globalny jest obiektem służącym do komunikacji ze skryptem głównym.
Rob W
13

Nie jestem pewien, jak działa ten przykład w starszych przeglądarkach internetowych, ale używam tego w IE, Firefox i Chrome bez problemu:

var iFrameDetection = (window === window.parent) ? false : true;
portal TheAnGeLs
źródło
6
Lub po prostu!(window===window.parent)
CalculatorFeline
11
okno! == window.parent
Ivan Leonenko
5

Używam tego:

var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
mikewolf78
źródło
Dlaczego to robisz .indexOf("HTMLIFrameElement"):?
Łukasz
.indexOf ('foobarbaz')> -1 jest sposobem sprawdzenia „dopasowania podłańcucha” (tzn. czy „foobarbaz” można znaleźć gdzieś w ciągu?), ale bez użycia wyrażeń regularnych. Jest to logicznie równoważne z .match (/ foobarbaz /). To (używane do :-) działa na szerszej gamie przeglądarek i nadal może być używane albo z przyzwyczajenia, albo z powodu obaw o wydajność maszyn wyrażeń regularnych.
Chuck Kollars
2

Użyj tej funkcji javascript jako przykładu, jak to osiągnąć.

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
źródło
2

Najlepszy na razie skrypt do łamania starszych ramek przeglądarki

Inne rozwiązania nie działały dla mnie. Ten działa we wszystkich przeglądarkach:

Jednym ze sposobów obrony przed kliknięciem jest umieszczenie skryptu „przerywającego ramki” na każdej stronie, którego nie należy oprawiać w ramkę. Poniższa metodologia zapobiegnie kadrowaniu strony internetowej nawet w starszych przeglądarkach, które nie obsługują nagłówka X-Frame-Options-Header.

W elemencie HEAD dokumentu dodaj:

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

Najpierw zastosuj identyfikator do samego elementu stylu:

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

W ten sposób wszystko może znajdować się w dokumencie HEAD i potrzebujesz tylko jednej metody / taglib w swoim API.

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

Albert Olivé
źródło
1
kadrowanie nie jest jedynym zagrożeniem, co ze sprzeciwem (co oznacza użycie tagu obiektu HTML5, który zastępuje tag iframe) .. myślę, że to nie zadziała przeciwko temu ..
Raymond Nijland
2
if ( window.location !== window.parent.location ) 
{
      // The page is in an iframe   
} 
else 
{     
      // The page is not in an iframe   
}
William
źródło
1

Ponieważ pytasz w kontekście aplikacji na Facebooku, możesz rozważyć wykrycie tego na serwerze, gdy zostanie wykonane pierwsze żądanie. Facebook przekaże wiele pytających danych, w tym klucz fb_sig_user, jeśli zostanie wywołany z elementu iframe.

Ponieważ prawdopodobnie musisz sprawdzić te dane i użyć ich w swojej aplikacji, użyj ich, aby określić odpowiedni kontekst do renderowania.

HectorMac
źródło
1

W rzeczywistości sprawdzałem window.parent i zadziałało to dla mnie, ale ostatnio okno jest obiektem cyklicznym i zawsze ma klucz nadrzędny, iframe lub brak iframe.

Ponieważ komentarze sugerują, że trudno porównywać z Window.Parent działa. Nie jestem pewien, czy to zadziała, jeśli element iframe jest dokładnie taki sam jak strona nadrzędna.

window === window.parent;
Jabran Saeed
źródło
w chrome w głównym oknie kontekst window.parent === windowjest prawdziwy. Twoja odpowiedź jest nieprawidłowa. I to porównanie może być użyte do sprawdzenia (przynajmniej w chrome, nie testowałem go w innych przeglądarkach).
MarkosyanArtur
NIE DZIAŁA w kontekście iFrame, ale działa `if (window === window.parent) {`. Nie zaznaczam cię, bo nie wiem, dlaczego to nie działa
www-0av-Com
-1

To starożytny fragment kodu, którego użyłem kilka razy:

if (parent.location.href == self.location.href) {
    window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
Błąd 601
źródło
Aby dodać do Eneko Alonso: to działa(parent.location == self.location)
piotr_cz
@piotr_cz, działa tylko wtedy, gdy zasady tego samego pochodzenia zezwalają na dostęp parent.location. W przeciwnym razie zgłoszony zostanie wyjątek
Dan
-1

Jeśli chcesz wiedzieć, czy użytkownik uzyskuje dostęp do Twojej aplikacji z karty strony na Facebooku lub kanwy, zaznacz Podpisane żądanie. Jeśli go nie dostaniesz, prawdopodobnie użytkownik nie ma dostępu z Facebooka. Aby upewnić się, że potwierdzono strukturę pól z podpisanymi żądaniami i zawartość pól.

Z php-sdk możesz uzyskać takie podpisane żądanie w następujący sposób:

$signed_request = $facebook->getSignedRequest();

Możesz przeczytać więcej o Podpisanym Żądaniu tutaj:

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

i tu:

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

Joao Belchior
źródło
-1

To zakończyło się dla mnie najprostszym rozwiązaniem.

    <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
źródło
-4
if (window.frames.length != parent.frames.length) { page loaded in iframe }

Ale tylko wtedy, gdy liczba elementów iframe różni się na stronie i stronie, która ładuje Cię w ramce iframe. Nie rób iframe na swojej stronie, aby mieć 100% gwarancję wyniku tego kodu

Vova Popov
źródło
Niezbyt eleganckie rozwiązanie pozwalające ustalić, czy okno znajduje się w elemencie iframe, czy nie, i nie ma gwarancji, że będziesz mógł czytać z pliku parent.frame.
Percy
-4

Napisz javascript na każdej stronie

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

Następnie automatycznie przekieruje na stronę główną.

Shibin
źródło
4
Twoje przekierowanie będzie działać, gdy nie będziesz ramki. Dlatego nie będę mógł nawigować w Twojej witrynie. Po drugie, istnieją techniki zapobiegające przekierowaniu ze strony nadrzędnej.
Marius Balčytis