IFrameオブジェクトは、その取得方法によって異なるオブジェクトとなります。
| 取得方法 | 返されるオブジェクト |
|---|---|
| Window.frames[] | Window |
| Name (name属性の値) | |
| Document.getElementById( ID ) | IFrame |
| Document.getElementsByTagName( 'iframe' ) | |
| ID (id属性の値で宣言されたグローバル変数) ※1 [非標準] |
IFrameオブジェクトとして取得しないと、このページで解説する方法を適用できません。もしWindowオブジェクトが必要ならば、IFrameオブジェクトからでも
IFrame.contentWindowIFrame.contentDocument.defaultView (IE9より前は非対応
defaultView property (Internet Explorer) | MSDN
互換性 - document.defaultView - Web API インターフェイス | MDN | MSDN)
のいずれかの方法で取得できます。
| 型 | プロパティ | 内容 |
|---|---|---|
| Document | contentDocument | iframe要素のHTMLドキュメント |
| Window | contentWindow | iframe要素のウィンドウ |
| String | src | iframe要素のsrc属性の内容 |
| String | width | |
| String | height | |
| String | frameBorder |
IE8より前はcontentDocumentをサポートしないため、それにも対応させるためにはIFrame.contentWindow.documentを使用します。よってクロスブラウザとするには、
var contentDocument = IFrame.contentDocument || IFrame.contentWindow.document;
とします。contentDocument property (Internet Explorer) | MSDN
IFrameのbody要素へは、Iframe.contentDocument.bodyでアクセスできます。
contentDocumentは、IFrameオブジェクトがドキュメントに追加されるまではnullを返します。
var iframe = document.createElement( 'iframe' );
console.log('1:' + iframe.contentDocument);
iframe.src='/';
console.log('2:' + iframe.contentDocument);
iframe.onload = function(e)
{
console.log('3:' + iframe.contentDocument);
}
document.body.appendChild(iframe);
console.log('4:' + iframe.contentDocument);
// 1:null
// 2:null
// 4:[object HTMLDocument]
// 3:[object HTMLDocument]
contentDocumentへのアクセスには同一起源ポリシーによる制約を受けます。そのためiframeにより埋め込まれた外部サイトの内容を、スクリプトで改変するようなことはできません。
外部サイトのcontentDocumentを読み込もうとすると、ブラウザごとに下表のような反応を示します。
| ブラウザ | |
|---|---|
| Internet Explorer 11 | 例外が発生する。
|
| Chrome | 例外が発生する。
|
| Firefox |
|
この制約はフレーム内のウィンドウに対しても同様で、srcプロパティで外部サイトを指定した場合、その外部サイトからwindow.parentを介してアクセスされることはありません。
このプロパティを変更すると、フレーム内に新しいドキュメントが読み込まれます。ただしX-Frame-Optionsヘッダが出力されるページは表示できません。
srcプロパティはただの文字列であるStringオブジェクトであり、Locationオブジェクトではありません。
onloadイベントは、src属性により指定されたリソースの読み込み完了時に発生します。しかしHTMLで記述したiframe要素が読み込まれるタイミングは予測できないため、HTMLの属性としてハンドラを記述するのが確実です。
<iframe onload="alert( '完了' )" src="http://example.com"></iframe>
なお、ハンドラ内でのthisキーワードはWindowオブジェクトです。
iframeの内容を動的に読み込み、その読み込み完了時に何らかの処理をする場合を考えます。このときiframeのonloadイベントに設定するだけでは、ページの読み込み時にもイベントが発生してしまいます。よってWindow.onloadイベント発生時に、IFrame.onloadイベントを設定するようにします。
<iframe name="frameName" id="frameId"></iframe>
<form target="frameName" action="sample.htm" method="get">
<input type="submit" value="読み込む" />
</form>
<script type="text/javascript">
window.onload = function()
{
var iframe = document.getElementById( 'frameId' );
iframe.onload = function()
{
alert( '読み込み完了' );
}
}
</script>
iframeではerrorイベントを捕捉できません。javascript - How can I handle errors in loading an iframe? - Stack Overflow
iframe要素内から、他のiframe要素内へアクセスする場合を考えます。それらの要素は、
<iframe id="frame1" src="frame1.htm"></iframe>
<iframe id="frame2" src="frame2.htm"></iframe>
<div>
parent Window
<input type="text" id="text" readonly="readonly" />
</div>
のように配置されているものとします。また読み込まれるフレーム内には、
frame1<input type="text" id="text" value="" />
の記述があるものとします。このとき1つ目のiframe内で、
// 親ウィンドウのドキュメントを取得する var parentDocument = window.parent.document; // 親ウィンドウのドキュメント内で、他のiframeをIDから取得する var frame2 = parentDocument.getElementById( 'frame2' ); // 他のiframeのドキュメントを取得する var otherDocument = frame2.contentWindow.document; otherDocument.getElementById( 'text' ).value = 'TEST';
とすることで、2つ目のiframe内の要素にアクセスできます。
frame1、frame2のテキストボックスへの入力を、それぞれ他方へコピーします。また同時に、親となるウィンドウのテキストボックスへもコピーします。
親ウィンドウとiframe要素内のウィンドウが同一起源ポリシーを満足していないと、このように親ウィンドウを介してアクセスすることはできません。
フレーム内のコンテンツがフレームより大きいとスクロールしなければ閲覧できず、一方でフレームより小さいと無駄な余白が生じます。これをフレーム内のコンテンツのサイズに合わせて、フレーム自身のサイズを調整する方法を考えます。
コンテンツのサイズはリソースの読み込み後に確定するため、onloadイベントで処理します。
iframe.onload = function()
{
var contentDocument = this.contentDocument || this.contentWindow.document;
this.width = contentDocument.documentElement.scrollWidth;
this.height = contentDocument.documentElement.scrollHeight;
}
なおこの方法はcontentDocumentへのアクセスを伴うため、外部サイトのドキュメントに対しては適用できません。