iframe ベースの Facebook アプリを作成しています。今、同じ html ページを使用して、通常の Web サイトと Facebook 内のキャンバス ページをレンダリングしたいと考えています。ページが iframe 内に読み込まれたのか、ブラウザーに直接読み込まれたのかを判断できるかどうかを知りたいですか?
19 に答える
ブラウザーは、同一オリジン ポリシーwindow.topにより へのアクセスをブロックできます。IE のバグも発生します。作業コードは次のとおりです。
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
topとselfはどちらもwindow( とともにparent) オブジェクトであるため、ウィンドウが一番上のウィンドウであるかどうかがわかります。
親と同じオリジンの iframe 内にある場合、window.frameElementメソッドは、ウィンドウが埋め込まれている要素 (iframeまたは など) を返します。objectそれ以外の場合、最上位のコンテキストでブラウジングする場合、または親フレームと子フレームのオリジンが異なる場合は、 と評価されnullます。
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
これは、最新のすべてのブラウザーで基本的なサポートを備えたHTML 標準です。
RoBorgは正しいですが、サイドノートを追加したいと思います。
IE7 / IE8では、Microsoftがブラウザーにタブを追加したときに、注意しないとJSに大混乱を引き起こす1つの問題が発生しました。
このページレイアウトを想像してみてください。
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が返されるため、この奇妙な状態をテストできます。
この例が古い Web ブラウザーでどのように機能するかはわかりませんが、IE、Firefox、および Chrome では問題なく使用できます。
var iFrameDetection = (window === window.parent) ? false : true;
受け入れられた回答は、Firefox 6.0 拡張機能 (Addon-SDK 1.0) のコンテンツ スクリプト内では機能しませんでした: Firefox は、トップレベル ウィンドウとすべての iframe のそれぞれでコンテンツ スクリプトを実行します。
コンテンツ スクリプト内で、次の結果が得られます。
(window !== window.top) : false
(window.self !== window.top) : true
この出力の奇妙な点は、コードが iframe 内で実行されるか最上位ウィンドウ内で実行されるかに関係なく、常に同じであることです。
一方、Google Chrome はコンテンツ スクリプトをトップレベル ウィンドウ内で 1 回だけ実行するように見えるため、上記はまったく機能しません。
両方のブラウザのコンテンツスクリプトで最終的に機能したのは次のとおりです。
console.log(window.frames.length + ':' + parent.frames.length);
iframe がない場合、これ0:0は 1 つのフレームを含むトップレベル ウィンドウに印刷1:1され、ドキュメントの唯一の iframe に印刷され0:1ます。
これにより、拡張機能は両方のブラウザーで iframe が存在するかどうかを判断し、さらに Firefox で iframe の 1 つ内で実行されているかどうかを判断できます。
私はこれを使用しています:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
これを実現する方法の例として、この 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;
}
Facebook アプリのコンテキストで要求しているため、最初の要求が行われたときにサーバーでこれを検出することを検討することをお勧めします。Facebook は、iframe から呼び出された場合、fb_sig_user キーを含む一連のクエリ文字列データを渡します。
いずれにせよアプリでこのデータを確認して使用する必要がある可能性があるため、レンダリングする適切なコンテキストを決定するために使用してください。
@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 ページのタブまたはキャンバスからアプリにアクセスしているかどうかを知りたい場合は、Signed Request を確認してください。取得できない場合は、ユーザーが 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/
if (window.frames.length != parent.frames.length) { page loaded in iframe }
ただし、ページと iframe にロードしているページで iframe の数が異なる場合に限ります。このコードの結果を 100% 保証するには、ページに iframe を作成しないでください
この JavaScript を各ページに記述します
if (self == top)
{ window.location = "Home.aspx"; }
その後、自動的にホームページにリダイレクトされます。