別のフレームに説明窓を表示


■Q&A

■Question

フレーム 2002.x.x No.xxxx

質問文失念(ログの取り忘れ)

左フレームのリンクにマウスを乗せたら右フレームにメッセージを出したい、とかそんなような質問

■Answer

Re:フレーム ぴろあき 2002.12.31.Tue No.1060

可能ですが、ちょっと難しいです。
マウスが乗ると表示される説明窓は、右フレームのファイルに書きます。
そして、それを実行するJavaScriptは左フレームのファイルに書きます。
つまり両方が揃って初めて動作するわけで、たとえば右フレームが他のページへ移っていたりすると、スクリプトエラーになります。

対処としては、右フレームに表示されるページのすべてに、説明窓の内容を埋めこむ方法があります。でも修正が恐ろしく大変で、とても現実的じゃないです。
外部スクリプトファイルを使えば回避できるにはできますが、その呼び出しタグをすべてのファイルに入れる必要があります。

古いブラウザを切り捨ててもいいなら、高度な処理を使ってこれらを一気に解決することもできます。
Step.88とはなんの関係もない別物のスクリプトになりますが…。

http://faq.creasus.net/02/1231/


■実行結果

左フレームのリンクにマウスを乗せてみましょう。対応ブラウザであれば右フレームに説明窓が出ます。(「ページ4」はわざと出ないようにしてある例です)


■スクリプトの概要

左フレームのリンクにマウスを乗せると、右フレームに説明窓が表示されます。
JavaScriptがOFFでも、title 属性に対応しているブラウザならチップで同じ内容が出ます。

スクリプトはすべてフレームセットへ集約しているので、左右のフレームともに、ファイルに手を加える必要はありません。フレームセットにスクリプトを入れるだけで動きます。
リンクに onmouseover とか onmouseout といったイベントハンドラも入れる必要はなく、ソースがとても綺麗。素人が見たら「どこにJavaScriptが?」です。

表示するメッセージは、左フレームの <a> にある title 属性から取得します。この属性がないリンクでは説明窓は表示されません。

フレームセットが実行するスクリプトの流れとしては、

  1. 左フレームの読込が終わったらリンクを全部取得して、title 属性のあるやつにだけイベントを設置していく。
  2. 右フレームに説明窓表示用のタグを作って挿入する。下準備が終わったらフラグを立てる。
  3. 左フレームのリンク(イベント付き)にマウスが乗った時、フラグが立ってたら説明窓のタグにテキストを流しこんで表示する。表示するテキストは title 属性のものを使う。
  4. マウスが離れたら説明窓を消す。
  5. クリックしたら、リンクに target が有効になっているか調べる。(<base> にも対応)
  6. 有効になっていたら、リンク先が右フレームになっているか調べる。
  7. 右フレームになっていたら、フラグを寝かせて2.からやり直す。(右フレームが別ページになるので、初期化して説明窓表示用のタグを作り直さないといけない)

…という感じです。スクリプトは結構でかいですが、処理効率は高いです。さらにスクリプトがフレームセットにあるので、読込も一度だけ。


■このスクリプトの対応ブラウザ

IE5.5以降・Netscape6以降
問題なく動きます。
IE4・IE5.0・Opera
動きませんが、リンクへ乗った時に同じ説明がチップで表示されます。
<frame onload=""> にさえ対応していれば、Opera7も対応できるんだけど。
IE3以下・Netscape4以下
動かないしチップも出ません。エラーも起きません。説明文に重要なことは書かないようにしましょう。

■フレームセットのソース

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>リンクに乗ったら別のフレームに説明窓を表示する</title>

<script type="text/javascript"><!--

var msgTop  = 150; //説明窓の画面上端からの位置
var msgLeft = 120; //説明窓の画面左端からの位置

