レイヤーの表示・非表示について教えてください。

[新着] Webテンプレートを仮オープンしました



0   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
すみません、ご存じの方がいらっしゃったら教えてください。

レイヤーの表示・非表示についてなんですが、
文字をマウスオーバーで表示、
表示されたレイヤーからマウスアウトで非表示になる
スクリプトを作成したんですが、どうしても
レイヤー内にあるにも関わらず、文字にマウスが乗っかると
レイヤーが非表示になってしまいます。
スクリプトはこうなっています↓

<head>
<script type="text/javascript">
function block(){
var doremi = document.getElementById('doremi');
doremi.style.display = "block";
}
function none(){
var doremi = document.getElementById('doremi');
doremi.style.display = "none";
}
</script>
<style type="text/css" media="screen"><!--
#doremi { background-color: #2e8b57; width: 100px; height: 100px }
--></style>
</head>


<body>
<a onMouseOver="block()">imoimoimo</a>
<div id="doremi" onMouseOut="none()">
<a>森</a>
<a>海</a>
<p>空</p>
</div>
</body>

どうしたらこのような動きを回避できるでしょうか。
よろしくお願いします。
ちなみに、レイヤー内のタグを外すという事ではありませんので。

1   名前: 匿名 : 2007/05/26(土) 18:38  ID:p5MXHd0e sub-Ds
レイヤーを最初に消す設定にしてないからでは?

2   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
レスありがとうございます。

>レイヤーを最初に消す設定にしてないからでは?
特に関係ありませんでした。

3   名前: key-child : 2007/05/26(土) 18:38  ID:8GUFlW2K sub-DR
doremi以外にマウスが移動しているから。


下記を追加すれば理由がわかる。
#doremi a, #doremi p {
 background-color: #f00;
}

4   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
key-childさんありがとうございます。

確かに#doremi a, #doremi p {background-color: #f00;}
を入れると、文字や段落の領域が分かるんですが、
ではどうしたら、その文字にマウスが乗っかっても
レイヤーdoremiが消えずにすむんでしょうか。
苦肉の策として
<div id="doremi" onMouseOut="none()">
<a onMouseOver="block()">森</a>
<a onMouseOver="block()">海</a>
<p onMouseOver="block()">空</p>
</div>
と、してみたんですが、今度はその文字から
マウスアウトするとレイヤーdoremiが消えてしまいます。

5   名前: 匿名 : 2007/05/26(土) 18:38  ID:eA8wk3La sub-Ds
アンカーテキストにマウスが乗ったらレイヤーを表示させるんじゃない
の? その表示させたところでマウスが仕事をして、マウスがレイヤー
を出ると、レイヤーが消える、と。コードはそう言ってるような気がす
るけど。

6   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
>アンカーテキストにマウスが乗ったらレイヤーを表示させるんじゃない
の?
確かにアンカー<a onMouseOver="block()">imoimoimo</a>
にマウスが乗るとレイヤーが表示されて
レイヤーdoremi↓
<div id="doremi" onMouseOut="none()">
<a>森</a>
<a>海</a>
<p>空</p>
</div>
の、領域からマウスがでると非表示になる設定なんですが
そのレイヤーdoremiの中にあるアンカーや段落に
マウスが乗ってもレイヤーが非表示になってしまうんです。
なので、レイヤーdoremiの中にあるアンカーや段落に
マウスが乗ってもレイヤーが非表示にならない為には
どうしたら良いものでしょうか?

7   名前: 匿名 : 2007/05/26(土) 18:38  ID:gEnky0MU sub-Ds
ああ、そういうことね。
イベントバブルを阻止するわけだから、cancelBubbleを使えば解決する。
ただし、IE専だが。

8   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
“イベントバブル”初めて聞きました。
色々ググッて調べてみたんですが
使い方は
event.cancelBubble=false or true;
で良いんですよね。
そしてこれは下層のイベントが
上層の同じイベントに伝播しないように
する為のもののようですが、
これら
<a>森</a>
<a>海</a>
<p>空</p>
はレイヤーdoremiの下層にあって
イベントが設定されていないので
どの様にすれば良いのか分かりません。

