iframeの表示で#を使った時のバグは直らなそう

 2021年に自分のサイトを作った際に気づいて、いまだに放置されているバグがあって、それをGeminiに尋ねたら、バグではなく正常な動作だということで、直りそうにないことが分かった。
 それはiframeタグのsrcに#を使ってiframe内に表示されるページの位置を決めようとしたときに生じるバグで、表示された親ページの一番上がページの先頭ではなくiframeタグの位置になってしまう。Firefoxでは起こらないのだけど、Edge、Chrome、Operaなど、Chromiumのブラウザで起こるらしい。当時は、Microsoft コミュニティで質問したのだけど、ふと思いついてGeminiに尋ねてみた。

iframeとハッシュ#の関係に関する質問です。次のコードでtest3-0.htmlを開くと、ページの先頭ではなく、iframeタグのある所が一番上に表示されてしまいます。原因は分かりますか?test3-0.htmlとiframeで表示するtest00-0.htmlのコードは次の通りです。

test3-0.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>バグ?</title>
</head>
<body>
<div style="height:200px;">
バナー、ヘッダなど。
</div>
<div style="height:250px;">
<h4>↓↓↓iframe↓↓↓</h4>
<iframe src="test00-0.html#y500" name="test00" id="test00" title="test00"></iframe>
</div>
<div style="height:1000px;">
<a href="test3-0.html">このページへのリンク</a><br>
</div>
</body>
</html>

test00-0.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>iframe内</title>
</head>
<body>
<p style="height:500px;">iframe内</p>
<p style="height:500px;" id="y500">
<span>iframe内500px</span><br>
</p>
</body>
</html>

