対話的なページを作成するときレイア上でのマウスイベントを利用するケースがあります。
しかし、クロスなマウスイベントのハンドリングは結構難しいものがあります。
そこで、汎用のマウスイベントをハンドリングするためのオブジェクトを紹介しましょう。
上のサンプルは各レイアに onmouseover/out イベントをハンドリングしたり、
onmousedown/move/up イベントによりレイアのドラッグ操作やクリック監視を行う例です。
「マウスオーバ監視」を登録すると2つのレイアに onmouseover/out を設定し、
レイア上にマウスがのると、そのレイアを最前面にします。
「ドラッグ監視」を登録すると各レイアでドラッグ操作でレイアを移動し、
クリック操作で「クリック状態」欄のテキスト入力域にメッセージを表示します。
以下は汎用イベントハンドラ用オブジェクトのコードです。
<script language="JavaScript1.2"><!-- _grabObj = null; // 占有中のオブジェクト // イベント制御オブジェクト function EventCtrl(div){ this.div = div; this.type = ''; this.mask = 0; this.pageX = 0; this.pageY = 0; } // イベント制御オブジェクトの共用化メソッド EventCtrl.prototype.linkCtrl = function(obj){ if(obj && !obj.eventCtrl) obj.eventCtrl=this; return this; } // 閾値の設定メソッド EventCtrl.prototype.setThreshold = function(threshold){ this.threshold = threshold; return this; } // 要素名から占有中のイベント制御オブジェクトを得る IE用関数 function getCtrlFromElementIE(el,tagName){ for(;el;el=el.parentElement) if((tagName==null || el.tagName==tagName) && el.eventCtrl) return el.eventCtrl; return null; } // イベントからイベント制御オブジェクトを得る関数 function getCtrlFromEventIE(e,tagName){ // IE用 var ctrl=_grabObj; var event=window.event; if(ctrl==null){ var mask=0, type=event.type; switch(type){ case 'mouseover': var fromCtrl = getCtrlFromElementIE( event.fromElement,tagName); var toCtrl = getCtrlFromElementIE( event.toElement, tagName); if(fromCtrl!=toCtrl) ctrl=toCtrl; if(!ctrl || (ctrl.mask&1)==0) ctrl=null; break; case 'mouseout': var fromCtrl = getCtrlFromElementIE( event.fromElement,tagName); var toCtrl = getCtrlFromElementIE( event.toElement, tagName); if(fromCtrl!=toCtrl) ctrl=fromCtrl; if(!ctrl || (ctrl.mask&1)==0) ctrl=null; break; case 'mousedown': case 'mousemove': case 'mouseup': ctrl = getCtrlFromElementIE(event.srcElement,tagName); if(ctrl && (ctrl.mask&2)!=0) break; default: ctrl=null; break; } } if(ctrl){ ctrl.pageX = document.body.scrollLeft+event.clientX; ctrl.pageY = document.body.scrollTop +event.clientY; ctrl.type = event.type; } return ctrl; } function getCtrlFromEventNN4(e,tagName){ // NN4用 var ctrl=_grabObj; if(ctrl==null) ctrl=e.target.eventCtrl; if(ctrl){ var mask=0; switch(e.type){ case 'mouseover': case 'mouseout': mask|=1; break; case 'mousedown': case 'mousemove': case 'mouseup': mask|=2; break; } if((ctrl.mask&mask)!=0){ ctrl.pageX = e.pageX; ctrl.pageY = e.pageY; ctrl.type = e.type; } else ctrl=null; } return ctrl; } function getCtrlFromEventMz(e,tagName){ // Mozilla用 var ctrl=_grabObj; if(ctrl==null){ for(var t=e.target; t!=null; t=t.parentNode){ if(( tagName==null ||( t.nodeType==Node.ELEMENT_NODE && t.tagName==tagName) ) && t.eventCtrl){ ctrl=t.eventCtrl; break; } } } if(ctrl){ ctrl.pageX = e.clientX+window.scrollX; ctrl.pageY = e.clientY+window.scrollY; ctrl.type = e.type; } return ctrl; } function getCtrlFromEventNop(e,tagName){ // Dummy return null; } getCtrlFromEvent=(_dom==1||_dom==2)?getCtrlFromEventIE: (_dom==3?getCtrlFromEventNN4: (_dom==4?getCtrlFromEventMz: getCtrlFromEventNop)); // mouseover ハンドラ function ech_mouseover(e){ var ctrl = getCtrlFromEvent(e,null); if(ctrl && ctrl.mouseover && !ctrl.mouseoverState){ ctrl.mouseoverState = true; if(ctrl.mouseover) ctrl.mouseover(ctrl,ctrl.mouseoverClient); } } // mouseout ハンドラ function ech_mouseout(e){ var ctrl = getCtrlFromEvent(e,null); if(ctrl && ctrl.mouseover && ctrl.mouseoverState){ ctrl.mouseoverState = false; if(ctrl.mouseout) ctrl.mouseout(ctrl,ctrl.mouseoutClient); } } // mousedown ハンドラ function ech_mousedown(e){ var ctrl = getCtrlFromEvent(e,null); if(ctrl && !ctrl.dragging){ _grabObj = ctrl; ctrl.dragging=true; ctrl.dragged = false; ctrl.startX = ctrl.curX = ctrl.pageX; ctrl.startY = ctrl.curY = ctrl.pageY; if(ctrl.mousedown) ctrl.mousedown(ctrl,ctrl.mousedownClient); return false; } return true; } // mousemove ハンドラ function ech_mousemove(e){ var ctrl = getCtrlFromEvent(e,null); if(ctrl && ctrl.dragging){ if(ctrl.curX!=ctrl.pageX || ctrl.curY!=ctrl.pageY){ if( Math.abs(ctrl.pageX-ctrl.startX)>ctrl.threshold || Math.abs(ctrl.pageY-ctrl.startY)>ctrl.threshold) ctrl.dragged = true; if(ctrl.mousemove) ctrl.mousemove(ctrl,ctrl.mousemoveClient); ctrl.curX = ctrl.pageX; ctrl.curY = ctrl.pageY; } return false; } return true; } // mouseup ハンドラ function ech_mouseup(e){ var ctrl = getCtrlFromEvent(e,null); if(ctrl && ctrl.dragging){ _grabObj = null; ctrl.dragging = false; if(ctrl.mouseup) ctrl.mouseup(ctrl,ctrl.mouseupClient); if(!ctrl.dragged && ctrl.mouseclick) ctrl.mouseclick(ctrl,ctrl.mouseclickClient); ctrl.curX = ctrl.pageX; ctrl.curY = ctrl.pageY; return false; } return true; } // mouseover/mouseout ハンドラ登録関数 function ech_attachMouseOverOut(div,ovrf,ovrc,outf,outc){ if(!div.eventCtrl) div.eventCtrl = new EventCtrl(div); var ctrl = div.eventCtrl; ctrl.mouseoverState = false; ctrl.mouseover = ovrf; ctrl.mouseoverClient = ovrc; ctrl.mouseout = outf; ctrl.mouseoutClient = outc; div.onmouseover = ech_mouseover; div.onmouseout = ech_mouseout; ctrl.mask|=1; return ctrl; } // mouseover/mouseout ハンドラ登録削除関数 function ech_detachMouseOverOut(div){ var ctrl = div.eventCtrl; if(ctrl){ ctrl.div.onmouseover = null; ctrl.div.onmouseout = null; ctrl.mask=~1; } } // ドラッグハンドラ登録関数 function ech_attachMouseDrag( div,dwnf,dwnc,movf,movc,upf,upc,clkf,clkc ){ var doc; if(_dom==1||_dom==2){ doc = div; doc.onmousedown = ech_mousedown; document.onmousemove = ech_mousemove; document.onmouseup = ech_mouseup; } else if(_dom==3){ doc = div.document; doc.onmousedown = ech_mousedown; doc.onmousemove = ech_mousemove; doc.onmouseup = ech_mouseup; doc.captureEvents( Event.MOUSEDOWN|Event.MOUSEMOVE|Event.MOUSEUP); } else if(_dom==4){ doc = div; div.onmousedown = ech_mousedown; document.body.onmousemove = ech_mousemove; document.body.onmouseup = ech_mouseup; } else return null; if(!doc.eventCtrl) doc.eventCtrl = new EventCtrl(div); var ctrl=doc.eventCtrl; ctrl.dragging = false; ctrl.dragged = false; ctrl.startX = 0; ctrl.startY = 0; ctrl.curX = 0; ctrl.curY = 0; ctrl.mousedown = dwnf; ctrl.mousedownClient = dwnc; ctrl.mousemove = movf; ctrl.mousemoveClient = movc; ctrl.mouseup = upf; ctrl.mouseupClient = upc; ctrl.mouseclick= clkf; ctrl.mouseclickClient= clkc; ctrl.threshold = 5; ctrl.mask|=2; return ctrl; } // ドラッグハンドラ登録削除関数 function ech_detachMouseDrag(div){ var ctrl = null; if(_dom==1||_dom==2){ ctrl=div.eventCtrl; if(ctrl) ctrl.div.onmousedown=null; } else if(_dom==3){ ctrl = div.document.eventCtrl; if(ctrl){ var doc = ctrl.div.document; doc.releaseEvents( Event.MOUSEDOWN|Event.MOUSEMOVE|Event.MOUSEUP); doc.onmousedown = null; doc.onmousemove = null; doc.onmouseup = null; } } else if(_dom==4){ ctrl = div.eventCtrl; if(ctrl) ctrl.div.onmousedown=null; } if(ctrl) ctrl.mask&=~2; } // --></script>
この文字の部分は
クリック動作として認識するマウス移動の閾値( 単位 : pixcel )の初期値です。
必要に応じて修正してください。
汎用イベントハンドラのオブジェクト、関数とプロパティを以下に示します。
イベント登録・削除関数 mouseover/out イベント登録関数 - 指定したレイアに mouseover/mouseout イベントを定義します object = ech_attachMouseOverOut(div,ovrf,ovrc,outf,outc); div : 登録するレイアオブジェクト ovrf,ovrc : mouseover時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り overf(ctrl,overc) ctrl : イベント制御オブジェクト overc : ユーザ定義変数 outf,outc : mouseout時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り outf(ctrl,outc) ctrl : イベント制御オブジェクト outc : ユーザ定義変数 object : イベント制御オブジェクト ( 詳細はイベント制御オブジェクト参照 ) mouseover/out イベント削除関数 - ech_attachMouseOverOut関数で登録したイベントを削除します ech_attachMouseOverOut(div); div : 削除するレイアオブジェクト ドラッグイベント登録関数 - 指定したレイアにドラッグ操作関連イベントを定義します object = ech_attachMouseDrag(div, dwnf,dwnc,movf,movc,upf,upc,clkf,clkc); div : 登録するレイアオブジェクト dwnf,dwnc : ドラッグ開始時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り dwnf(ctrl,dwnc) ctrl : イベント制御オブジェクト dwnc : ユーザ定義変数 movf,movc : ドラッグ操作時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り movf(ctrl,movc) ctrl : イベント制御オブジェクト movc : ユーザ定義変数 upf,upc : ドラッグ終了時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り upf(ctrl,upc) ctrl : イベント制御オブジェクト movc : ユーザ定義変数 clkf,clkc : クリック時に呼び出す関数とユーザ定義データ 必要ない場合は null を指定する 関数の呼び出し形式は以下の通り clkf(ctrl,clkc) ctrl : イベント制御オブジェクト clkc : ユーザ定義変数 object : イベント制御オブジェクト ( 詳細はイベント制御オブジェクト参照 ) ドラッグイベント削除関数 - ech_detachMouseDrag関数で登録したイベントを削除します。 ech_detachMouseDrag(div); div : 削除するレイアオブジェクト イベント制御オブジェクト コンストラクタ - イベント登録関数内で自動的に生成されます new EventCtrl(div); div : 関連づけるレイアオブジェクト 共用化メソッド - 指定したオブジェクトをレイアと関連づけます linkCtrl(obj); obj : 関連づけるオブジェクト 閾値設定メソッド - クリック操作時に許容できるマウスのズレの範囲を設定します setThreshold(threshold); threshold : 閾値( pixel ) イベント制御オブジェクトの公開プロパティ div : [C ] 管理しているレイアのオブジェクト type : [E0] 発生したイベントの種類 pageX, pageY : [E0] イベント発生時のマウス位置 startX,startY : [E1] ドラッグ開始時のマウス位置 curX,curY : [E1] 前回のマウス位置 [C ] は常に有効 [E0] は登録したマウスハンドラ内でのみ有効 [E1] はドラッグ操作時に関するハンドラ内でのみ有効
使い方
レイア "d1" に mouseover/mouseout イベント発生時に、 ハンドラ mover,mout を呼び出すようにするには以下のようにします。
// mouseover, mouseout ハンドラの登録 function regMoverMout(){ ech_attachMouseOverOut(getDivFromName('d1'),mover,1,mout,2); } function mover(ctrl,client){ // mouseover 時の処理( 発生元レイアは ctrl.div ) } function mout(ctrl,client){ // mouseout 時の処理( 発生元レイアは ctrl.div ) }
ech_attachMouseOverOut関数の呼び出し( regMoverMout の処理 )は
HTML の読み込み処理が完了した onLoad イベント時やサンプルのように何らかの操作で行います。
同様にドラッグ操作を行う場合は以下のようにします。
// マウスドラッグの登録 function regDrag(){ ech_attachMouseDrag(getDivFromName('d1'), null, null, // ドラッグ開始 dragging,1, // ドラッグ処理 null, null, // ドラッグ終了 null, null); // クリック } // ドラッグ処理 function dragging(ctrl,client){ // レイアを移動するなら... moveDivBy(ctrl.div, ctrl.pageX-ctrl.curX, ctrl.pageY-ctrl.curY); }
もしレイア "d1" 内に画像があるなら、NN4 では画像上にマウスをあてると反応しないので
linkCtrl メソッドを使用して関連づけます。
例えば、画像の名称が 'face' であれば 上の regDrag 関数は以下のようにします。
// マウスドラッグの登録
function regDrag(){
ech_attachMouseDrag(getDivFromName('d1'),
null,null, dragging,1, null,null, null,null)
.linkCtrl(getDivImage(getDivFromName('d1'),'face'));
}
複数の画像がある場合は必要数分だけ linkCtrl メソッドを使用して関連づけます。
注意事項
^^;
)、
また、バージョン毎に抑止方法も異なったりしますので、
特にイベント伝搬の抑止処理はしていません。