JavaScript ドラッグ&ドロップ

概要: このチュートリアルでは、JavaScript ドラッグ&ドロップ API について、そしてそれを用いてシンプルなドラッグ&ドロップアプリケーションを実装する方法を学びます。

JavaScript ドラッグ&ドロップ API入門

HTML5 はドラッグ&ドロップ仕様を正式に導入しました。 ほとんどのモダン Web ブラウザは、HTML5 仕様に基づいたネイティブのドラッグ&ドロップを実装しています。

デフォルトでは、画像とテキストのみドラッグ可能です。 画像をドラッグするには、マウスボタンを押したまま移動するだけです。 テキストをドラッグするには、テキストをハイライトして、画像をドラッグするのと同じ方法でドラッグする必要があります。

HTML5 仕様では、ほぼすべての要素がドラッグ可能であると規定されています。 要素をドラッグ可能にするには、HTML タグに値が truedraggable プロパティを追加します。 例:

<div class="item" draggable="true"></div>Code language: HTML, XML (xml)

ドラッグ可能要素のイベント

要素をドラッグすると、以下の順序でイベントが発生します。

  • dragstart
  • drag
  • dragend

マウスボタンを押したままマウスを動かし始めると、ドラッグしているドラッグ可能要素で dragstart イベントが発生します。 カーソルは、要素を自身にドロップできないことを示すドロップ不可記号(線で斜線を引いた円)に変わります。

dragstart イベントが発生した後、要素をドラッグしている間、drag イベントが繰り返し発生します。

そして、要素のドラッグを停止すると、dragend イベントが発生します。

すべてのイベントのターゲット (e.target) は、ドラッグされている要素です。

デフォルトでは、ブラウザはドラッグされた要素の外観を変更しません。 したがって、好みに基づいて外観をカスタマイズできます。

ドロップターゲットのイベント

要素を有効なドロップターゲットにドラッグすると、以下の順序でイベントが発生します。

  • dragenter
  • dragover
  • dragleave または drop

dragenter イベントは、要素をドロップターゲットにドラッグするとすぐに発生します。

dragenter イベントが発生した後、ドロップターゲットの境界内で要素をドラッグしている間、dragover イベントが繰り返し発生します。

要素をドロップターゲットの境界外にドラッグすると、dragover イベントの発生が停止し、dragleave イベントが発生します。

ターゲットに要素をドロップした場合、dragleave イベントの代わりに drop イベントが発生します。

dragenterdragoverdragleave、および drop イベントのターゲット (e.target) は、ドロップターゲット要素です。

有効なドロップターゲット

ほぼすべての要素がドロップターゲットイベント (dragenterdragoverdragleave、および drop) をサポートしています。 ただし、デフォルトではドロップは許可されていません。

ドロップを許可しないドロップターゲットに要素をドロップした場合、drop イベントは発生しません。

要素を有効なドロップターゲットにするには、対応するイベントハンドラで event.preventDefault() メソッドを呼び出すことにより、dragenter イベントと dragover イベントの両方のデフォルトの動作をオーバーライドできます。 (詳細は例セクションを参照)

dataTransfer オブジェクトを使用したデータ転送

ドラッグ&ドロップアクションでデータを転送するには、dataTransfer オブジェクトを使用します。

dataTransfer オブジェクトは、イベントのプロパティです。 ドラッグされた要素からドロップターゲットにデータを転送することができます。

dataTransfer オブジェクトには、setData()getData() の 2 つのメソッドがあります。

setData() を使用すると、ドラッグ操作のデータを指定された形式とデータに設定できます。

dataTransfer.setData(format, data)Code language: CSS (css)

形式は text/plain または text/uri-list です。 また、データは、ドラッグオブジェクトに追加するデータを表す文字列です。

getData() メソッドは、setData() メソッドによって保存されたドラッグデータを取得します。

getData() は 1 つの引数を受け入れます。

dataTransfer(format)

