Mozilla Mozilla の憂鬱 Mozilla


◆イベントハンドリング

HTML4.0 では殆ど全てのタグにイベントが指定できます。 そのため、W3C DOM Level 2 ではイベントは大雑把に言って EventTargetEvent と呼ばれる独立したオブジェクトで管理されます。 この EventTarget は全ての Node 及び Node のサブクラスのオブジェクトに 実装されていることになっています。
#> 回りくどい表現ですが、これはチョット胡散臭い気がするので...
#> 特に TextNode あたりなんか... ( 調査中ですけどね ^^; )
#> M18(と NN6正版)からは WindowEventOwner なんてのもあります。 f(^^;

イベントハンドリングを行う方法としては、 W3C HTML4.0 で規定されているタグ内で記述する方法 と W3C DOM Level 2 で規定されているスクリプトで指定する方法 の 2種類が存在するワケです。


タグ内で記述する方法について

タグ内で記述する方法は、従来のように onmouseover, onclick など をタグに記述すればよく、IE4,5 のように殆どのタグで記述できることを除いては 従来と殆ど同じ指定方法と言えます。

考えようによっては、これは便利で比較的安全な方法と言えます。
例えば、hover のようにマウスが文字の上に来たら文字の色を変更すると言ったことが したい場合

<SPAN onmouseover="this.style.color='red'"
   onmouseout="this.style.color='black'">Hover</SPAN>

#> この例は CSS2 に疑似要素として :hover があるので全く意味がないですけど

のように、NN でイベント指定できないタグに記述する限り、 無視されるので、クロスな環境でも問題が発生しにくい特徴があります。

#> このスクリプトは IE4,5 及び Mozilla では動作して、 NN4 では動作しませんがエラーも出ません。

また、逆に言えばクロスな環境で、どのブラウザでも同様に何らかの効果を演出したい場合には、 従来通り INPUT や A タグなどどこでも通用するタグで指定しなければなりません。

#> まあ、これは Mozilla に限った話じゃないですけどね。 f(^^;


スクリプトで指定する方法について

クロスなイベントハンドリング方法の紹介の前に、 先ず、Mozilla における実装について少し説明しておく必要があるでしょう。

#> 実はこのあたりは Undocumented な部分が結構あるような気がします。

W3C DOM Level2 - Document Object Model Events ではイベントの登録は addEventListener メソッドで行うことになっています。
つまり、以下のように記述するわけです。

var div=document.getElementById('div1');
div.addEventListener('mousedown',mousedown,true);

これを見て、「ああなるほど」と思った人は多いと思いますが、 「ちょっと待てよ」と思った人もいると思います。
ここで、 div は getElementById で取得した HTMLDivElement オブジェクトで、 addEventListener は EventTarget オブジェクトのメソッドです。 また、 HTMLDivElement オブジェクトを for(prop in div) でプロパティを取り出しても EventTarget の要素は出ませんし、 IDL を覗いても該当するメソッドやオブジェクトは 見あたりません。
つまり、Node と EventTarget は仲良し( friendly )な関係だけど、他人の関係にあるようです。

では、タグで記述した場合の onmouseover などのイベントはどうでしょう。
これも、該当するオブジェクトを for(prop in div) などで見ても、 IDL にも該当する 属性はありません。

現に
div.setAttribute('onmouseover',mousedown);
なんて記述をしてもなんの効果も得られません。

つまり、記述の仕方は style などと同様、タグの属性のように見えるわけですが、 実際には全く別物として扱われているようです。

#> この意味不明なオブジェクトに管理されるイベントは、 M18 からは WindowEventOwner と呼ばれるオブジェクトとして明示的な存在になったようです。
( だからといって、何かがわかったワケではないですけど... ^^; )

そこで、次にレイアをドラッグ操作で移動するスクリプトを例に 手法を研究してみましょう。


レイアのドラッグ操作 - DOM2 な方法

