Как определить, загружается ли веб-страница внутри iframe или непосредственно в окне браузера?

598

Я пишу приложение Facebook на основе iframe. Теперь я хочу использовать ту же HTML-страницу для рендеринга обычного веб-сайта, а также страницу холста в Facebook. Я хочу знать, могу ли я определить, была ли страница загружена внутри iframe или непосредственно в браузере?

Akshat
источник
2
Несколько хороших способов (включая комментарии): tommcfarlin.com/check-if-a-page-is-in-an-iframe
simhumileco

Ответы:

1109

Браузеры могут заблокировать доступ window.topиз-за той же политики происхождения . IE ошибки тоже имеют место. Вот рабочий код:

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

topи selfоба windowобъекта (вместе с parent), так что вы видите, является ли ваше окно верхним окном.

Greg
источник
4
Это похоже на работу в Firefox. Это работает и в других браузерах?
Акшат
5
Имея страницу с iframe внутри iframe, чтобы проверить iframe моего ребенка, был ли вставлен iframe моего родителя на страницу, я использовал if (parent === top)
sglessard
7
@sglessard Если дочерняя страница и родительский элемент принадлежат разным доменам, Firefox будет жаловаться на
одну и ту
18
Это работает в IE 11, 10 и 9 для меня. Также он работает в FF и Opera, а также, конечно, в Chrome. У меня не было никаких проблем с этим.
jeremysawesome
4
Для тех, кто все еще качает последние технологии 1995 года, window.self !== window.topвозвращается, trueкогда запускается из содержимого frame, или iframe .
Джером Френч
84

Когда iframe находится в том же источнике, что и родительский window.frameElementэлемент , метод возвращает элемент (например, iframeor object), в который встроено окно. В противном случае, если просмотр выполняется в контексте верхнего уровня, или если родительский и дочерний фреймы имеют разные источники, он оценивается как null.

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

Это стандарт HTML с базовой поддержкой во всех современных браузерах.

Константин Смолянин
источник
2
Примечание, попытка чтения frameElementвызовет исключение SecurityError в кросс-исходных фреймах, согласно W3C ( WHATWG говорит, что вместо этого должно возвращаться ноль). Таким образом, вы можете захотеть обернуть это внутри try...catchутверждения.
Oriol
5
попасть nullвнутрь и наружуiframe
OlehZiniak
4
В описании в 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.
Art3mix
26

Роборг прав, но я хотел добавить примечание.

В IE7 / IE8, когда Microsoft добавила вкладки в свой браузер, они нарушили одну вещь, которая вызовет хаос в вашем JS, если вы не будете осторожны.

Представьте себе этот макет страницы:

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

Теперь во фрейме «baz» вы нажимаете на ссылку (нет цели, загружается в фрейм «baz»), все работает нормально.

Если загружаемая страница, давайте назовем ее special.html, использует JS, чтобы проверить, есть ли у «нее» родительский фрейм с именем «bar», она вернет истину (ожидаемо).

Теперь допустим, что страница special.html при загрузке проверяет родительский фрейм (на наличие и его имя, и, если это «bar», перезагружается в фрейме bar. Например,

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

Все идет нормально. Теперь приходит ошибка.

Допустим, вместо того, чтобы нажимать на исходную ссылку, как обычно, и загружать страницу special.html во фрейме «baz», вы щелкаете ее по среднему щелчку или решили открыть ее в новой вкладке.

Когда эта новая вкладка загружается ( без родительских фреймов вообще! ), IE вступает в бесконечный цикл загрузки страницы! потому что IE «копирует» структуру фрейма в JavaScript, так что новая вкладка имеет родителя, и у этого родителя есть имя «bar».

Хорошей новостью является то, что проверка:

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

в этой новой вкладке возвращается true, и, таким образом, вы можете проверить это странное условие.

scunliffe
источник
Исправлена ​​ли ошибка за это время? Я не могу воспроизвести его в IE8. Я всегда получаю правильное window.parent.
user123444555621 10.09.12
1
Не уверен - я снова запущу свой тестовый набор для IE9 и ниже и выложу обновление.
Scunliffe
18

Принятый ответ не работал для меня в скрипте контента расширения Firefox 6.0 (Addon-SDK 1.0): Firefox выполняет скрипт контента в каждом: окне верхнего уровня и во всех фреймах.

Внутри скрипта контента я получаю следующие результаты:

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

Странная вещь в этом выводе состоит в том, что он всегда один и тот же, независимо от того, выполняется ли код внутри iframe или окна верхнего уровня.

С другой стороны, кажется, что Google Chrome выполняет мой контент-скрипт только один раз в окне верхнего уровня, поэтому вышеописанное не будет работать вообще.

Что в итоге сработало для меня в скрипте контента в обоих браузерах:

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

Без iframe это печатает 0:0, в окне верхнего уровня, содержащем один кадр, который он печатает 1:1, и в единственном iframe документа, который это печатает 0:1.

Это позволяет моему расширению определять в обоих браузерах, присутствуют ли какие-либо iframe, и дополнительно в Firefox, если оно запускается внутри одного из iframe.

magnoz
источник
1
Попробуйте document.defaultView.self === document.defaultView.topили window !== window.top. В сценарии содержимого дополнительного SDK Firefox глобальный selfобъект - это объект, используемый для связи с основным сценарием.
Роб W
13

Я не уверен, как этот пример работает для старых веб-браузеров, но я использую это для IE, Firefox и Chrome без и выпускать:

var iFrameDetection = (window === window.parent) ? false : true;
портал TheAnGeLs
источник
6
Или просто!(window===window.parent)
CalculatorFeline
11
окно! == window.parent
Иван Леоненко
5

Я использую это:

var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
mikewolf78
источник
Зачем ты это делаешь .indexOf("HTMLIFrameElement")?
Люк
.indexOf ('foobarbaz')> -1 - это способ проверки «совпадения подстроки» (т. е. можно ли найти «foobarbaz» где-нибудь в строке?), но без использования регулярных выражений. Логически эквивалентно .match (/ foobarbaz /). Он (используется для :-) работы в более широком диапазоне браузеров, и все еще может использоваться либо по привычке, либо из-за опасений по поводу производительности механизма регулярных выражений.
Чак Колларс
2

Используйте эту функцию 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;
}
Рольф
источник
2