形式は text/plain または text/uri-list です。 getData() は、setData() メソッドによって保存された文字列、またはドラッグ操作にデータが含まれていない場合は空の文字列を返します。

JavaScript ドラッグ&ドロップの例

JavaScript ドラッグ&ドロップ API を説明するために、次の シンプルなドラッグ&ドロップアプリケーション を開発します。

プロジェクト構造の作成

まず、drag-n-drop-basics という新しいフォルダを作成します。 このフォルダ内に、cssjs という 2 つのサブフォルダを作成します。

次に、js フォルダに app.js という新しいファイル、css フォルダに style.cssdrag-n-drop-basics フォルダに index.html を作成します。

3 番目に、style.css へのリンクと app.js にリンクするスクリプトタグを index.html ファイルに次のように配置します。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript - Drag and Drop Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
   
    <script src="js/app.js"></script>
</body>

</html>Code language: HTML, XML (xml)

CSS については、ここから入手できます

index.html ファイルの構築

次のコードを index.html ファイルに配置します。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript - Drag and Drop Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
    <div class="container">
        <h1>JavaScript - Drag and Drop</h1>
        <div class="drop-targets">
            <div class="box">
                <div class="item" id="item">
                </div>
            </div>
            <div class="box"></div>
            <div class="box"></div>
        </div>
    </div>
    <script src="js/app.js"></script>
</body>

</html>Code language: HTML, XML (xml)

この index.html ファイルでは、.container 要素を使用して、見出しと drop-targets 要素を配置しました。

drop-targets 要素内に、同じクラス box を持つ 3 つの div 要素を配置しました。 そして、クラス item を持つ別の div 要素を最初のボックスに配置しました。

index.html を開いて黄色のボックスをドラッグしようとすると、ドラッグできないことを示すカーソルが表示されます。

要素をドラッグ可能にするには、HTML タグに値が truedraggable プロパティを次のように追加します。

<div class="item" id="item" draggable="true">Code language: JavaScript (javascript)

これで、index.html を保存してブラウザで再度開くと、次のように item 要素をドラッグできることがわかります。

ドラッグ可能要素のイベント処理

style.css ファイルには、要素を非表示にする .hide クラスがあります。

.hide {
    display: none;
}Code language: CSS (css)

app.js ファイルに、次のコードを追加します。

// select the item element
const item = document.querySelector('.item');

// attach the dragstart event handler
item.addEventListener('dragstart', dragStart);

// handle the dragstart

function dragStart(e) {
   console.log('drag starts...');
}
Code language: JavaScript (javascript)

仕組み

  • まず、querySelector() を使用してドラッグ可能要素を選択します。
  • 次に、ドラッグ可能要素に dragstart イベントハンドラをアタッチします。
  • 3 番目に、dragstart イベントを処理する dragStart() 関数を定義します。

index.html ファイルを開いてドラッグ可能要素のドラッグを開始すると、コンソールに「ドラッグ開始...」メッセージが表示されます。

dragStart イベントハンドラでは、ドラッグ可能要素の id を格納する必要があります。 そして、それを非表示にする必要があります。

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    e.target.classList.add('hide');
}Code language: JavaScript (javascript)

要素をドラッグすると、ドラッグを開始するとすぐに要素が消えることがわかります。

これを解決するには、setTimeout() 関数を使用します。

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    setTimeout(() => {
        e.target.classList.add('hide');
    }, 0);

}Code language: JavaScript (javascript)

これで、ドラッグ可能要素を元の位置からドラッグできます。

ドロップターゲットのイベント処理

style.css ファイルには、ドロップターゲットの境界線スタイルを破線と赤に変更する .drag-over という CSS クラスもあります。

.drag-over {
    border: dashed 3px red;
}
Code language: CSS (css)

app.js では、ドロップターゲット要素を選択し、これらの要素の dragenterdragoverdragleave、および drop イベントを処理する必要があります。

