IT the Best

はてなブログのカスタマイズ情報を中心に、WEBデザインからJavaScriptまでWEB系の開発情報を発信します。便利ツールや暇つぶしのゲームなど開発物も公開します。

【JS】ドラッグアンドドロップでHTML要素の位置を移動させる | jQuery | JavaScript ... jQuery拡張(プラグイン)

Photo by Yoav Aziz on Unsplash

Photo by Yoav Aziz on Unsplash

更新情報
2020/06/30 コードの微修正をしました。

 

当ScriptではjQueryを使用しています。jQueryの設置方法は「jQueryの設定方法 - jQueryを使用するまでの手順 |【jQuery】 - IT the Best」をご覧ください。

ドラッグでHTML要素の位置を移動させる

mousemove(touchmove)イベントを使って、HTML要素のドラッグによる位置の移動を実装します。
※ブラウザによっては正常に動作しない可能性があります。

 

移動可能にする

var device = navigator.userAgent.match(/iphone|ipad|ipod|android/i) ? "sp" : "pc"; // デバイス確認 
// デバイス別のイベント名を格納
var touchstart = device == "sp" ? "touchstart" : "mousedown";
var touchend = device == "sp" ? "touchend" : "mouseup";
var touchmove = device == "sp" ? "touchmove" : "mousemove";
/** @type {JQuery} */
var target=$(element); // 動かす対象
target.on(touchstart, function (event) {
    if($(event.target).hasClass("immovable")){return} // 移動をさせないクラスが存在したら処理をさせない
    event.preventDefault();
    // 位置取得 デバイスに応じて取得対象を変える
    let x = device == "sp" ? event.touches[0].clientX : event.clientX; 
    let y = device == "sp" ? event.touches[0].clientY : event.clientY; 
    $(this).data({  // タップ対象の移動に必要なデータを設定
        "start": true, "move": false,  // flag
        "initialX": x, "initialY": y,  // タップ位置
        "top": this.offsetTop,"left": this.offsetLeft  // 要素の初期位置
    }); 
})
target.click(function (event) { // move後のリンク無効化
    if ($(this).data("move")) {  
        $(this).data("move",false);
        return false
    }
});
// カーソルの移動イベントは、動かす要素ではなくdocument(ページ)全体に設定する
$(document).on(touchmove, (event) => { // move event
    if ($(target).data("start")) {  // targetがタップされていたら
        event.preventDefault();
        let x = device == "sp" ? event.touches[0].clientX : event.clientX;
        let y = device == "sp" ? event.touches[0].clientY : event.clientY;
        let moveX = x - target.data("initialX"); // ドラッグ距離 X
        let moveY = y - target.data("initialY"); // ドラッグ距離 Y
        if (moveX !== 0 || moveY !== 0) { // ドラッグ距離が0以外だったら
            target.data("move", true).css("position", "absolute");  // moveフラグを設定
            // 移動を可能にするためのスタイルを設定
        }
        // 初期位置にドラッグ距離を加算して移動させる
        target[0].style.top = $(target).data("top") + moveY + "px";
        target[0].style.left = $(target).data("left") + moveX + "px";
    }
}).on(touchend, function (event) {
    if ($(target).data("start")) { // targetがタップされていたら
        $(target).data("start", false);
    }
})

 

移動不可能にする(クラスの切り替え)

$(element).toggleClass("immovable");

 

 

jQuery 拡張版

jQuery.prototype.movable = function (startFunc, moveFunc, endFunc) {
    let device = navigator.userAgent.match(/iphone|ipad|ipod|android/i) ? "sp" : "pc"; // デバイス確認 
    // デバイス別のイベント名を格納
    let touchstart = device == "sp" ? "touchstart" : "mousedown";
    let touchend = device == "sp" ? "touchend" : "mouseup";
    let touchmove = device == "sp" ? "touchmove" : "mousemove";
    /** @type {JQuery} */
    let target; // 動かす対象
    $(this).each(function (i, e) {
        $(e).on(touchstart, function (event) {
            if ($(e).hasClass("immovable")) { return } // 移動をさせないクラスが存在したら処理をさせない
            event.preventDefault();
            target = $(e); // 動かす対象
            // 位置取得 デバイスに応じて取得対象を変える
            let x = device == "sp" ? event.touches[0].clientX : event.clientX;
            let y = device == "sp" ? event.touches[0].clientY : event.clientY;
            $(this).data({  // タップ対象の移動に必要なデータを設定
                "start": true, "move": false, // flag
                "initialX": x, "initialY": y, // タップ位置
                "top": this.offsetTop, "left": this.offsetLeft // 要素の初期位置
            }); 
            if (startFunc) {  // タップ時の処理
                startFunc(event,target);
            }
        })
        $(e).click(function (event) { // move後のリンク無効化
            if ($(this).data("move")) {
                $(this).data("move", false);
                return false
            }
        });
    })
    // カーソルの移動イベントは、動かす要素ではなくdocument(ページ)全体に設定する
    $(document).on(touchmove, (event) => { // move event
        if ($(target).data("start")) {  // targetがタップされていたら
            event.preventDefault();
            let x = device == "sp" ? event.touches[0].clientX : event.clientX;
            let y = device == "sp" ? event.touches[0].clientY : event.clientY;
            let moveX = x - target.data("initialX"); // ドラッグ距離 X
            let moveY = y - target.data("initialY"); // ドラッグ距離 Y
            if (moveX !== 0 || moveY !== 0) {  // ドラッグ距離が0以外だったら
                target.data("move", true).css("position", "absolute");  // moveフラグを設定
                // 移動を可能にするためのスタイルを設定
            }
            // 初期位置にドラッグ距離を加算して移動させる
            target[0].style.top = target.data("top") + moveY + "px";
            target[0].style.left = target.data("left") + moveX + "px";
            if (moveFunc) {  // 移動時の処理
                moveFunc(event, target);
            }
        }
    }).on(touchend, function (event) {
        if ($(target).data("start")) { // targetがタップされていたら
            target.data("start", false);
            if (target.data("move")) { // 移動終了後の処理
                if (endFunc) {
                    endFunc(event, target);
                }
            }
        }
    })
    return this
}

jQuery.prototype.switchMovable = function () {
    this.toggleClass("immovable");
}

 

要素の移動処理をjQueryのメソッドとして拡張し、引数にはタップ開始時移動時移動終了時のコールバック関数を用意しました。

 

使用例

要素(id="move-me")の移動 HTML CSS JS
移動させてください
移動可 
<div id="move-me" style="padding: 50px; background: skyblue;">移動させてください</div>
移動可<switch id="aaa-switch-movable" class="on"></switch>
 
$("switch#aaa-switch-movable").click((e)=>{
    $(e.target).toggleClass("on");
    $("#move-me").switchMovable();
})
$("#move-me").movable(function(e,target){ // タップ時の処理
    target.css("border","1px solid black");
},function(e,target){  // 移動時の処理
    target.css("box-shadow","0 0 10px 0 #000");
},function(e,target){  // 移動終了時の処理
    target.css({
        "border":"0",
        "box-shadow":"unset",
        "position":"unset","top":0,"left":0
    });
})

 

 

 

補足

  • move後のクリック無効は、リンクなどが設定されている要素の移動後のイベント(リンク先に飛ぶなど)を発生させないためです。

 

更新情報

  • position:absoluteの設定するタイミングを移動を検知したときに変更。