大変お手数なんですけど、もし具体的な
書き方を教えて頂ければ助かります。

9   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
あれから色々調べてるんですけど、
全然分かりません。
根本的に間違っているんですかね。

10   名前: 匿名 : 2007/05/26(土) 18:38  ID:XItuWrOP sub-Cz
> 根本的に間違っているんですかね。

…うん。

まず、イベント伝播を抑止するのは、標準的には event.stopPropagation()(IE だけ event.cancelBubble = true)。実行したいイベントハンドラが終了した時点で、これを呼び出す。

しかし、イベント伝播を抑止するなんてのは、よくよくのことであって、基本的に抑止すべきではない。たいていは、設計の見直しが必要。
<div onmouseout="displayToNone (event);">
  <a>森</a>
  <a>海</a>
  <p>空</p>
</div>

ここでは、div 要素が「オブザーバ」として、内部でのイベント発生を監視している。

(1). div 要素内のどこかの要素で mouseout イベントが発生したら、
(2). event.relatedTarget(IE のみ event.toElement)に、マウス移動先の要素が格納されるので、
(3). event.relatedTarget(IE のみ event.toElement)が div 要素の外にあれば、div 要素を消す。

という手順になる。もっと言えば、いちいち a 要素やら div 要素やらに mouseover だの mouseout なんぞ付けなくとも、document に mouseover を 1 個だけ付ければ、この件は事足りるんだ。考えてみて。

まあ、絶対に他のスクリプトに干渉しないなら、手っ取り早くイベント伝播抑止でも良いけどね。

11   名前: 匿名 : 2007/05/26(土) 18:38  ID:XItuWrOP sub-Cz
あーごめん、脊髄反射でレスってしまったけど、この件にイベント伝播抑止は関係なかったね。失礼。

>>10 の (1)〜(3) のようにして、event.relatedTarget が div 要素内にあるかをチェックする。地道に parentNode を辿っても良いし、Firefox なら Node#compareDocumentPosition() を使えるし、IE なら Node#conains() もあるし、やり方はいろいろある。

12   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
>>10さん、ありがとうございます。
やはり根本的に間違っているんですね。
今度はレイヤーdoremiからマウスアウトした時に
レイヤーdoremiのそとにカーソルがあるかどうか
判定するスクリプトに挑戦してみます。

最後に教えて欲しいのですが、
>いちいち a 要素やら div 要素やらに mouseover だの mouseout なんぞ付けなくとも、
>document に mouseover を 1 個だけ付ければ、この件は事足りるんだ。考えてみて。
これは、prototype.jsをつかてwindow.onload時に
イベントハンドラを設定するという事でしょうか?
もしお手数でなければ、書き方の見本を頂ければ
ありがたいです。

13   名前: 匿名 : 2007/05/26(土) 18:38  ID:XItuWrOP sub-Cz
>>12
何故ここで prototype.js が?

以下、Firefox (DOM3-Core + DOM2-Events + α) 用。Opera、Safari も少し直せば動く。即席だからエラーがある可能性が大。

IE だけが特殊な書き換えを必要とするので、まあ頑張って下さい(attachEvent、srcElement その他)。
/*document.*/addEventListener ('load', function (event)
{
    document.addEventListener ('mouseover',
    {
        related    : document.getElementById ('doremi'),
        hasEntered : false,
        
        contains : function (node) {
            // node が div#doremi 内にあれば true、そうでなければ false
            return node == this.related
                || 0 != (node.compareDocumentPosition (this.related) & Node.DOCUMENT_POSITION_CONTAINS);
        },
        
        show : function () {
            this.related.style.display = 'block';
        },
        
        hide : function () {
            this.related.style.display = 'none';
        },
        
        handleEvent : function (event) {
            var t = event.target;
            
            if (t.nodeName == 'A' && t.hasChildNodes () && t.firstChild.nodeValue == 'imoimoimo') {
                // マウスが a[.="imoimoimo"] に入ったとき
                this.show ();
                
            } else if (this.contains (t)) {
                // マウスが div#doremi 内にあるとき
                this.hasEntered = true;
                
            } else if (this.hasEntered) {
                // マウスが div#doremi 内に入ったことがあり、外に出たとき
                this.hide ();
                this.hasEntered = false;
            }
        }
    }, false);
    
    event.target.removeEventListener ('load', arguments.callee, false);
}, false);