W3C DOM Level 2 で規定されている addEventListener や removeEventListener を 使用すると、レイア( id="t1" - DIV タグ )をドラッグ操作で移動するスクリプトは 次のようになります。

function drag(){ // ドラッグ操作の登録
  var div=document.getElementById('t1');
  div.addEventListener('mousedown',mdownMz,true);
  div.addEventListener('mouseup',  mupMz,  true);
}
function mdownMz(e){ // mouse down 処理
  // 現在のマウス位置を記憶
  e.target.eX=window.scrollX+e.clientX;
  e.target.eY=window.scrollY+e.clientY;
  // mousemove イベントハンドラを登録
  e.target.addEventListener('mousemove',mmoveMz,true);
  e.cancelBubble=true;
}
function mmoveMz(e){ // mouse move 処理
  var ex=window.scrollX+e.clientX;
  var ey=window.scrollY+e.clientY;
  // レイアを移動
  e.target.style.left=parseInt(e.target.style.left)+ex-e.target.eX;
  e.target.style.top =parseInt(e.target.style.top )+ey-e.target.eY;
  e.target.eX=ex; e.target.eY=ey;
  e.cancelBubble=true;
}
function mupMz(e){   // mouse up 処理
  // mousemove イベントハンドラを削除
  e.target.removeEventListener('mousemove',mmoveMz0,true);
  e.cancelBubble=true;
}

然し、現在の Mozilla(M13,M14) では

1. addEventListener の caputure 指定が有効にならない(?)
2. removeEventListener が何故か機能しない

ので、次のようにします。

grabObj=null; // ドラッグ操作中オブジェクトのメモ
function drag(){ // ドラッグ操作の登録
  var div=document.getElementById('t1');
  div.addEventListener('mousedown',mdownMz,true);
}
function mdownMz(e){ // mouse down 処理
  grabObj=e.target;
  grabObj.eX=window.scrollX+e.clientX;
  grabObj.eY=window.scrollY+e.clientY;
  document.addEventListener('mousemove',mmoveMz,true);
  document.addEventListener('mouseup',  mupMz,  true);
  e.cancelBubble=true;
}
function mmoveMz(e){ // mouse move 処理
  if(grabObj==null) return;
  var ex=window.scrollX+e.clientX;
  var ey=window.scrollY+e.clientY;
  grabObj.style.left=parseInt(grabObj.style.left)+ex-grabObj.eX;
  grabObj.style.top =parseInt(grabObj.style.top )+ey-grabObj.eY;
  grabObj.eX=ex; grabObj.eY=ey;
  e.cancelBubble=true;
}
function mupMz(e){ // mouse up 処理
  if(grabObj==null) return;
  document.removeEventListener('mousemove',mmoveMz,false);
  document.removeEventListener('mouseup',  mupMz,  false);
  e.cancelBubble=true;
  grabObj=null;
}

レイアのドラッグ操作 - Mozilla な方法

Mozilla には addEventListener でイベントハンドラを登録する方法以外に 従来のように( 互換のためか? )、 onmousedown プロパティ等にハンドラを登録する 方法も機能するようです。
#> これは調べた限り、記述されていませんでしたが... f(^^;;

grabObj=null; // ドラッグ操作中オブジェクトのメモ
function drag(){ // ドラッグ操作の登録
  var div=document.getElementById('t1');
  div.onmousedown=mdownMz;
}
function mdownMz(e){ // mouse down 処理
  grabObj=e.target;
  grabObj.eX=window.scrollX+e.clientX;
  grabObj.eY=window.scrollY+e.clientY;
  document.onmousemove=mmoveMz;
  document.onmouseup  =mupMz;
  e.cancelBubble=true;
}
function mmoveMz(e){ // mouse move 処理
  if(grabObj==null) return;
  var ex=window.scrollX+e.clientX;
  var ey=window.scrollY+e.clientY;
  grabObj.style.left=parseInt(grabObj.style.left)+ex-grabObj.eX;
  grabObj.style.top =parseInt(grabObj.style.top )+ey-grabObj.eY;
  grabObj.eX=ex; grabObj.eY=ey;
  e.cancelBubble=true;
}
function mupMz(e){ // mouse up 処理
  if(grabObj==null) return;
  document.onmousemove=null;
  document.onmouseup  =null;
  e.cancelBubble=true;
  grabObj=null;
}

