以下は2013年4月に、Internet Explorer 9と10、FireFox20.0.1、Google Chrome27.0.1453.12、Safari 5.1.7、Opera 12.5で動作を確認しています。
なお、Internet Explorer 8では、ここで使っているイベント登録の手法が使えませんが、下で紹介したライブラリは利用可能です。
その使用例は、ここにあります。
ここでは、マウス移動、マウスダウン、マウスアップイベントを利用した画像ドラック処理の基本的な考え方を示しています。
このページでマウスの移動に対するマウス座標を取得する例を紹介しています。
これらのイベントを利用して、IE専用で画像ドラック用プログラムを昔作ったのですが。現在は動作しません。
それは次のように div の中に絶対座標(absolute)で画像(img)を配置して,
画像をドラックするものですが、マウス移動の座標を取得が失敗したようです。
ブラウザやバージョンによって挙動が微妙に変化する可能性があること思い知らされました。
現在のバージョンでは画像などをドラックするとdragstratイベントが働いて、
マウスボタンを押して移動させた後は、moveイベントが得られていないからです。
この現象は以下の+の画像のドラックで確認できます。移動座標は確認できますが画像ドラックで確認できていません。
<span id="pA"></span><br> <div id="divA" style="position: relative ; background-color:gray; margin: 20px; width: 300px; height: 200px"> <img src="img/bar_plus.gif" style="position: absolute; left: 100px; top: 50px;"> </div> <script language="JavaScript"><!-- document.getElementById("divA").addEventListener("mousemove", function (itEvent){ var itX = this.offsetLeft;//divA要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divA内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//divA要素のページ上からの位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 //( 上記のスクロールの値において、左項はIEやFireFox用、右項はChromeやSafariで取得する表現で、他方は0の前提で作っている。) document.getElementById("pA").innerHTML = "イベントで取得できた座標 ("+ mx + "," + my + ")"; } ,false);//イベントが登録listener に発送され、その後、DOMツリーにおいてその下に位置する EventTarget に発送させる時にtrue // --> </script>
上記のソースより、対応策を検討しました。
透明なdivを画像の上に重ねて配置して それのイベント処理を書くことで下の画像のドラック処理が働なくならないか?
と試しましたができませんでした。
最終的には、画像のドラックが始まらないように、画像に「ondragstart="return false"」を指定する方法が良いと結論付けました。
それを利用したマウスドラックによる画像の移動例を以下に示します。
上記のソースを以下に示します。
<div id="divB" style="position: relative ; background-color:gray; margin: 20px; width: 300px; height: 200px"> <img id="imgB" src="img/bar_plus.gif" style="position: absolute; left: 100px; top: 50px;" ondragstart="return false"> </div> <script language="JavaScript"> var dragFlag = false;//対象画像上でマウスボタンを押すと、true var prevX,prevY;//ドラック中における前のイベントのマウス座標 var dragObj = null; document.getElementById("divB").addEventListener("mousemove", function (itEvent){ var itX = this.offsetLeft;//divB要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divB内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//ページの上からのdivB要素位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 document.getElementById("pB").innerHTML = "イベントで取得できた座標 ("+ mx + "," + my + ")"; if(dragFlag){//ドラック中か? var dx = mx - prevX;//現在もマウス位置と 前の位置で、移動量を求める var objX = dragObj.offsetLeft + dx; dragObj.style.left = objX + "px";//対象画像の移動 prevX =mx;//ドラックでの移動量を求めるため、現在の位置を移動前の情報として記憶 var dy = my - prevY;//現在もマウス位置と 前の位置で、移動量を求める var objY = dragObj.offsetTop + dy; dragObj.style.top = objY + "px";//対象画像の移動 prevY =my;//ドラックでの移動量を求めるため、現在の位置を移動前の情報として記憶 document.getElementById("pB").innerHTML = "ドラックで移動させた座標:"+ objX + "," + objY; } } ,false);//イベントが登録listener に発送され、その後、DOMツリーにおいてその下に位置する EventTarget に発送させる時にtrue document.getElementById("divB").addEventListener("mousedown", function (itEvent){ var itX = this.offsetLeft;//divB要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divB内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//ページの上からのdivB要素位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 // (Safariではscroll系が未対応で、0の加算になることを期待して作っている。) var imgX = document.getElementById("imgB").offsetLeft;//画像imgBの位置、幅、高さを取得 var imgW = document.getElementById("imgB").offsetWidth; var imgY = document.getElementById("imgB").offsetTop; var imgH = document.getElementById("imgB").offsetHeight; document.getElementById("pB").innerHTML = "マウスダウン時のマウス座標(" + mx + "," + my +"),img情報:" +imgX + "," + imgW; if(mx > imgX && mx < imgX+imgW && my > imgY && my < imgY+imgH) {//画像の範囲にマウス位置があるかどうかを調べる。 dragFlag = true;//ドラック開始 dragObj = document.getElementById("imgB"); prevX = mx;//ドラックでの移動量を求めるため、現在位置を移動前の情報として記憶 prevY = my; document.getElementById("pB").innerHTML = "ドラック開始 のマウス座標:("+mx + "," + my + ")"; } } ,false); document.getElementById("divB").addEventListener("mouseup", function (itEvent){ dragFlag = false;//ドラック終了 document.getElementById("pB").innerHTML = "ドラック終了"; } ,false); </script>
上記では マウスムーブ以外で、マウスダウンとマウスアップのイベントも使っています。
まず、マウスダウンした位置がドラック対象の画像の上かを調べて、そうであればdragFlag をtrueにしています。
この時、そのマウス座標と移動対象の画像のオブジェクトをグローバル変数に記憶しています。
後はマウスアップイベントで終了させるまで、マウス移動イベントでドラック移動します。
マウス移動イベントでは、dragFlag がtrueであれば以前のマウス位置からの移動量を算出して、その変位を画像に施すことで移動させています。
以上の動作を利用した、ライブラリをここに作成しました。(自由に右クリックしてダウンロードし、お使いください。)
上記プログラムとの大きな違いは、上記のグローバルであったデータをDragArea のクラス変数にして、ドラック対象を配列で管理している点です。
Dragドラック操作範囲を要素と、ドラック対象要素の配列を引数にして初期設定するためのDragArea.setAreaAndArray関数をライブラリとして用意しています。
第1引数がドラック操作範囲を意味するdivなど要素で、第2引数の配列に入れた画像要素をすべて移動対象にしています。第2引数の配列の要素は、
第1引数要素内でposition: absoluteのスタイル指定をしてあらかじめ配置して使わなければなりません。
また必要に応じて、第3引数でマウスアップした時に実行するユーザ関数の登録、第4引数にデバックでメッセージを表示させたいidを指定できます。
マウスアップした時のユーザ関数は、その時のドラック操作範囲要素内におけるドロップ座標が引数です。
その引数からどこにドロップしたかを調べることを容易にするDragArea.contains(obj, mx, my)関数も用意されいます。
これは引数objの要素中にドロップした座標があればtrueになる関数です。
以下にこの実装例と、そのソースを示します。
デバック用メッセージ→<span id="debugMsg"></span><br> <div id="example" style="position: relative ; background-color:gray; margin: 20px; width: 600; height: 200px"> <img id="img1" ondragstart="return false" src="img/bar_plus.gif" style="position: absolute; left: 100px; top: 10px;"> <img id="img2" ondragstart="return false" src="img/bar_H.gif" style="position: absolute; left: 300px; top: 60px;"> <img id="img3" ondragstart="return false" src="img/bar_V.gif" style="position: absolute; left: 500px; top: 110px;"> </div> <script type="text/javascript" src="../js/drag_area.js"></script> <script language="JavaScript"> var dragarray = new Array(//ドラック対象とする要素の配列 document.getElementById("img1"), document.getElementById("img2"), document.getElementById("img3") ); function dropfunc(mx,my){//ドロップ時に実行させるユーザ定義関数 if(DragArea.dragObj != null){ var s = DragArea.dragObj.id+"をドラックして(" + mx + "," + my + ")の座標でドロップした。\n"; for(i = 0; i < dragarray.length; i++){//ドロップ対象の検索 if(dragarray[i].id != DragArea.dragObj.id && DragArea.contains( dragarray[i] , mx,my)){//画像の範囲にマウス位置があるかどうかを調べる。 s += dragarray[i].id + "の要素にドロップしたと判断されています。" } } alert(s); document.getElementById("debugMsg").innerHTML = s; DragArea.dragObj=null; } } DragArea.setAreaAndArray(document.getElementById("example"), dragarray,dropfunc,"debugMsg");//初期設定の実行 </script>
上記で使っているdrag_area.jsは、ここからダウンロードできます。 また、その内容を以下に示します。
// drag_area.js ドラックにより画像などを移動するライブラリ で DragAreaクラスを定義している。 // 次のメソッドを定義 (ここで、このライブラリ利用者が使うのは、DragArea.setAreaAndArrayとDragArea.containsの2つを想定している) // DragArea.setAreaAndArray(area, dragarray,dropfunc , debugMsgId)//ドラック操作範囲対象のarea要素,ドラック対象要素の配列,マウスアップ時の利用者定義関数、デバック表示要素のid名を引数にする初期設定関数 // areaの要素内に、dragarrayの各ドラック対象要素(position: absolute指定)をあらかじめ配置して使う。 // DragArea.contains(obj, mx, my)//マウスアップ時の利用者定義関数で使うことも想定した関数で、objの要素内にmx, myの座標があるならtrue // DragArea.mousemoveEvent(itEvent)//内部で使われるマウスイベント関数 // DragArea.mousedownEvent(itEvent)//内部で使われるマウスイベント関数 // DragArea.mouseupEvent(itEvent)//内部で使われるマウスイベント関数 // なお、debugMsgIdのidのpなど領域がデバック用メッセージに変更される。それは処理で【1】,【2】,【3】,【4】,【5】,【6】と変化する。 // 利用例および解説 http://manabu.quu.cc/up/6/eventobj2.htm // <script > var DragArea =function(){}; DragArea.prevX=0;//ドラック中における前のイベントのマウス座標 DragArea.prevY=0; DragArea.dragFlag = false; DragArea.dragObj = null;//ドラックしたオブジェクトをドラック開始時にだけ記憶(変更)する。ユーザー定義関数で、何をドラックしたかを確認できてそこでnullにしても構わない。 DragArea.dropfunc = null;//ドロップしたした時の挙動を関数を記憶 DragArea.debugMsgObj = null;//エラーメッセージ表示用要素 DragArea.contains=function (obj, mx, my){// objの要素範囲にmx, my の座標が入っている場合にtrueを返す。 var imgX = obj.offsetLeft;//画像imgBの位置、幅、高さを取得 var imgW = obj.offsetWidth; var imgY = obj.offsetTop; var imgH = obj.offsetHeight; if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【3】マウス位置がオブジェクト上にあるかチェックするDragArea.containsの実行 マウス座標(" + mx + "," + my +"),img情報:" +imgX + "," + imgW; if(mx > imgX && mx < imgX+imgW && my > imgY && my < imgY+imgH) {//画像の範囲にマウス位置があるかどうかを調べる。 return true; } return false; } DragArea.mousedownEvent=function (itEvent){ var ver = navigator.appVersion.toLowerCase(); if(ver.indexOf('msie') != -1){// IE ? ver = parseInt(ver.replace(/.*msie[ ]/,'').match(/^[0-9]+/)); if(ver <= 8) itEvent = event; //IE8以前 }//以上までが IE8対応処理 var itX = this.offsetLeft;//divB要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divB内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//ページの上からのdivB要素位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 // (Safariではscroll系が未対応で、0の加算になることを期待して作っている。) var i = 0; for(i = 0; i < DragArea.dragarray.length; i++){//ドラック対象の上でマウスボタンを押したか? if(DragArea.contains( DragArea.dragarray[i] , mx,my)){//画像の範囲にマウス位置があるかどうかを調べる。 DragArea.dragFlag = true;//ドラック開始 DragArea.dragObj = DragArea.dragarray[i]; DragArea.prevX = mx;//ドラックでの移動量を求めるため、現在位置を移動前の情報として記憶 DragArea.prevY = my; if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【4】ドラック開始 のマウス座標:("+mx + "," + my + ")"; break; } } }; DragArea.mousemoveEvent=function (itEvent){ var ver = navigator.appVersion.toLowerCase(); if(ver.indexOf('msie') != -1){// IE ? ver = parseInt(ver.replace(/.*msie[ ]/,'').match(/^[0-9]+/)); if(ver <= 8) itEvent = event; //IE8以前 }//以上までが IE8対応処理 var itX = this.offsetLeft;//divB要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divB内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//ページの上からのdivB要素位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【2】mousemoveイベントで取得できた座標 ("+ mx + "," + my + ")"; if(DragArea.dragFlag){ var dx = mx - DragArea.prevX;//現在もマウス位置と 前の位置で、移動量を求める var objX = DragArea.dragObj.offsetLeft + dx; DragArea.dragObj.style.left = objX + "px";//対象画像の移動 DragArea.prevX =mx;//ドラックでの移動量を求めるため、現在の位置を移動前の情報として記憶 var dy = my - DragArea.prevY;//現在もマウス位置と 前の位置で、移動量を求める var objY = DragArea.dragObj.offsetTop + dy; DragArea.dragObj.style.top = objY + "px";//対象画像の移動 DragArea.prevY =my;//ドラックでの移動量を求めるため、現在の位置を移動前の情報として記憶 if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【5】ドラック中で移動させた座標:"+ objX + "," + objY; } } DragArea.mouseupEvent=function (itEvent){ var ver = navigator.appVersion.toLowerCase(); if(ver.indexOf('msie') != -1){// IE ? ver = parseInt(ver.replace(/.*msie[ ]/,'').match(/^[0-9]+/)); if(ver <= 8) itEvent = event; //IE8以前 }//以上までが IE8対応処理 DragArea.dragFlag = false;//ドラック終了 var itX = this.offsetLeft;//divB要素の左からの位置を得る。 var mx = itEvent.clientX-itX;//divB内の相対座標を得る //(上記では、offsetXに相当する値であるがFireFoxが使えないので相当する情報を算術している) var itY = this.offsetTop;//ページの上からのdivB要素位置を得る。 var my = itEvent.clientY-itY; my += document.documentElement.scrollTop + document.body.scrollTop;//ページがスクロールされている場合にも対応させる。 if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【6】ドラック終了のマウスアップ座標 ("+ mx + "," + my + ")"; if(DragArea.dropfunc != null) DragArea.dropfunc(mx,my);//ユーザ定義関数の呼び出し } DragArea.setAreaAndArray=function(area, dragarray,dropfunc , debugMsgId){ DragArea.dragarray = dragarray;//ドラック対象の要素の配列 DragArea.dragFlag = false; //ドラック移動中にtrue if(arguments.length > 2) DragArea.dropfunc = dropfunc; //ドロップのユーザ関数 if(arguments.length > 3) DragArea.debugMsgObj = document.getElementById(debugMsgId); //デバック用メッセージ表示先要素 //DOM 2 Events 仕様で導入されたイベントリスナーを登録するための古い方法を使う area.onmousedown=DragArea.mousedownEvent; area.onmousemove=DragArea.mousemoveEvent; area.onmouseup=DragArea.mouseupEvent; if(DragArea.debugMsgObj != null) DragArea.debugMsgObj.innerHTML = "【1】各種イベントの登録:setAreaAndArray実行"; };