ドラッグ処理を行う

 ここでは基本的なドラッグ処理について説明します。ドラッグ処理の流れは以下のようになります。

  1. オブジェクト(タグ上)でマウスのボタンが押されたら
  2. マウスの座標に合わせてオブジェクトを動かす
  3. マウスのボタンが離されたらオブジェクトを動かすのを中止

 まず、マウスのボタンが押されたら、というのはmousedownイベントで検知できます。サンプルではdivタグにmousedownイベントハンドラを設定します。マウスボタンが押されたら、どのオブジェクトが押されているかを調べる必要があります。ここでは、どのオブジェクトなのかを示すdragObj変数を用意しておきます。以下のサンプルでは1つしかドラッグするオブジェクトがないので、dragObjには常に同じオブジェクト情報が入ります。Ajaxでは複数のオブジェクトをドラッグすることがあるため、複数のオブジェクトに対応できるようにしておきます。
 ドラッグ中かどうかを示すため、フラグ変数dragFlagを用意しておきます。このフラグがtrueの場合のみドラッグ処理を行います。オブジェクトをマウスに追従させるためには、マウスが移動した時にオブジェクトにマウスの座標を設定します。マウスが移動するとmousemoveイベントが発生するので、イベント発生時の処理先の関数を設定しておきます。処理先の関数ではフラグ変数dragFlagがtrueの場合、つまりドラッグ中の場合のみ、オブジェクトの座標を設定します。
 マウスの座標を、そのままオブジェクトの座標としてしまうと、どうしても表示がずれてしまいます(中央をつかんでも、常にオブジェクトの左上がマウスに追従してしまう)。これを解消するには、最初にマウスボタンが押された時に、オブジェクトとマウスの座標の差分(オフセット)を求めておきます。以下のサンプルでは変数offsetX, offsetYが該当します。
 最後にマウスボタンが離されたらドラッグ中であることを示す変数dragFlagをfalseにします。これでオブジェクトをドラッグすることができます。(サンプルを実行する


<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>ドラッグ処理</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
dragFlag = false; // ドラッグ中かどうかのフラグ変数
dragObj = null // ドラッグ対象オブジェクト情報
// イベントハンドラなどを設定
window.onload = function()
{
document.getElementById("mainContents").onmousedown = dragStart;
document.getElementById("mainContents").onmouseup = dragEnd;
document.getElementById("mainContents").style.left = "60px";
document.getElementById("mainContents").style.top = "50px";
window.document.onmousemove = dragProc;
}
// ドラッグ開始処理
function dragStart()
{
dragFlag = true;
dragObj = document.getElementById("mainContents");
offsetX = mouseX - parseInt(dragObj.style.left);
offsetY = mouseY - parseInt(dragObj.style.top);
return false;
}
// ドラッグ終了処理
function dragEnd()
{
dragFlag = false;
}
// ドラッグ中の処理
function dragProc(evt)
{
if (document.all)
{
mouseX = event.x;
mouseY = event.y;
}else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
if (!dragFlag) return;
dragObj.style.left = mouseX - offsetX;
dragObj.style.top = mouseY - offsetY;
return false;
}
// --></script>
</head>
<body>
<h1>ドラッグ処理</h1>
<div id="mainContents">
ここをドラッグできます。
</div>
</body>
</html>

 関数の最後でreturn falseを指定していますが、このようにしないとブラウザ側で処理するドラッグ処理が行われてしまいます。つまりページ上の画像やテキストが選択されてしまいます。

 ドラッグするオブジェクトが1つの場合は非常に簡単です。実際には1つでなく複数のオブジェクトをドラッグすることの方が多いでしょう。上記のスクリプトを改良して複数のオブジェクトをドラッグできるようにします。基本的な処理の流れは同じで、唯一異なるのがドラッグ対象となるオブジェクトが複数あることです。先ほどはdragObj変数にドラッグ対象のオブジェクトを指定しました。複数の場合、この変数にドラッグ対象のオブジェクトへの参照を設定すればできあがりです。
 マウスボタンが押された場合に、どのオブジェクト(サンプルではdivタグ)なのかを調べてdragObj変数に設定します。マウスボタンが押された時には、どのオブジェクト上でイベントが発生したのかを調べることができます。まず、Internet Explorerの場合はevent.srcElementで求めることができます。FirefoxなどW3Cに準拠したブラウザではイベントオブジェクトのtargetにオブジェクトへの参照が入っています。これらのオブジェクト情報を変数dragObjに設定すれば、複数のオブジェクトをドラッグすることができます。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>ドラッグ処理(複数)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
dragFlag = false; // ドラッグ中かどうかのフラグ変数
dragObj = null; // ドラッグ対象オブジェクト情報
// イベントハンドラなどを設定
window.onload = function()
{
document.getElementById("mainContents").onmousedown = dragStart;
document.getElementById("mainContents").onmouseup = dragEnd;
document.getElementById("mainContents").style.left = "60px";
document.getElementById("mainContents").style.top = "50px";
document.getElementById("mainContents2").onmousedown = dragStart;
document.getElementById("mainContents2").onmouseup = dragEnd;
document.getElementById("mainContents2").style.left = "160px";
document.getElementById("mainContents2").style.top = "150px";
document.getElementById("mainContents3").onmousedown = dragStart;
document.getElementById("mainContents3").onmouseup = dragEnd;
document.getElementById("mainContents3").style.left = "260px";
document.getElementById("mainContents3").style.top = "260px";
window.document.onmousemove = dragProc;
}
// ドラッグ開始処理
function dragStart(evt)
{
if(window.addEventListener)
{
dragObj = evt.target;
}else{
dragObj = event.srcElement;
}
dragFlag = true;
offsetX = mouseX - parseInt(dragObj.style.left);
offsetY = mouseY - parseInt(dragObj.style.top);
}
// ドラッグ終了処理
function dragEnd()
{
dragFlag = false;
}
// ドラッグ中の処理
function dragProc(evt)
{
if (document.all)
{
mouseX = event.x;
mouseY = event.y;
}else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
if (!dragFlag) return;
dragObj.style.left = mouseX - offsetX;
dragObj.style.top = mouseY - offsetY;
return false;
}
// --></script>
</head>
<body>
<h1>ドラッグ処理(複数)</h1>
<div id="mainContents">ここをドラッグできます。</div>
<div id="mainContents2">ここをドラッグできます。</div>
<div id="mainContents3">ここをドラッグできます。</div>
</body>
</html>

 これで複数のオブジェクトもドラッグできるようになりました。短いプログラム内で利用するには問題ないでしょう。ただし、Ajaxではプログラムの規模が通常のJavaScriptと比べて長くなり、肥大化する傾向にあります。特に複数のライブラリなどを併用すると変数名や関数名などが衝突し問題になる場合があります。そこで上記のスクリプトを少し書き換えて、ややオブジェクト指向のものに変更してみましょう。
 まず、dragObjという名前のオブジェクトを作成し、必要な情報はそこに格納するようにします。ライブラリであればドラッグ用のオブジェクト(クラス)を用意しますが、ここでは単純にObejctでオブジェクトを作成します。以下のスクリプトで赤字で示す部分が該当部分になります。後は、dragObjオブジェクトに必要事項を入れるためのプロパティを用意します。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>ドラッグ処理(複数)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
// ドラッグ対象オブジェクト情報
dragObj = new Object();
dragObj.dragFlag = false;
dragObj.offsetX = dragObj.offsetX = 0;
dragObj.mouseX = dragObj.mouseY = 0;
dragObj.target = null;

// イベントハンドラなどを設定
window.onload = function()
{
document.getElementById("mainContents").onmousedown = dragStart;
document.getElementById("mainContents").style.left = "60px";
document.getElementById("mainContents").style.top = "50px";
document.getElementById("mainContents2").onmousedown = dragStart;
document.getElementById("mainContents2").style.left = "160px";
document.getElementById("mainContents2").style.top = "150px";
document.getElementById("mainContents3").onmousedown = dragStart;
document.getElementById("mainContents3").style.left = "260px";
document.getElementById("mainContents3").style.top = "260px";
window.document.onmousemove = dragProc;
window.document.onmouseup = dragEnd;
}
// ドラッグ開始処理
function dragStart(evt)
{
if(window.addEventListener)
{
targetElement = evt.target;
}else{
targetElement = event.srcElement;
}
dragObj.dragFlag = true;
dragObj.targetObj = targetElement;
dragObj.offsetX = dragObj.mouseX - parseInt(targetElement.style.left);
dragObj.offsetY = dragObj.mouseY - parseInt(targetElement.style.top);
return false;
}
// ドラッグ終了処理
function dragEnd()
{
dragObj.dragFlag = false;
}
// ドラッグ中の処理
function dragProc(evt)
{
var mouseX,mouseY;
if (document.all)
{
mouseX = event.x;
mouseY = event.y;
}else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
dragObj.mouseX = mouseX;
dragObj.mouseY = mouseY;
if (!dragObj.dragFlag) return;
dragObj.targetObj.style.left = mouseX - dragObj.offsetX;
dragObj.targetObj.style.top = mouseY - dragObj.offsetY;
return false;
}
// --></script>
</head>
<body>
<h1>ドラッグ処理(複数)</h1>
<div id="mainContents">ここをドラッグできます。</div>
<div id="mainContents2">ここをドラッグできます。</div>
<div id="mainContents3">ここをドラッグできます。</div>
</body>
</html>

 ここまでのドラッグスクリプトはオブジェクトの前後関係を保ったまま動かす事ができました。WindowsやMacOSではドラッグする際に対象となるオブジェクトを最前面に出すようになっています。これは対象となるオブジェクトのスタイルシートのz-index(JavaScriptではzIndexプロパティ)にZ座標値を設定します。Z座標は正数で手前に表示されるものほど値が大きくなります。つまりドラッグ開始時にzIndexプロパティに値を設定すれば一番手前にオブジェクトが表示されることになります。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>ドラッグ処理(複数)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
// ドラッグ対象オブジェクト情報
dragObj = new Object();
dragObj.dragFlag = false;
dragObj.offsetX = dragObj.offsetX = 0;
dragObj.mouseX = dragObj.mouseY = 0;
dragObj.target = null;
dragObj.zIndex = 1; // 最初のZ-Index
dragObj.maxLayer = 3; // 最大レイヤー枚数

// イベントハンドラなどを設定
window.onload = function()
{
document.getElementById("mainContents").onmousedown = dragStart;
document.getElementById("mainContents").style.left = "60px";
document.getElementById("mainContents").style.top = "50px";
document.getElementById("mainContents").style.zIndex = 1;
document.getElementById("mainContents2").onmousedown = dragStart;
document.getElementById("mainContents2").style.left = "160px";
document.getElementById("mainContents2").style.top = "150px";
document.getElementById("mainContents2").style.zIndex = 2;
document.getElementById("mainContents3").onmousedown = dragStart;
document.getElementById("mainContents3").style.left = "260px";
document.getElementById("mainContents3").style.top = "260px";
document.getElementById("mainContents3").style.zIndex = 3;
window.document.onmousemove = dragProc;
window.document.onmouseup = dragEnd;
}
// ドラッグ開始処理
function dragStart(evt)
{
if(window.addEventListener)
{
targetElement = evt.target;
}else{
targetElement = event.srcElement;
}
dragObj.dragFlag = true;
dragObj.targetObj = targetElement;
dragObj.offsetX = dragObj.mouseX - parseInt(targetElement.style.left);
dragObj.offsetY = dragObj.mouseY - parseInt(targetElement.style.top);
dragObj.zIndex += dragObj.maxLayer;
dragObj.targetObj.style.zIndex = dragObj.zIndex;
return false;
}
// ドラッグ終了処理
function dragEnd()
{
dragObj.dragFlag = false;
}
// ドラッグ中の処理
function dragProc(evt)
{
var mouseX,mouseY;
if (document.all)
{
mouseX = event.x;
mouseY = event.y;
}else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
dragObj.mouseX = mouseX;
dragObj.mouseY = mouseY;
if (!dragObj.dragFlag) return;
dragObj.targetObj.style.left = mouseX - dragObj.offsetX;
dragObj.targetObj.style.top = mouseY - dragObj.offsetY;
return false;
}
// --></script>
</head>
<body>
<h1>ドラッグ処理(複数)</h1>
<div id="mainContents">ここをドラッグできます。</div>
<div id="mainContents2">ここをドラッグできます。</div>
<div id="mainContents3">ここをドラッグできます。</div>
</body>
</html>

 Ajaxでは、より複雑な処理を行うこともあります。このため、ここで解説したような簡単な処理で終わらない可能性もあります。提供するサービス内容に応じて新たに作成する必要があるかもしれません。(多少見た目をよくするだけならば簡単です。サンプルを実行する

 次項では定期的に処理を行わせるタイマーについて説明します。

[第五章 12:タイマーで定期的に処理を行わせるへ]
[目次へ]

(2006.1.16, 2006.1.18修正)