Home
JavaScript
【基本編】JavaScriptのマウスイベントでドラッグ&ドロップ機能を実装する

【基本編】JavaScriptのマウスイベントでドラッグ&ドロップ機能を実装する

公開日
2022.06.25
更新日
2023.01.12
【基本編】JavaScriptのマウスイベントでドラッグ&ドロップ機能を実装する

どうも!かけちまるです!

Webツールを開発するときによく使われるドラッグ&ドロップ機能。
感覚的に操作できるので使用者側からするととても便利だと思います。

しかし、実装は難しいと思いがちですがJavaScriptを使うと案外サクッとできてしまいます。

今回は次のような制作物を作る事を目標にします。

この記事では、

  • ・ドラッグ&ドロップ機能に必要なJavaScriptの知識
  • ・ドラッグ&ドロップ機能の実装方法

がわかります。

ドラッグ&ドロップ機能に必要なJavaScriptの知識

改めてドラッグ&ドロップという動作を切り分けて考えてみましょう。

  • ①ドラッグ可能な要素をマウスを押して掴む
  • ②マウスを押したまま要素を移動する
  • ③ドロップ可能な要素の上に持ってくる
  • ④掴んでいる要素を離す

こんな感じですかね。

で、上記のようなアクションをしたときに処理が発生するイベントハンドラは、

onMouseDown マウスを押した時に発生
onMouseUp 押していたマウス離した時に発生
onMouseMove マウスを動かしている時に発生

というようにこの辺りを使えばドラッグ中に要素をz-indexで1番上にしたり、ドロップ可能な要素に重なったときに色を変えたりできそうですね。

ドラッグ&ドロップ機能の実装方法

次のHTMLとCSSをベースにドラッグ&ドロップ機能を実装していきます。