ご提示いただいた現象は、ブラウザの**「フラグメント識別子(#ハッシュ)への自動スクロール機能」**が原因です。

バグではなく、ブラウザが「ユーザーは特定の場所(この場合は#y500)を見たいのだ」と判断して、その要素を画面内に収めようとする仕様(親切心)による挙動です。


原因の詳細

なぜ親ページまで動いてしまうのか、その論理的なステップは以下の通りです。

  1. 子画面(iframe)内の遷移: test00-0.html#y500 が読み込まれると、ブラウザは iframe 内で #y500 の位置までスクロールします。
  2. ブラウザの視覚的優先度: 多くのブラウザ(特に Chrome や Edge など)は、URL にハッシュが含まれている場合、**「そのターゲット要素がユーザーの目に見える位置(ビューポート内)にあること」**を優先します。
  3. 親ページへの波及: iframe 内でターゲット位置までスクロールした結果、そのターゲットが「親ページから見て画面外(または下の方)」にある場合、ブラウザは親ページ側も自動でスクロールさせ、iframe 自体を画面の上部に持ってこようとします。

つまり、**「iframeの中の特定の場所を表示したいなら、iframeそのものも画面の一番見えやすい場所に移動させよう」**というブラウザの自動調整が働いています。


解決策

この挙動を回避し、親ページの先頭を表示したままにするには、いくつかのアプローチがあります。

1. JavaScriptで後からスクロールさせる(推奨)

src 属性に最初からハッシュを書かず、iframe の読み込み完了後に JavaScript でスクロールを実行します。これにより、ブラウザの「初期ロード時の自動スクロール機能」を回避できます。

test3-0.html の修正案:

HTML

<iframe src="test00-0.html" name="test00" id="test00" title="test00"></iframe>

<script>
  const frame = document.getElementById('test00');
  frame.onload = function() {
    // iframe内の特定のIDへ移動させる
    frame.contentWindow.location.replace("test00-0.html#y500");
    // 移動後、即座に親ページのトップへ戻す(念のため)
    window.scrollTo(0, 0);
  };
</script>

2. 親ページ側のスクロール位置を強制固定する

ページ読み込み完了時に、強制的に親ページのトップ (0, 0) へ戻す処理を入れます。

JavaScript

window.onload = function() {
  setTimeout(function() {
    window.scrollTo(0, 0);
  }, 0); // 実行タイミングをずらすのがコツです
};

3. ハッシュを使わずに要素の位置を調整する

もし特定の場所を見せたいだけなら、iframe 内の test00-0.html 側で、読み込み時に Element.scrollIntoView()block: "nearest" などのオプション付きで実行する方法もありますが、これもブラウザによっては親を動かしてしまいます。


まとめ

  • 原因: iframesrc にハッシュを指定すると、ブラウザがその場所を画面内に収めるために親ページごとスクロールさせるため。
  • 対策: src にはハッシュを含めず、ページ読み込みが終わった後に JavaScript で制御するのが最も確実です。

他にご不明な点や、特定のブラウザ環境での制約などはありますか?

 対処法は既に見つけているので、Javascriptを使った解決策はいらないので、この仕様の問題点を指摘してチャットを続けた後、次のように尋ねた。

子ページが別のサーバーにある場合は、お節介なスクロールが生じないようです。その理由も合わせて説明できますか?

別サーバー(クロスドメイン)の場合にお節介なスクロールが発生しないのは、ブラウザの**「セキュリティ(プライバシー保護)」「サンドボックス(隔離)」**という考え方が強く働くためです。

結論から言うと、**「外部サイトの都合で、勝手にメイン(親)ページの表示位置を操作させない」**というブラウザの防御壁が機能しているからです。

論理的な理由は主に3つあります。


1. ユーザー体験の乗っ取り(スクロールジャック)防止

同じサーバー(同一ドメイン)の場合、ブラウザは「親ページも子ページも同じ制約者が管理している」と判断します。そのため、子ページのハッシュ移動に合わせて親ページを動かすことを「サイト制作者の意図した親切な挙動」として許可します。

しかし、別サーバーの場合は以下のリスクが生じます。

  • 悪意のある広告や外部コンテンツ: iframe内の外部サイトが、勝手に親ページをガタガタ動かしたり、特定の広告位置まで強制スクロールさせたりすることを防ぐ必要があります。
  • 不意の移動の禁止: ユーザーが読んでいる親記事を、無関係な外部iframeの読み込み完了のタイミングで勝手に飛ばされるのを防ぐため、ブラウザが「親への波及」をブロックします。

2. サイドチャネル攻撃(情報漏洩)への対策

もし「子ページがスクロールした時に親ページも動く」という挙動を許してしまうと、親ページ側が自身のスクロール量を監視することで、「外部サイト(子ページ)のどこが表示されているか」という情報を間接的に知ることができてしまいます。

これは、例えば「ログインしている時だけ表示される特定のID要素」がiframe内にある場合、スクロールが発生したかどうかでユーザーのログイン状態を推測できてしまうなどのセキュリティリスク(プライバシー侵害)に繋がります。そのため、異なるオリジン間ではスクロールの影響を遮断します。

3. ブラウザの「祖先スクロール制限」

現代のブラウザ(特にChromeなどのChromium系)には、**「クロスオリジンのサブフレームは、祖先(親)フレームをスクロールさせてはならない」**という明確なルール(Ancestor Scroll Restriction)が組み込まれています。

  • 同一サーバー: 親と子は「運命共同体」として扱われ、スクロールが連動する。
  • 別サーバー: 子は「隔離された箱」として扱われ、箱の中身は動いても、箱の外(親ページ)には一切干渉できない。

挙動の比較まとめ

構成ブラウザの判断親ページの挙動
同一サーバー信頼関係あり(同一サイト)ハッシュ位置が見えるよう、親も自動スクロールする
別サーバー信頼関係なし(クロスドメイン)子の中身だけがスクロールし、親は動かない

補足:あえて「別サーバーっぽく」見せる方法

もし、どうしてもJSを使わず、かつ「同一サーバー内」でこの問題を解決したい場合、**サブドメイン(例: https://www.google.com/search?q=sub.example.com)**に子ページを置くだけでも、ブラウザが「別サイト」と認識してスクロールが止まる場合があります(ブラウザの厳格な設定によります)。

JavaScriptを使いたくないというご要望に沿うなら、**「別サーバー(または別ドメイン)から読み込んでいる」という現在の状態が、実はHTMLの仕様的には最も理想的な「お節介回避策」**となっていると言えます。

他にも、この「同一サーバー内での制限」について気になる点はありますか?

なるほど。親ページは先頭から表示したいが、子ページは例えば日時や曜日などによって途中から表示したい場合、このお節介な仕様は迷惑だと思います。改善される可能性はありますか?

そのお気持ち、非常によく分かります。制作者からすれば「余計な動きをしないでほしい」場面ですよね。

この「お節介な仕様」が将来的に改善(=オフにする設定が追加)される可能性と、現状の技術動向から見た論理的な展望についてお伝えします。


1. 「改善」される可能性について

結論から申し上げますと、「HTMLの標準仕様として、自動スクロールを無効化する属性(例:noscrollなど)が追加される可能性」は、現時点では極めて低いと考えられます。

理由は、ハッシュ(#)の本来の定義にあります:

  • ウェブの基本原則: URLにハッシュが含まれている場合、それは「文書内の特定の断片(Fragment)へのナビゲーション」を意味します。
  • ブラウザの責務: 「ナビゲーション」である以上、その場所をユーザーに見せるのがブラウザの最も基本的な仕事(義務)と定義されています。

もし「ハッシュがあるのに移動しない」という設定を許してしまうと、ウェブ全体の「リンクを押せばその場所に飛ぶ」という一貫性が崩れてしまうため、ブラウザベンダーは慎重な立場をとっています。

2. 実は「改善」は進んでいるが、方向が異なる

ブラウザ側も「勝手に動くのが迷惑」なケースがあることは認識しています。ただし、その対策は「設定を追加する」のではなく、**「セキュリティを厳しくして、意図しない挙動を防ぐ」**方向で進んでいます。

  • クロスオリジンの制限: 前述の通り、別サーバーからの読み込みでスクロールしないのは、まさに「お節介の防止」の結果です。
  • Scroll-to-Text Fragment: 最近のブラウザにはURLで特定の文字列にハイライトして飛ぶ機能がありますが、これも「ユーザーが直接URLを入力した時」などの特定の条件下でのみ動くよう、厳格に制限されています。

3. JavaScriptを使わずに「日時で表示を変える」論理的な代替案

ご質問の「日時や曜日によって表示位置を変えたい」という目的を、ハッシュを使わずに(=お節介を発生させずに)実現する、**「現代的で論理的な解決策」**がいくつかあります。

A. サーバーサイドで表示を切り替える(最も推奨)

JavaScriptを使いたくない、かつお節介も避けたい場合、URLの末尾を #(ハッシュ)ではなく ?(クエリパラメータ)にします。

  • URL例: test00-0.html?mode=monday
  • 仕組み:
    1. 親ページから曜日情報をパラメータで送る。
    2. サーバー(PHPやPythonなど)側で、その曜日に必要な部分だけをHTMLとして生成して返す。
  • 利点: ブラウザは「新しいページの先頭」を表示するだけなので、勝手なスクロールは一切発生しません。

B. CSSの scroll-margin を利用する(微調整)

もし「親ページが少し動くのは許容できるが、表示位置が極端すぎる」という場合は、子ページ側でターゲット要素に余白を持たせることができます。

CSS

/* 子ページ(test00-0.html)のCSS */
#y500 {
  scroll-margin-top: 100vh; /* 画面の高さ分だけ余裕を持たせる */
}

これを使うと、ブラウザは「ターゲットを一番上に持っていこう」としつつも、指定した余白の分だけ位置をずらすため、親ページの跳ね上がりを抑制できる場合があります(ただし、完全な制御は難しいです)。


まとめ

ブラウザの自動スクロールは、「情報のアクセシビリティ(到達性)」を保証するための根幹的な仕様であるため、設定一つでオフにできるようになる可能性は低いでしょう。

もし、今後この挙動に悩まされずに「動的な表示」を行いたい場合は、**「ハッシュ(ナビゲーション)」に頼るのではなく、「サーバーサイドでのHTML出し分け」や「URLパラメータによる構成の変化」**にシフトするのが、最も堅実で「お節介」を受けない方法となります。

今回のケースで、サーバーサイド(PHP等)での制御を選択肢に入れることは可能でしょうか?もし可能であれば、その具体的な実装イメージをお伝えすることもできます。

 改善される見込みはないということで、次のように複数のiframeがある場合について質問してみた。

test3-0.htmlに次のように複数のiframeがあった場合は、どのように表示されますか?

test3-0.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>バグ?</title>
</head>
<body>
<div style="height:200px;">
バナー、ヘッダなど。
</div>
<div style="height:250px;">
<h4>↓↓↓iframeA↓↓↓</h4>
<iframe src="test00-0.html#y500" name="test00a" id="test00a" title="test00a"></iframe>
</div>
<div style="height:1000px;">
<a href="test3-0.html">このページへのリンク</a><br>
</div>
<div style="height:250px;">
<h4>↓↓↓iframeB↓↓↓</h4>
<iframe src="test00-0.html#y400" name="test00b" id="test00b" title="test00b"></iframe>
</div>
</body>
</html>  

複数の iframe があり、それぞれがハッシュ(#)付きの URL を読み込んでいる場合、結論から言うと**「後から読み込み(ターゲット位置への到達)が完了した iframe」が優先され、画面がそちらへ強制的に連れて行かれます。**

つまり、このコードを実行すると、最終的には**「iframeB」がある場所まで自動スクロールした状態で止まる**可能性が非常に高いです。

何が起きるのか、論理的なステップを追って解説します。


ページ読み込み時の挙動(シミュレーション)

ブラウザは HTML を上から順に解析していきますが、ハッシュによるスクロールは各 iframe の中身が読み込まれたタイミングで発生します。

  1. 初期状態: test3-0.html を開いた瞬間、まずページの最上部(バナー、ヘッダのあたり)が表示されます。
  2. iframeAの読み込み: iframeA(#y500)の読み込みが完了します。ブラウザは「よし、#y500 を表示するぞ」と判断し、iframeAが画面の上の方に来るように親ページをスクロールさせます。
  3. iframeBの読み込み: その後、少し遅れて iframeB(#y400)の読み込みが完了します。ブラウザは「次は #y400 を見せなきゃ」と考え、さらに下にある iframeBが画面内に入るまで、親ページをさらに深く(約1450px以上下へ)スクロールさせます。

結果:どのような見た目になるか

ユーザーの目には、ページが開いた瞬間にガタガタッと2回(あるいは一気に)画面が下に飛び、最終的に iframeBが一番上に表示されているように見えます。


この挙動の「困るポイント」

この「後出しジャンケン」のような挙動には、以下のような論理的な矛盾や不便さが生じます。

  • ページ先頭のコンテンツが無視される: 制作者が一番見てほしいはずの「バナー」や「iframeA」を通り過ぎて、勝手に一番下の iframeBまで飛ばされてしまいます。
  • 通信速度によるランダム性: もし iframeAのサーバーが重く、iframeBの方が先に読み込み終わった場合は、逆に iframeAが最後にスクロールを発生させるため、iframeAの位置で止まることもあります。**「どっちが表示されるか運任せ」**という非常に不安定なページになってしまいます。

複数の iframe を扱う際の論理的解決策

「JavaScript を使わない」という制約がある中で、複数の iframe を並べつつ意図した場所(例えばページ先頭)を表示したい場合、以下の構成を検討せざるを得ません。

  1. ハッシュを一切使わない:そもそも src に #y500 や #y400 を含めないのが最も確実です。
  2. 表示したい部分だけの別ページを作る:test00-0.html の 500px 目以降だけを記述した「専用の小さな HTML ファイル」を作成し、それを iframe で読み込みます。これならハッシュを使わずに済み、親ページが動くこともありません。

まとめ

複数の iframe にハッシュを指定すると、ブラウザによる「お節介なスクロール」が iframe の数だけ連鎖的に発生し、最終的にどの位置で止まるか制御不能になるというのが、このコードの論理的な帰結です。

「やはり、ハッシュを iframe で使うのは避けたほうがよさそうだ」という印象を持たれましたでしょうか?もし「どうしてもこの構造で、特定の順番で表示したい」といった特別な目的があれば、また別のハックを探してみますが、いかがいたしましょう。

 ハッシュをiframeで使うな!という主旨のことを言い出した。最後に次のように質問した。

ブラウザの仕様が変わるのが一番良いと思います。

本当におっしゃる通りです。制作者の意図をこれほど無視して画面が勝手に動いてしまう仕様は、現代の複雑なウェブレイアウトにおいては「時代遅れ」と感じる部分が多々あります。

なぜブラウザの仕様が変わるのが一番いいのか、そしてなぜそれが難しいのか、論理的に整理すると見えてくるものがあります。


1. 理想的な仕様の形

ユーザーが仰るように、HTMLに以下のような属性が一つあれば、すべての問題は解決します。

HTML

<iframe src="test00-0.html#y500" capture-scroll="false"></iframe>

このように、**「ハッシュによる移動は iframe 内で完結させ、親要素(祖先)へのスクロール伝播を禁止する」**というスイッチがあれば、制作者の意図とブラウザの親切心が共存できます。

2. 仕様が変わらない「論理的な壁」

しかし、ブラウザの仕様を策定する団体(W3CやWHATWG)が慎重なのは、**「URLの信頼性」**を重視しているからです。

  • URLの契約: 「URL末尾の #id は、その情報をユーザーに見せるという約束である」という大原則があります。
  • アクセシビリティの懸念: もし「親をスクロールさせない」属性を許可すると、悪意のあるサイト(または単なる設計ミス)によって、**「URLは特定の場所を指しているのに、実際には画面外に隠れていて見えない」**という状況が生まれます。これはアクセシビリティ(情報の到達性)の観点から「ウェブの破壊」とみなされる傾向があります。

3. 現状の「妥協点」としての進化

ブラウザ側は「属性を増やす」のではなく、**「別サーバーなら動かさない」**というセキュリティ的な境界線でこの問題を解決しようとしています。

  • 現在の思想: 「同じサーバーなら親と子は一体だから動かす。別サーバーなら他人だから動かさない」
  • 不都合な点: 今回のように、同じサーバー内で整然とコンテンツを管理したい制作者にとっては、この「一体扱い」が逆に仇となってしまっています。

結論

「お節介をオフにするスイッチ」がない現状は、開発者にとっては非常に不便ですが、ブラウザ側は**「ユーザーを迷子にさせない(目的のハッシュを必ず見せる)」**という古いルールを頑なに守り続けています。

もし今後、scroll-behavior などのCSSプロパティに「親への伝播を止める」オプションが追加されれば、ようやくこの「残念な仕様」から解放される日が来るかもしれません。

今回の件で、他にも「こうなればいいのに」と思う挙動や、特定の構成で困っていることはありますか?もしよろしければ、他に試してみたい構成などがあればお聞かせください。

 動作を確認するためのデモ:test3-0.html

未分類
管理人のマストドンアカウントへのリンクなど

コメント

タイトルとURLをコピーしました