まあ、 DOM2 な方法と比べてそれほど変わるわけではありませんが、 removeEventListener でハンドラが削除できないよりはマシってとこですか(笑)。


レイアのドラッグ操作 - クロスでちょっと汎用

こんどは、ドラッグ操作を行うためのクロスな関数を作ってみましょう。

関数の I/F はとりあえず

setDrag(div,dStart,dStartData,dIng,dIngData,dEnd,dEndData);
  div        : レイア
  dStart     : ドラッグ開始時の通知関数
  dStartData : dStart で指定した関数に付けるデータ
  dIng       : ドラッグ中の通知関数
  dIngData   : dIng で指定した関数に付けるデータ
  dEnd       : ドラッグ終了時の通知関数
  dEndData   : dEnd で指定した関数に付けるデータ

  dStart,dIng,dEnd 各ハンドラの呼出形式は
  handler(div,x,y,data);
    div  : レイア
    x,y  : マウスの位置
    data : setDrag で指定したデータ

にでもすると以下のようになります。

_dom=(document.all?3:(document.getElementById?1:(document.layers?2:0)));
_ie5=(navigator.appVersion.indexOf('MSIE 5')>=0);
grabObj=null; // ドラッグ操作中オブジェクトのメモ

// 登録関数
function setDrag(div,dStart,dStartData,dIng,dIngData,dEnd,dEndData){
  var targetObj=null;
  if(_dom==1){ // Mozilla
    div.onmousedown=mdownMz;
    targetObj=div;
  }
  else if(_dom==2){ // NN4
    div.document.onmousedown=mdownNN;
    div.document.captureEvents(Event.MOUSEDOWN);
    targetObj=div.document;
  }
  else if(_dom==3){ // IE4,IE5
    div.onmousedown=mdownIE;
    targetObj=div;
  }
  if(targetObj)
    targetObj.objctrl=new ObjCtrl(div,
      dStart,dStartData,dIng,dIngData,dEnd,dEndData);
}

// イベント制御用オブジェクト
function ObjCtrl(div,dStart,dStartData,dIng,dIngData,dEnd,dEndData){
  this.div      =div;
  this.dragStart=dStart; this.dragStartData=dStartData;
  this.dragging =dIng;   this.draggingData =dIngData;
  this.dragEnd  =dEnd;   this.dragEndData  =dEndData;
}

// ============= Mozilla用イベントハンドラ ============
function mdownMz(e){ // mousedownハンドラ
  if(e.target.objctrl==null) return;
  grabObj=e.target.objctrl;
  var div = grabObj.div;
  if(grabObj.dragging) document.onmousemove=mmoveMz;
  document.onmouseup=mupMz;
  if(grabObj.dragStart)
    grabObj.dragStart(div,
      window.scrollX+e.clientX,window.scrollY+e.clientY,
      grabObj.dragStartData);
  e.cancelBubble=true;
}
function mmoveMz(e){ // mousemoveハンドラ
  if(grabObj==null) return;
  if(grabObj.dragging)
    grabObj.dragging(grabObj.div,
      window.scrollX+e.clientX,window.scrollY+e.clientY,
      grabObj.draggingData);
  e.cancelBubble=true;
}
function mupMz(e){ // mouseupハンドラ
  if(grabObj==null) return;
  var div=grabObj.div;
  if(grabObj.dragEnd)
    grabObj.dragEnd(div,
      window.scrollX+e.clientX,window.scrollY+e.clientY,
      grabObj.dragEndData);
  if(grabObj.dragging) document.onmousemove=null;
  document.onmouseup=null;
  grabObj=null;
  e.cancelBubble=true;
}