Лучший на данный момент устаревший скрипт разрыва фрейма браузера

Другие решения не работали для меня. Этот работает во всех браузерах:

Один из способов защиты от кликджекинга состоит в том, чтобы на каждой странице был добавлен сценарий «разрыва фрейма», который не следует создавать в рамке. Следующая методология предотвратит создание веб-страницы даже в устаревших браузерах, которые не поддерживают X-Frame-Options-Header.

В элемент документа HEAD добавьте следующее:

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

Сначала примените идентификатор к самому элементу стиля:

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

Таким образом, все может быть в документе HEAD, и вам нужен только один метод / taglib в вашем API.

Ссылка: https://www.codemagi.com/blog/post/194

Альберт Оливе
источник
1
кадрирование - не единственная угроза, как насчет возражений (имеется в виду использование тега объекта HTML5, который заменяет тег iframe) ... я думаю, что это не сработает против этого ...
Рэймонд Нейланд
2
if ( window.location !== window.parent.location ) 
{
      // The page is in an iframe   
} 
else 
{     
      // The page is not in an iframe   
}
Уильям
источник
1

Поскольку вы спрашиваете в контексте приложения Facebook, вы можете рассмотреть возможность обнаружения этого на сервере, когда сделан первоначальный запрос. Facebook будет передавать кучу данных строки запроса, включая ключ fb_sig_user, если он вызывается из iframe.

Так как вам, вероятно, все равно нужно проверить и использовать эти данные в вашем приложении, используйте их, чтобы определить соответствующий контекст для рендеринга.

HectorMac
источник
1

Я на самом деле проверял window.parent, и он работал для меня, но в последнее время window является циклическим объектом и всегда имеет родительский ключ, iframe или нет iframe.

Поскольку комментарии предлагают трудно сравнивать с работами window.parent. Не уверен, будет ли это работать, если iframe точно такая же веб-страница, как родительский.

window === window.parent;
Джабран Саид
источник
в Chrome в контексте главного окна window.parent === windowэто правда. Так что ваш ответ неверен. И это сравнение может быть использовано для проверки (по крайней мере, в Chrome, не проверял его в других браузерах).
MarkosyanArtur
НЕ РАБОТАЕТ в контексте iFrame, но `if (window === window.parent) {` делает. Я не отмечаю вас, потому что я не знаю, почему это не работает
www-0av-Com
-1

Это древний кусок кода, который я использовал несколько раз:

if (parent.location.href == self.location.href) {
    window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
Error601
источник
Чтобы добавить Энеко Алонсо: это работает(parent.location == self.location)
piotr_cz
@piotr_cz, это работает, только если политика того же происхождения разрешает доступ parent.location. В противном случае выдается исключение
Дан
-1

Если вы хотите узнать, имеет ли пользователь доступ к вашему приложению на вкладке страницы Facebook или на холсте, проверьте подписанный запрос. Если вы не получили его, вероятно, пользователь не имеет доступа с Facebook. Чтобы убедиться, подтвердите структуру полей Sign_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/

Жоао Бельчиор
источник
-1

Это оказалось самым простым решением для меня.

    <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>
Алекс Розеланд
источник
-4
if (window.frames.length != parent.frames.length) { page loaded in iframe }

Но только если количество фреймов отличается на вашей странице и странице, которые загружают вас в фрейм. Не делайте фрейм на своей странице, чтобы иметь 100% гарантию результата этого кода

Вова Попов
источник
Не очень элегантное решение для определения того, находится ли окно внутри iframe или нет, и нет никаких гарантий, что вы также сможете читать из parent.frames.
Перси
-4

Написать этот JavaScript на каждой странице

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

Затем он автоматически перенаправляет на домашнюю страницу.

Шибинь
источник
4
Ваше перенаправление будет работать, когда вы не если кадр. Поэтому я не смогу перемещаться по вашему сайту. Во-вторых, существуют способы предотвращения перенаправления с родительской страницы.
Мариус Балчитис