HTML
コピー
<div class="demo"> <div id="droparea" class="droppable"></div> <div id="box"></div> </div>
CSS
コピー
.demo{ background-color: #f0f0f0; box-sizing: border-box; border: 1px solid #646464; padding: 10px; width: 100%; height: 300px; position: relative; } #droparea { background-color: #e6e6e6; box-sizing: border-box; border: 1px dotted #646464; width: 50%; height: 100%; } #box { cursor: grab; background-color: #000; width: 40px; height: 40px; position: absolute; top: 50%; left: 70%; }

こんな感じになりましたかね。

コーディング完成図

ドラッグ&ドロップを実装するためのステップは次の通りです。

  • ①ドラッグできるようにする
  • ②マウスを離した時の動作を追加する
  • ③気になる部分の調整
  • ④完成
1

ドラッグできるようにする

とりあえず、#box要素をドラッグできるようにします。
で、JavaScriptは次のように書きます。

#box要素の上でマウスを押した時(1行目)、カーソルの位置を取得したものをmovePosition関数の引数に渡します。(8行目)
movePosition関数では、#box要素のstyleにtopleftを設定しています。(12、13行目)
mousemoveイベントでマウスが動いた時(21行目)mouseMove関数の処理を走らせます。(17行目)
ブラウザ独自のドラッグ&ドロップ機能が発生しないようにdragstartイベントを潰しておきましょう。(24行目)

JavaScript
コピー
const boxDOM = document.querySelector('#box'); boxDOM.onmousedown = function(event) { boxDOM.style.position = 'absolute'; boxDOM.style.zIndex = 1000; movePosition(event.pageX, event.pageY); // #box要素の位置を決める関数 function movePosition(pageX, pageY) { boxDOM.style.left = pageX + 'px'; boxDOM.style.top = pageY + 'px'; } // マウスを動かした時の処理 function mouseMove(event) { movePosition(event.pageX, event.pageY); } document.addEventListener('mousemove', mouseMove); }; boxDOM.ondragstart = function() { return false; };
2

マウスを離した時の動作を追加する

①のままだとマウスを離しても#box要素がついて来てしまうのでそれを解決します。
追記行は背景色を変えています。

mouseupイベントでマウスを離した時にmousemoveイベントを無効にします。(23行目)

JavaScript
コピー
const boxDOM = document.querySelector('#box'); boxDOM.onmousedown = function(event) { boxDOM.style.position = 'absolute'; boxDOM.style.zIndex = 1000; movePosition(event.pageX, event.pageY); // #box要素の位置を決める function movePosition(pageX, pageY) { boxDOM.style.left = pageX + 'px'; boxDOM.style.top = pageY + 'px'; } // マウスを動かした時の処理 function mouseMove(event) { movePosition(event.pageX, event.pageY); } document.addEventListener('mousemove', mouseMove); // マウスを離した時にmousemoveイベントを解除する document.onmouseup = function() { document.removeEventListener('mousemove', mouseMove); }; }; boxDOM.ondragstart = function() { return false; };

これでドラッグ&ドロップ自体はできたかと思います。

3

気になる部分の調整

ちょっと気になるところを調整していきましょう。
追記行は背景色を変えています。

気になるところ
  • #box要素をドラッグすると必ず左上を掴んでしまうのでドラッグを開始した位置で掴みたい。 ※1 説明
  • #droparea要素にドラッグした時にbackground-color: #969696;にしたい。 ※2 説明
  • ・ドラッグ中はカーソルをcursor: grabbing;にしたい。

※1 説明
マウスを押した時にカーソルから#box要素の左上の部分までの距離を求めます。(10、11行目)
20、21行目で先ほど求めた値を引くことでドラッグを開始した位置で掴むことができます。

ドラッグを開始した位置で掴むの図
               

※2 説明
まず、ドロップエリアに入ったか判定するためにdropJudge変数にnullを入れておきます。(1行目)

そして、カーソルがある位置の最上部要素(今回の場合は.demo要素)を取得します。(29行目)
elementFromPointメソッドは対象要素がなければnullを返します。

それから、最上部要素(.demo要素)の子要素である#droparea要素を指定します。(32行目)
これで、カーソルが#droparea要素の上になければnullを返すようになります。

あとは、if(33~41行目)#droparea要素の上にカーソルがあるかないかを判定することで#droparea要素をbackground-color: #969696;にすることができます。

JavaScript
コピー
let dropJudge = null; const boxDOM = document.querySelector('#box'); const dropareaDOM = document.querySelector('#droparea'); boxDOM.onmousedown = function(event) { boxDOM.style.cursor = 'grabbing'; let shiftX = event.clientX - boxDOM.getBoundingClientRect().left; let shiftY = event.clientY - boxDOM.getBoundingClientRect().top; boxDOM.style.position = 'absolute'; boxDOM.style.zIndex = 1000; movePosition(event.pageX, event.pageY); // #box要素の位置を決める function movePosition(pageX, pageY) { boxDOM.style.left = pageX - shiftX + 'px'; boxDOM.style.top = pageY - shiftY + 'px'; } // マウスを動かした時の処理 function mouseMove(event) { movePosition(event.pageX, event.pageY); boxDOM.hidden = true; let elemBelow = document.elementFromPoint(event.clientX, event.clientY); boxDOM.hidden = false; let droppableBelow = elemBelow.closest('#droparea'); if (dropJudge != droppableBelow) { if (dropJudge) { notDroppable(dropJudge); } dropJudge = droppableBelow; if (dropJudge) { onDroppable(dropJudge); } } } document.addEventListener('mousemove', mouseMove); // マウスを離した時にmousemoveイベントを解除する document.onmouseup = function() { document.removeEventListener('mousemove', mouseMove); boxDOM.style.cursor = 'grab'; }; }; function onDroppable(element) { element.style.background = '#969696'; } function notDroppable(element) { element.style.background = '#e6e6e6'; } boxDOM.ondragstart = function() { return false; };
4

完成

これでマウスイベントでドラッグ&ドロップ機能の基本はできたかと思います。
完成コードはこちらに他になります。

HTML
コピー
<div class="demo"> <div id="droparea" class="droppable"></div> <div id="box"></div> </div>
CSS
コピー
.demo{ background-color: #f0f0f0; box-sizing: border-box; border: 1px solid #646464; padding: 10px; width: 100%; height: 300px; position: relative; } #droparea { background-color: #e6e6e6; box-sizing: border-box; border: 1px dotted #646464; width: 50%; height: 100%; } #box { cursor: grab; background-color: #000; width: 40px; height: 40px; position: absolute; top: 50%; left: 70%; }
JavaScript
コピー
let dropJudge = null; const boxDOM = document.querySelector('#box'); const dropareaDOM = document.querySelector('#droparea'); boxDOM.onmousedown = function(event) { boxDOM.style.cursor = 'grabbing'; let shiftX = event.clientX - boxDOM.getBoundingClientRect().left; let shiftY = event.clientY - boxDOM.getBoundingClientRect().top; boxDOM.style.position = 'absolute'; boxDOM.style.zIndex = 1000; movePosition(event.pageX, event.pageY); // #box要素の位置を決める function movePosition(pageX, pageY) { boxDOM.style.left = pageX - shiftX + 'px'; boxDOM.style.top = pageY - shiftY + 'px'; } // マウスを動かした時の処理 function mouseMove(event) { movePosition(event.pageX, event.pageY); boxDOM.hidden = true; let elemBelow = document.elementFromPoint(event.clientX, event.clientY); boxDOM.hidden = false; let droppableBelow = elemBelow.closest('#droparea'); if (dropJudge != droppableBelow) { if (dropJudge) { notDroppable(dropJudge); } dropJudge = droppableBelow; if (dropJudge) { onDroppable(dropJudge); } } } document.addEventListener('mousemove', mouseMove); // マウスを離した時にmousemoveイベントを解除する document.onmouseup = function() { document.removeEventListener('mousemove', mouseMove); boxDOM.style.cursor = 'grab'; }; }; function onDroppable(element) { element.style.background = '#969696'; } function notDroppable(element) { element.style.background = '#e6e6e6'; } boxDOM.ondragstart = function() { return false; };

おわり

かけちまる
かけちまる
Webエンジニアをしています。
HTML/CSS/JavaScript/jQuery/PHPができます。
WEB制作を中心に日々学びになったこと、興味が沸いたことについて初心者の方でもわかりやすいようにアウトプットしていくブログです。

関連記事

画像形式をWebPに変換するツール6選

画像形式をWebPに変換するツール6選

【macOS】よく使うターミナルコマンド一覧

【macOS】よく使うターミナルコマンド一覧

clamp関数がSafariで効かない時の対処法

clamp関数がSafariで効かない時の対処法

【初心者向け】CodePenの使い方からサイト埋め込み方法

【初心者向け】CodePenの使い方からサイト埋め込み方法

Contact form 7「サイトのドメインに属していないメールアドレスが送信元に設定されています」の理由と対処法

Contact form 7「サイトのドメインに属していないメールアドレスが送信元に設定されています」の理由と対処法

【videoタグ】HTMLでWebサイトに動画を埋め込む

【videoタグ】HTMLでWebサイトに動画を埋め込む