Как определить, загружается ли веб-страница внутри iframe или непосредственно в окне браузера?
Я пишу приложение на основе iframe для Facebook. Теперь я хочу использовать ту же HTML-страницу для рендеринга обычного веб-сайта, а также страницы холста в Facebook. Я хочу знать, могу ли я определить, была ли страница загружена внутри iframe или непосредственно в браузере?
19 ответов
Браузеры могут заблокировать доступ к window.top
из-за той же политики происхождения. IE ошибки тоже имеют место. Вот рабочий код:
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
top
а также self
оба window
объекты (наряду с parent
), поэтому вы видите, является ли ваше окно верхним окном.
window.frameElement
метод возвращает элемент (например, iframe
или же object
) в которое встроено окно, или null
если просмотр в контексте верхнего уровня:
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
Это стандарт HTML с базовой поддержкой во всех современных браузерах.
if ( window.location !== window.parent.location )
{
// The page is in an iframe
}
else
{
// The page is not in an iframe
}
Я не уверен, как этот пример работает для старых веб-браузеров, но я использую это для IE, Firefox и Chrome без и выпускать:
var iFrameDetection = (window === window.parent) ? false : true;
Роборг прав, но я хотел добавить примечание.
В IE7/IE8, когда Microsoft добавила вкладки в свой браузер, они нарушили одну вещь, которая вызовет хаос в вашем JS, если вы не будете осторожны.
Представьте себе этот макет страницы:
MainPage.html
IframedPage1.html (named "foo")
IframedPage2.html (named "bar")
IframedPage3.html (named "baz")
Теперь во фрейме "baz" вы нажимаете на ссылку (нет цели, загружается в фрейм "baz"), все работает нормально.
Если загружаемая страница, давайте назовем ее special.html, использует JS, чтобы проверить, есть ли у "нее" родительский фрейм с именем "bar", она вернет true (ожидается).
Теперь допустим, что страница 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, и, таким образом, вы можете проверить это странное условие.
Принятый ответ не работал для меня в скрипте контента расширения 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);
Без фреймов это печатает 0:0
в окне верхнего уровня, содержащем один кадр, он печатает 1:1
и в единственном фрейме документа он печатает 0:1
,
Это позволяет моему расширению определять в обоих браузерах, присутствуют ли какие-либо iframe, и дополнительно в Firefox, если оно запускается внутри одного из iframe.
Я использую это:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
Лучший на данный момент устаревший скрипт разрыва фрейма браузера
Другие решения не работали для меня. Этот работает во всех браузерах:
Один из способов защиты от кликджекинга состоит в том, чтобы на каждой странице был добавлен сценарий "разрыва фрейма", который не следует создавать в рамке. Следующая методология предотвратит создание веб-страницы даже в устаревших браузерах, которые не поддерживают 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.
Я на самом деле проверял window.parent, и он работал для меня, но в последнее время window является циклическим объектом и всегда имеет родительский ключ, iframe или нет iframe.
Поскольку комментарии предлагают трудно сравнивать с работами window.parent. Не уверен, сработает ли это, если iframe точно такая же веб-страница, как родительский.
window === window.parent;
Используйте эту функцию 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;
}
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;
}
}
Поскольку вы спрашиваете в контексте приложения facebook, вы можете рассмотреть возможность обнаружения этого на сервере, когда сделан первоначальный запрос. Facebook будет передавать кучу данных строки запроса, включая ключ fb_sig_user, если он вызывается из iframe.
Так как вам, вероятно, все равно нужно проверить и использовать эти данные в вашем приложении, используйте их, чтобы определить соответствующий контекст для рендеринга.
Следуя тому, что сказал @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!')
}
}
Это древний кусок кода, который я использовал несколько раз:
if (parent.location.href == self.location.href) {
window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
Если вы хотите узнать, имеет ли пользователь доступ к вашему приложению на вкладке страницы 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/
Это оказалось самым простым решением для меня.
<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>
if (window.frames.length != parent.frames.length) { page loaded in iframe }
Но только если количество фреймов отличается на вашей странице и странице, которые загружают вас в фрейм. Не делайте фрейм на своей странице, чтобы иметь 100% гарантию результата этого кода
Написать этот JavaScript на каждой странице
if (self == top)
{ window.location = "Home.aspx"; }
Затем он автоматически перенаправляет на домашнюю страницу.