JavaScript入門[HTML5編]複数のドラッグ要素を扱う

  1. HOME
  2. JavaScript入門[HTML5編]
  3. 複数のドラッグ要素を扱う

今まではドラッグする要素は1つだけでした。 このページでは複数の要素をドラッグ可能にする方法を見ていきたいと思います。

複数の要素をドラッグするサンプルプログラム

複数の要素1つ1つにdragstartイベントを書いていては手間になります。 この場合ドラッグする要素を一括で取得し、 for文でdragstartイベントを追加すると楽になります。 以下の命令を使うと、一括で取得できます。

document.getElementsByTagName(HTMLタグ)
ページ内に存在する指定したHTMLタグ要素を取得します。
取得した要素は、配列として格納されます。

以下のサンプルでは、A~Dまでの4つの文字を描いた画像をドラッグできるようにします。 上の列から下の列にドラッグしてみてください。

<div class="dd" id="dragarea">
<img src="img/a.png" id="a" alt="">
<img src="img/b.png" id="b" alt="">
<img src="img/c.png" id="c" alt="">
<img src="img/d.png" id="d" alt="">
</div>
<div class="dd" id="droparea"></div>

<style type="text/css">
div.dd{ width:100%; height:100px }
div#dragarea{ background-color:#80ffff }
div#droparea{ background-color:#ffff80 }
div.dd img{ width:100px; height:100px }
</style>

<script>
//imgタグ要素を全て取得する
var elm = document.getElementsByTagName("img");

//全imgタグにdragstartイベントを追加する
for ( var i=0 ; i < elm.length ; i++ )
{
  elm[i].addEventListener("dragstart", function(evt){
    evt.dataTransfer.setData("text/plain",evt.target.id);
    evt.stopPropagation();
  },false);
}

var obj1 = document.getElementById("droparea");

//ドロップされる側のコードは変化なし
obj1.addEventListener( "drop" , function(evt){
  var id = evt.dataTransfer.getData( "text/plain" );

  var obj2 = document.getElementById( id );
  if( obj2 )
  {
    obj1.appendChild( obj2 );  //要素を移動する
  }

  evt.preventDefault();
}, false );

//2つのイベントでデフォルト動作を抑制
obj1.addEventListener( "dragenter" , function(evt){
  evt.preventDefault();
}, false );
obj1.addEventListener( "dragover" , function(evt){
  evt.preventDefault();
}, false );
</script>

<サンプル>A~Dの文字を下の列にドラッグしてみてください。

サンプルプログラムの解説

では上記のスクリプトを詳しく見ていきましょう。

HTML部分

<div class="dd" id="dragarea">
<img src="img/a.png" id="a" alt="">
<img src="img/b.png" id="b" alt="">
<img src="img/c.png" id="c" alt="">
<img src="img/d.png" id="d" alt="">
</div>
<div class="dd" id="droparea"></div>

まずはHTML部分の説明です。 2つのdivタグで、ドラッグ要素を表示するエリアと、ドロップされるエリアを作ります。 クラス名「dd」を付けているのは、両者に共通のスタイルを指定をするためです。

上のエリアに、画像を4つ表示させます。これらにはid属性を付けておきます。 なおimgタグは元からドラッグ可能なので、draggable属性は記述していません。

CSS部分

<style type="text/css">
div.dd{ width:100%; height:100px }
div#dragarea{ background-color:#80ffff }
div#droparea{ background-color:#ffff80 }
div.dd img{ width:100px; height:100px }
</style>

続いてスタイルシートを見ていきます。 最初はクラス名「dd」の付いたdivタグの横幅を100%、高さを100pxに指定しています。

続く2行で、id名「dragarea」の付いたdivタグの背景色を薄い青に、 id名「droparea」の付いたdivタグの背景色を薄い黄色にするよう指定しています。

最後はクラス名「dd」の付いたdivタグの中にあるimgタグの縦横の幅を100pxに指定しています。

imgタグ要素を全て取得する

var elm = document.getElementsByTagName("img");

続いてスクリプト内部を見ていきます。 最初にgetElementsByTagName()を使って、ページ内のimgタグを全て取得し、変数elmに格納しています。 このページにはA~Dの文字の画像以外にもimgタグが存在しているので、 それらも格納されてしまうのですが、取りあえず今回はこのままでいきます。

全imgタグにdragstartイベントを追加する

for ( var i=0 ; i < elm.length ; i++ )
{
  elm[i].addEventListener("dragstart", function(evt){
    evt.dataTransfer.setData("text/plain",evt.target.id);
    evt.stopPropagation();
  },false);
}

今度はfor文を使って全部のimgタグにdragstartイベントを追加します。 lengthでimgタグの総数を取得できるので、for文の範囲は0~elm.length未満とします。

elm[i]で、取得した画像タグの個々の要素を指定できます。 これらにaddEventListener()でイベントを追加していきます。

イベント内部で、dataTransferにドラッグ中の画像タグのidをセットします。 stopPropagation()はイベントの予期せぬ伝播を防ぐために記述しています。

dropイベントを追加する

var obj1 = document.getElementById("droparea");

obj1.addEventListener( "drop" , function(evt){
  var id = evt.dataTransfer.getData( "text/plain" );
  var obj2 = document.getElementById( id );
  if( obj2 )
  {
    obj1.appendChild( obj2 );
  }

  evt.preventDefault();
}, false );

次はdropイベントの方です。こちらは前のページと殆ど変わっていません。 最初にドロップされるdivタグをgetElementById()で取得し、変数obj1に格納しています。 これにaddEventListener()でdropイベントを追加します。

イベント内部で、最初にdataTransferからデータを取得し、変数idに格納しています。 dataTransferに渡されたデータは、ドラッグされたimgタグのid名でした。

続いて、取得したid名とgetElementById()を使ってドラッグされた画像タグを取得し、 変数obj2に格納します。

このページのA~Dの画像以外では、id属性を付けていません。 これらの画像をドラッグした場合は、dataTransferにid名の受け渡しができないので、 変数obj2にはnullが入っています。ですからif文で変数obj2をチェックし、 うまくimgタグを掴んでいる場合だけ、appendChild()で画像の移動を行ないます。

preventDefault()はブラウザのデフォルトの動作を抑制するために記述しています。

2つのイベントでデフォルト動作を抑制

obj1.addEventListener( "dragenter" , function(evt){
  evt.preventDefault();
}, false );
obj1.addEventListener( "dragover" , function(evt){
  evt.preventDefault();
}, false );

毎回のことですが、daragstartイベントとdragenterイベントでもブラウザのデフォルトの動作を抑制します。