LoginSignup
795
791

「JavaScriptで要素をドラッグして移動する簡単な方法」という記事が初耳だらけだった件

Posted at

はじめに

まず↓の記事を見てない方はぜひ見てください!

自分にとってはこの記事には「えっ、ナニコレ!」なテクニックが多く、特に解説もなかったのでいろいろ調べてたら休日が消えてました...

なのでその時間の供養もかねて、自分が知らなかった部分を中心に、僭越ながら元記事の解説を書いてみたいと思います。

ちなみに、以下が元記事のコードそのままを実装したものです。たしかに掲載コードだけで要素がグリグリ動きますね。

See the Pen js-drag-move-original by www-tacos (@www-tacos) on CodePen.

初耳1: $img

まずコードのここ

<img id="$img" src="https://js.cx/clipart/ball.svg" width="40" height="40">
<script>
$img.onpointermove = function(event){

$ってことはjQueryか?でも使ってる様子ないよな...なんで要素オブジェクトを参照できてるんだ?$に特別な仕様でもあるのか...?」

調べてみると、実は単純な話で、HTML内でIDをつけた要素はグローバル変数に格納される仕様とのことでした。(2014年の記事...)

なので名前は別になんでもよく、またより正確に参照を書くとこうなるわけですね。

<img id="ballSvg" src="https://js.cx/clipart/ball.svg" width="40" height="40">
<script>
window.ballSvg.onpointermove = function(event){

初耳2: event.buttons

次にコードのここ

$img.onpointermove = function(event){
    if(event.buttons){

「PointerEventのプロパティにボタン?押下状態かな?」

実際に調べてみるとその通りで、どのボタンを押しているかの状態を持つプロパティでした。

以下の表は上記ドキュメントの引用ですが、左クリックや右クリックも識別できるみたいです。

機器のボタンの状態 button buttons
最後のイベント以降、
ボタンもタッチ/ペンの接触も変化がなかった
-1
ボタンを押さずにマウスを動かした
ボタンを押さずにホバー中にペンを動かした
0
マウスの左ボタン、タッチ接触、ペン接触 0 1
マウスの中ボタン 1 4
マウスの右ボタン、ペンのバレルボタン 2 2
マウスの X1 (戻る)ボタン 3 8
マウスの X2 (進む)ボタン 4 16
ペンの消しゴムボタン 5 32

元記事だとそのまま条件に渡しているので、event.buttonsが0(ボタン押下してない状態)以外で後続の処理が動くようになってます。

なのでマウスの右ボタンやホイールボタンでもドラッグできます。

また、いつ押されたかは関係ないので事前に左ボタンを押下した状態でマウスを重ねに行くことでもドラッグできますね。
※ここを動作として切り分けたいなら、やはりpointerdownで開始地点を監視する必要がありそうです

初耳3: setPointerCapture

最後にコードのここ

        this.setPointerCapture(event.pointerId)

これはもう特に予想とかしないですぐにググりましたw

重要なのはここ

ポインターデバイスの接触が要素から外れた場合でも、(スクロールやパンなどで)要素がポインターイベントを受信し続けるようにするために使用できます。

つまりマウスをグリグリ動かしてマウスがimg要素から離れてもpointermoveイベントを発生させ続けることができるわけです。

ここで気になるのが

「そのキャプチャはいつ解除されるの?永遠につかみ続けない?」

という点ですが、キャプチャはreleasePointerCaptureメソッドを呼べば明示的に解除することができ、またpointerupイベントが発生した時も自動で解除されるようです。

また、

「じゃあキャプチャしなかったらどうなるの?」

と思う人もいるかもしれません。なのでsetPointerCaptureをコメントアウトしたバージョンも用意しました。

See the Pen js-drag-move-no-capture by www-tacos (@www-tacos) on CodePen.

ボールをドラッグしたあとにマウスを素早く動かしてボールの外に出すと追従が止まると思います。

その他の知ってたテクニック

上記以外はだいたい知ってたテクニックですが、知識の整理もかねて軽く説明してみます。

onpointermove

まずここ

$img.onpointermove = function(event){

pointermoveイベントは要素の上でマウスカーソルやスマホのタッチ位置が動いたタイミングで発火されるPointerEvent型のイベントです。

最近はPointerEvent型を使っておけば問題ないと思ってますが、ほかにもMouseEvent型とTouchEvent型があるので、古い記事やドキュメントを読むときに読み替えたり、場合によっては使い分けたりするといいかも?です。

style

次にここ

        this.style.left     = this.offsetLeft + event.movementX + 'px'
        this.style.top      = this.offsetTop  + event.movementY + 'px'
        this.style.position = 'absolute'

これは要素の位置を元の位置からマウスの動き分だけ移動させる処理ですね。

this.offsetLeft(Top)は親要素に対するimg要素の相対位置、event.movementX(Y)はマウスの移動量です。

this.style.position = 'absolute' はCSSで position: absolute; を指定するのと同じ意味で、position: relative;な親要素に対する相対座標で位置を指定するという設定です。

draggable

最後にここ

        this.draggable      = false

draggableは要素がドラッグ可能かどうかを示す属性です。

上記ドキュメントに

この属性が設定されなかった場合の既定値は auto であり、ブラウザーの既定のドラッグ動作であることを意味します。テキストの選択範囲、画像、リンクのみがドラッグ可能です。

とあるように画像はデフォルトでdraggable=trueなので、そのままだとドラッグ操作をしたときにブラウザのデフォルトの挙動をとってしまいます。(半透明の画像がマウスカーソル上に表示され、例えばデスクトップで離すとデスクトップに画像がダウンロードされる)

なので明示的にfalseを設定しているというわけです。

さいごに

いかがでしたか?

個人的には元記事で解説もしてほしかったなぁ~と思いつつ、ないなら自分で書くか!という気持ちで書かせてもらいました。
この記事が自分と同じような疑問を持った方の助けになれたら幸いです。

記事は以上になります。最後まで読んでくださりありがとうございます。

795
791
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
795
791