const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
    box.addEventListener('dragenter', dragEnter)
    box.addEventListener('dragover', dragOver);
    box.addEventListener('dragleave', dragLeave);
    box.addEventListener('drop', drop);
});

function dragEnter(e) {
}

function dragOver(e) {
}

function dragLeave(e) {
}

function drop(e) {
}
Code language: JavaScript (javascript)

ドロップターゲットの境界線スタイルは、dragenter イベントと dragover イベントが発生したときに変更する必要があります。 dragleave イベントと drop イベントが発生したときにスタイルを復元する必要があります。

これを行うには、次のようにドロップターゲットに drag-over クラスを追加および削除します。

function dragEnter(e) {
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.target.classList.add('drag-over');
}

function dragLeave(e) {
    e.target.classList.remove('drag-over');
}

function drop(e) {
    e.target.classList.remove('drag-over');

}Code language: JavaScript (javascript)

これで、ドラッグ可能要素を別のドロップターゲットにドラッグすると、次の図に示すように、ドロップターゲットの境界線が変更されます。

ドロップターゲットを有効にするには、次のように dragenter イベントハンドラと dragover イベントハンドラで event.preventDefault() を呼び出す必要があります。

function dragEnter(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}Code language: JavaScript (javascript)

これを行わないと、div 要素はデフォルトでは有効なドロップターゲットではないため、drop イベントは発生しません。

ドラッグ可能要素をドロップターゲットにドラッグすると、要素をドロップできることを示すカーソルが変更されます。

ここで、item 要素をドロップすると、すぐに消えることがわかります。

この問題を解決するには、drop イベントを処理する必要があります。

  • まず、dataTransfer オブジェクトの getData() メソッドを使用して、ドラッグ可能要素の id を取得します。
  • 次に、ドラッグ可能要素をドロップターゲット要素の子要素として追加します。
  • 3 番目に、draggable 要素から hide クラスを削除します。

次のコードは、完全な drop イベントハンドラを示しています。

function drop(e) {
    e.target.classList.remove('drag-over');

    // get the draggable element
    const id = e.dataTransfer.getData('text/plain');
    const draggable = document.getElementById(id);

    // add it to the drop target
    e.target.appendChild(draggable);

    // display the draggable element
    draggable.classList.remove('hide');
}Code language: JavaScript (javascript)

これで、ドラッグ可能要素をドラッグアンドドロップすると、期待どおりに動作するはずです。

以下は、完全な app.js ファイルです。

/* draggable element */
const item = document.querySelector('.item');

item.addEventListener('dragstart', dragStart);

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    setTimeout(() => {
        e.target.classList.add('hide');
    }, 0);
}


/* drop targets */
const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
    box.addEventListener('dragenter', dragEnter)
    box.addEventListener('dragover', dragOver);
    box.addEventListener('dragleave', dragLeave);
    box.addEventListener('drop', drop);
});


function dragEnter(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragLeave(e) {
    e.target.classList.remove('drag-over');
}

function drop(e) {
    e.target.classList.remove('drag-over');

    // get the draggable element
    const id = e.dataTransfer.getData('text/plain');
    const draggable = document.getElementById(id);

    // add it to the drop target
    e.target.appendChild(draggable);

    // display the draggable element
    draggable.classList.remove('hide');
}Code language: JavaScript (javascript)

そして、こちらがデモへのリンクです

まとめ

  • 要素をドラッグ可能にするには、値が true の draggable プロパティを追加します。
  • dragstartdrag、および dragend イベントは、ドラッグ可能要素で発生します。
  • dragenterdragoverdragleave、または drop イベントは、ドロップターゲットで発生します。
  • 要素を有効なドロップターゲットにするには、dragenter イベントハンドラと dragover イベントハンドラで event.preventDefault() を呼び出します。
  • ドラッグアンドドロップ操作でデータを転送するには、setData() メソッドと getData() メソッドを備えた event.dataTransfer オブジェクトを使用します。
このチュートリアルは役に立ちましたか?