オブザーバが document では広すぎると思ったら、適当な範囲に。

14   名前: ゆう : 2007/05/26(土) 18:38  ID:CGK2XsFC sub-ux
>>13さん、ありがとうございます。
せっかく作って頂いたのに悲しい事に私では
理解できない部分が多々ありまして
つくづく初心者なんだなと思いました。

先ほど言っていた
レイヤーdoremiのそとにカーソルがあるかどうか
判定するスクリプトが出来ました。
上級者から見るととんでもない書き方かも知れませんが
prototype.jswを読み込んだ後に
function none(obj){//引数 1:レイヤーのID

	//レイヤーの表示領域を割り出す。
	pos = {x:0, x_end:0, y:0, y_end:0}
	var rei = $(obj);
	var rei2 = rei;
	while(rei2){ 
		pos.x += rei2.offsetLeft; 
		pos.y += rei2.offsetTop; 
		rei2 = rei2.offsetParent;
	}
	pos.x_end = pos.x + rei.offsetWidth;
	pos.y_end = pos.y + rei.offsetHeight;
	//-------------------------------------------
	//レイヤーの領域外にカーソルが出たらレイヤーを非表示にする。
	var cursor_x;
	var cursor_y;
	
	var browser = navigator.userAgent;
	if (browser.match(/Firefox/)){
		function rr(myEvent){
			cursor_x =myEvent.pageX;
			cursor_y =myEvent.pageY;
			//rei.innerHTML = cursor_x;
			if(pos.x>cursor_x || pos.y>cursor_y || pos.x_end<cursor_x+5 || pos.y_end<cursor_y+5){
				//alert('!!!');
				rei.style.display = "none"
			}
		}	
		rei.onmouseout = rr;	
	}else{
		cursor_x = Event.pointerX(event);
		cursor_y = Event.pointerY(event);
		if(pos.x>cursor_x-5 || pos.y>cursor_y-5 || pos.x_end<cursor_x+5 || pos.y_end<cursor_y+5){
			//alert('!!!');
			rei.style.display = "none"
		}
	}
	//rei.innerHTML =cursor_x;
	
}
と、しました。
取り敢えずこれで狙った動きが出来たので
これを使っていきます。

15   名前: 匿名 : 2007/05/26(土) 18:38  ID:eh6GGPu9 sub-Cz
function contains (node, parentNode) {
    // node が parentNode の子孫かどうかを探索
    while (node && node != parentNode) node = node.parentNode;
    return Boolean (node);
}

function none (div, event) {
    // マウスが出て行った先
    var r = event.relatedTarget || event.toElement;
    
    // マウスが出て行った先が div の外なら div を消す
    if (r && ! contains (r, div)) {
        div.style.display = 'none';
    }
}

<div onmouseout="none (this, event);">

とりあえず「レイヤー」なる発想は捨て去った方が良いと思う。CSS でも DOM でも XPath でも、基本単位は「ノード」です。

あと、ブラウザ分岐するのなら、userAgent を見るのではなく、これから使用するプロパティ・メソッドの有無を調べるべき。

16   名前: ゆう : 2007/05/26(土) 18:38  ID:Maf/p1QT sub-ux
あれから基本単位“ノード”で>>15さんのスクリプトを参考に
作り直しました。
>>15さんのスクリプトは上級者で理解出来ない部分も
ありましたが、自分なりに
function none(objID, event){//引数 第1:レイヤーのID, 第2:eventオブジェクト
	
	//Firefoxは関数内からeventを参照できないので、引数でeventを入れてあげる
	var rei = document.getElementById(objID);
	var ele= (event.relatedTarget || window.event.toElement);
	while(!(ele.parentNode.tagName=="HTML")){
		if(ele.id == objID){var sin = true;	}
		ele = ele.parentNode;
	}
	if(!sin){
		alert("OK");
		rei.style.display="none";
	}
}
と、こうなりました。
私ではこれが精一杯ではないでしょうか。
かなりスッキリしました!

一覧へ戻る