// ============= NN用イベントハンドラ ============
function mdownNN(e){ // mousedownハンドラ
  if(e.target.objctrl==null) return;
  grabObj=e.target.objctrl;
  var div = grabObj.div;
  if(grabObj.dragging){
    div.document.onmousemove=mmoveNN;
    div.document.captureEvents(Event.MOUSEMOVE);
  }
  div.document.onmouseup=mupNN;
  div.document.captureEvents(Event.MOUSEUP);
  if(grabObj.dragStart)
    grabObj.dragStart(div,e.pageX,e.pageY,grabObj.dragStartData);
  return false;
}
function mmoveNN(e){ // mousemoveハンドラ
  if(grabObj==null) return;
  if(grabObj.dragging)
    grabObj.dragging(grabObj.div,e.pageX,e.pageY,grabObj.draggingData);
  return false;
}
function mupNN(e){ // mouseupハンドラ
  if(grabObj==null) return;
  var div=grabObj.div;
  if(grabObj.dragEnd)
    grabObj.dragEnd(div,e.pageX,e.pageY,grabObj.dragEndData);
  if(grabObj.dragging){
    div.document.onmousemove=null;
  }
  div.document.onmouseup=null;
  grabObj=null;
  return false;
}

// ============= IE用イベントハンドラ ============
function mdownIE(){ // mousedownハンドラ
  if(window.event.srcElement.objctrl==null) return;
  grabObj=window.event.srcElement.objctrl;
  var div = grabObj.div;
  if(grabObj.dragging)
    document.onmousemove=mmoveIE;
  document.onmouseup=mupIE;
  if(grabObj.dragStart)
    grabObj.dragStart(div,
      (_ie5?document.body.scrollLeft:0)+window.event.clientX,
      (_ie5?document.body.scrollTop :0)+window.event.clientY,
      grabObj.dragStartData);
  window.event.returnValue=false;
}
function mmoveIE(){ // mousemoveハンドラ
  if(grabObj==null) return;
  if(grabObj.dragging)
    grabObj.dragging(grabObj.div,
      (_ie5?document.body.scrollLeft:0)+window.event.clientX,
      (_ie5?document.body.scrollTop :0)+window.event.clientY,
      grabObj.draggingData);
  window.event.returnValue=false;
}
function mupIE(){ // mouseupハンドラ
  if(grabObj==null) return;
  var div=grabObj.div;
  if(grabObj.dragEnd)
    grabObj.dragEnd(div,
      (_ie5?document.body.scrollLeft:0)+window.event.clientX,
      (_ie5?document.body.scrollTop :0)+window.event.clientY,
      grabObj.dragEndData);
  if(grabObj.dragging)
    document.onmousemove=null;
  document.onmouseup=null;
  window.event.returnValue=false;
  window.event.cancelBubble=true;
  grabObj=null;
}
注1
この関数は少し手抜きをしています。

つまり、この関数のみではレイア内が空白の場合のみ正常に動作しますが、 その他のタグや文字が含まれる場合イベントが発生しないためにドラッグできない 場合があるからです。

そのようなレイアでもドラッグ操作できるようにするためにはイベントの発生しない タグに対してイベントハンドラを登録し、該当のオブジェクトにドラッグしたい レイアの ObjCtrl を objctrl プロパティとして設定する必要があります。
#> ObjCtrlオブジェクトはこれを制御するためのオブジェクトです。
注2
このサンプルは Mozilla の場合のイベントハンドラ登録に onmousedown/move/up を 使用しています。

将来これらの機能が削除された場合は addEventListener/removeEventListener に 変更する必要があります。

戻る Copyright(c) 2000 ShinSoft. All rights reserved.