//右フレームの初期化
function msgFrameInit(frmObj) {

  //説明窓のスタイルシート指定
  //左側に属性名、右側にその値を記入する
  //不要な行は消してもOK
  //border-left-style などは borderLeftStyle のように記述(ハイフンを消して直後を大文字に)
  var msgStyles = new Array(
    "width"           , "50%",         //説明窓の横幅
    "height"          , "",            //説明窓の縦幅(""のままにしておくと自動設定)
    "padding"         , "10px",        //枠内余白
    "borderWidth"     , "3px",         //枠線幅
    "borderStyle"     , "solid",       //枠線スタイル
    "borderColor"     , "#ada",        //枠線色
    "backgroundColor" , "#cfc",        //背景色
    "backgroundImage" , "url(bg.gif)", //背景画像(カッコ内に画像のURLを指定)
    "color"           , "#a00",        //文字色
    "fontSize"        , "100%",        //文字サイズ
    "fontWeight"      , "normal",      //太字なら"bold"
    "fontStyle"       , "normal",      //斜体なら"italic"
    "lineHeight"      , "100%",        //行間
    "textAlign"       , "center",      //テキストの寄せ方
    //以下は必須項目
    "position"        , "absolute", //変更不可
    "visibility"      , "hidden"    //変更不可
  );

  //右フレームのnameを取得
  msgFrameName = frmObj.name;

  //右フレームのドキュメントオブジェクトを取得
  var rfd = self.frames[msgFrameName].document;

  //右フレームに<div>を新しく作る
  var tag = rfd.createElement("div");

  //<div>にスタイルシートを適用
  for (var i = 0; i < msgStyles.length; i++)
    tag.style[msgStyles[i]] = msgStyles[++i];

  //説明文用のテキストノードを作成して<div>に挿入
  tag.appendChild(rfd.createTextNode(""));

  //できあがった<div>を右フレームへ挿入
  rfd.body.appendChild(tag);

  //下準備完了
  div = tag;
}


//左フレームの初期化
function menuFrameInit(frmObj) {
  //左フレームにあるリンクをすべて調べ、title属性のあるリンクに説明窓イベントを仕こむ
  for (var i = 0, a, as = self.frames[frmObj.name].document.links, l = as.length;i < l; i++) {
    a = as[i];
    if (a.title) {
      a.onclick = clickEvent;
      a.onmouseover = overEvent;
      a.onmouseout  = outEvent;
    }
  }

  //左フレームの<base>を取得、target属性があれば格納する
  var tags = self.frames[frmObj.name].document.getElementsByTagName("base");
  for (i = 0; i < tags.length; i++) {
    if (tags[i] && tags[i].target)
      base = tags[i].target;
  }
}


//左フレームのリンクがクリックされた時(説明窓フレームを初期化)
function clickEvent()  {

  //下準備が終わっていてリンクにターゲットが指定されているか
  if (div && (this.target || base)) {

    //ターゲットが右フレームになっているか
    if (this.target == msgFrameName || (!this.target && base == msgFrameName))

      //下準備で作った<div>が消失するので中身を消す
      div = null;
  }

  //trueを返してリンクを作動させる(リンク後の<frame onload="">で再び<div>が作られる)
  return true;
}


//マウスが離れたら説明窓を非表示にする
function outEvent() {
  if (div)
    div.style.visibility = "hidden";
}


//マウスが乗ったら右フレームの説明窓を表示する
function overEvent()   {

  //下準備が終わってなかったらなにもしない
  if (!div)
    return;

  //クラスを使って右フレームの現在のスクロール位置を取得
  var scrollPos = new ScrollPos(self.frames[msgFrameName]);

  //説明窓の表示位置をスクロールに合わせる
  if (document.all) { //IE用
    div.style.pixelLeft = msgLeft + scrollPos.x;
    div.style.pixelTop  = msgTop + scrollPos.y;

  } else { //Netscape用
    div.style.left = msgLeft + scrollPos.x + "px";
    div.style.top  = msgTop + scrollPos.y + "px";
  }

  //msgFrameInit()で作った<div>のテキストノードをリンクのtitle属性の値にする
  div.firstChild.nodeValue = this.title;

  //説明窓の表示状態を"表示"にする
  div.style.visibility = "visible";
}


//右フレームのスクロール位置を取得するクラス
function ScrollPos(win) {
  var d = win.document;
  if (d.selection) { //IE用
    if (d.compatMode == "CSS1Compat") { //IE6用
      this.x = d.documentElement.scrollLeft;
      this.y = d.documentElement.scrollTop;
    } else { //IE5.5-用
      this.x = d.body.scrollLeft;
      this.y = d.body.scrollTop;
    }
  } else { //Netscape用
    this.x = win.pageXOffset;
    this.y = win.pageYOffset;
  }
  return this;
}


//この関数からスタート
//各フレームがロードされたら実行
function init(frmObj, side) {
  //簡易ブラウザ判別
  if (window.opera)
    return; //Operaには非対応
  else if (!window.createPopup || !document.selection) //IE5.5以降ではない
    if (!navigator.product || navigator.product != "Gecko" || navigator.appName != "Netscape") //Netscape6以降でもない
      return; //なにもしないで終了

  if (side)
    msgFrameInit(frmObj); //右フレームを初期化
  else
    menuFrameInit(frmObj); //左フレームを初期化
}


var base; //左フレームに<base target="">があったら格納
var div;  //右フレームに挿入する<div>のオブジェクト(フラグ兼用)
var msgFrameName; //右フレームのname(自動取得)
//-->
</script>
</head>



<frameset cols="140,*">

  <!--メニュー側のフレームに onload="init(this)" を入れる-->
  <frame src="left.html" name="menu" onload="init(this)">

  <!--説明窓を表示するフレーム onload="init(this,1)" を入れる-->
  <frame src="right.html" name="main" onload="init(this,1)">

  <!--name 属性は必須だけど、自動取得するので名前はなんでもよし-->

  <noframes>
    <body>
      <h1>このページはフレームを使用しています。</h1>
      <p><a href="right.html">スクリプトの解説ページへ</a></p>
    </body>
  </noframes>
</frameset>

</html>


■初期設定

■説明窓の表示位置

var msgTop  = 150; //説明窓の画面上端からの位置
var msgLeft = 120; //説明窓の画面左端からの位置

■説明窓のスタイルシート

//説明窓のスタイルシート指定
//左側に属性名、右側にその値を記入する
//不要な行は消してもOK
//border-left-style などは borderLeftStyle のように記述(ハイフンを消して直後を大文字に)

var msgStyles = new Array(
  "width"           , "50%",         //説明窓の横幅
  "height"          , "",            //説明窓の縦幅(""のままにしておくと自動設定)
  "padding"         , "10px",        //枠内余白
  "borderWidth"     , "3px",         //枠線幅
  "borderStyle"     , "solid",       //枠線スタイル
  "borderColor"     , "#ada",        //枠線色
  "backgroundColor" , "#cfc",        //背景色
  "backgroundImage" , "url(bg.gif)", //背景画像(カッコ内に画像のURLを指定)
  "color"           , "#a00",        //文字色
  "fontSize"        , "100%",        //文字サイズ
  "fontWeight"      , "normal",      //太字なら"bold"
  "fontStyle"       , "normal",      //斜体なら"italic"
  "lineHeight"      , "100%",        //行間
  "textAlign"       , "center",      //テキストの寄せ方
  //以下は必須項目
  "position"        , "absolute", //変更不可
  "visibility"      , "hidden"    //変更不可
);

■左フレームのリンク

<a href="../page.html" target="main" title="説明">


■追記:2003.8.16.Sat

このページにあったスクリプトは「たまに原因不明のエラーが起きる」という少々厄介なバグがあったため、最初から組み直しています。


■ このページについて

このページは、質疑応答掲示板で使用した回答の残骸です。検索エンジンに捕まったりリサイクルしたりすることもあろうので、なんとなく残してあります。広く公開しているページではありませんが、第3者の閲覧を禁止するものでもありません。
恒久的なURIは保証できないものの、よほどのことがない限り削除されることはないでしょう。心配性な人はページの保存をおすすめします。(IEは mht 形式でないと保存できないかも)