任意のDOM要素同士 を ドラッグ&ドロップ(後編)

0 件のコメント

前編からの続き。 後編では、実際にドラッグ&ドロップのプログラムを作ってみる。 一応、jQueryを使う前提で。

ソースコード

ドラッグ機能を有効にするスクリプト。

var g = g || {};
g.ui = g.ui || {};

/**
* @public
* @constructor
* @class
*/
g.ui.Draggable = function (element) {
    this.element = element;
    this.cloneNode = null;
    this.initialize();
};

/**
* フラグとして利用する static 変数
*/
g.ui.Draggable.source = null;

/**
* 初期化処理
*/
g.ui.Draggable.prototype.initialize = function () {
    var self = this;
    var element = this.element;
    
    $(element).on('mousedown', function (event) {
        self.onMouseDown_sourceNode(event);
    });
};

/**
* mousedown イベント 発生時に呼ばれる
*/
g.ui.Draggable.prototype.onMouseDown_sourceNode = function (event) {
    var self = this;
    var onMouseMoveCallBack, onMouseUpCallBack;
    var cloneNode, offset, style;
    
    // Mouse move call back function.
    onMouseMoveCallBack = function (event) {
        self.onMouseMove_document(event);
    };
    
    // Mouse up call back function.
    onMouseUpCallBack = function (event) {
        // Detach event handler.
        $(document).off(
            'mousemove', onMouseMoveCallBack
        ).off(
            'mouseup', onMouseUpCallBack
        );
        self.onMouseUp_document(event);
    };
    
    // Attach event handler.
    $(document).on(
        'mousemove', onMouseMoveCallBack
    ).on(
        'mouseup', onMouseUpCallBack
    );
    
    // Create clone DOM node.
    cloneNode = this.element.cloneNode(true);
    cloneNode.style.position = 'absolute';
    $(cloneNode).addClass('draggable-clone');
    window.document.body.appendChild(cloneNode);
    this.cloneNode = cloneNode;
    
    // Add effect to the original DOMElement.
    $(this.element).addClass('draggable-source-dragstart');
    
    offset = {  // for FF
        x: event.pageX - event.target.offsetLeft,
        y: event.pageY - event.target.offsetTop
    }
    style = cloneNode.style;
    style.left = (event.pageX - offset.x) + 'px';
    style.top = (event.pageY - offset.y) + 'px';
    this.offset = offset;
    
    // Set flag.
    g.ui.Draggable.dragging = this.element;
};

/**
* mousemove イベント 発生時に呼ばれる
*/
g.ui.Draggable.prototype.onMouseMove_document = function (event) {
    var style = this.cloneNode.style;
    var offset = this.offset;
    style.top = (event.pageY - offset.y) + 'px';
    style.left = (event.pageX - offset.x) + 'px';
};

/**
* museup イベント 発生時に呼ばれる
*/
g.ui.Draggable.prototype.onMouseUp_document = function (event) {
    window.document.body.removeChild(this.cloneNode);
    this.cloneNode = null;
    
    $(this.element).removeClass('draggable-source-dragstart');
    
    // 後始末用にイベントを追加
    $(document).on('mouseover', function (event) {
        $(document).off('mouseover', arguments.callee);
        
        // 後始末
        g.ui.Draggable.source = null;
    });
    
    // ドロップしたフラグを設定。
    g.ui.Draggable.source = this.element;
};

ドロップ機能を有効にするスクリプト。

var g = g || {};
g.ui = g.ui || {};


/**
* @public
* @constructor
* @class
*/
g.ui.Droppable = function (element) {
    this.element = element;
    this.onDragDrop = null;
    this.initialize();
};

/**
* 初期化処理
*/
g.ui.Droppable.prototype.initialize = function () {
    var self = this;
    
    $(this.element).on('mouseover', function (event) {
        self.onMouseOver_targetNode(event);
    });
};

/**
* mouseover イベント 発生時に呼ばれる
*/
g.ui.Droppable.prototype.onMouseOver_targetNode = function (event) {
    if (!g.ui.Draggable.source) {
        return;
    }
    
    // ドラッグ&ドロップイベントを実行
    var source = g.ui.Draggable.source;
    var target = this.element;
    if (this.onDragDrop) {
        this.onDragDrop.call(this, source, target);
    }
    
    // 後始末
    g.ui.Draggable.source = null;
};

サンプル

HTMLを含めたソースコード。

<!DOCTYPE html>
<html>
<head>
    <title>g.ui.DragDrop</title>
    <link type="text/css" rel="stylesheet" href="default.css" />
    <link type="text/css" rel="stylesheet" href="DragDrop.css" />
    <style>
body
{
 cursor: default;
}
.dragSource
{
 width: 100px; height: 100px;
 background-color: #99ff99;
 border: 1px solid #33aa33;
 float: left;
 text-align: center;
 line-height: 100px;
}
.dropTarget
{
 width: 100px; height: 100px;
 background-color: #9999ff;
 border: 1px solid #3333aa;
 float: left;
 text-align: center;
 line-height: 100px;
}
    </style>
    <script type="text/javascript" src="./third_party/jquery-1.7.2.js"></script>
    <script type="text/javascript" src="DragDrop.js"></script>
    <script type="text/javascript">
$(window).ready(function (event) {
 var text = window.document.getElementById('text');
 var source = window.document.getElementById('srcelm');
 var target = window.document.getElementById('trgelm');
 var draggable, droppable;
 draggable = new g.ui.Draggable(source);
 droppable = new g.ui.Droppable(target);
 droppable.onDragDrop = function (source, target) {
  text.value += '[dragdrop] ' + source.id + ' -> ' + target.id + '\r\n';
 };
 
 var btn = window.document.getElementById('btn');
 $(btn).on('click', function(event) {
  text.value = '';
 });
});
    </script>
</head>
<body>

<div id="srcelm" class="dragSource">source</div>

<div id="trgelm" class="dropTarget">target</div>

<div style="clear: both;"><input id="btn" type="button" value="reset" /></div>
<textarea id="text" style="width:500px; height:200px;"></textarea>


</body>
</html>

DOM要素が重なったからといって、イベントが発生する訳じゃない。。 ドロップしたときマウスポインタがドロップ先要素の上にあったときイベントが発生する。 良いか、悪いか微妙な仕様だけど…要素同士の重なりを計算しようとすると 結構な量になるし、大量の要素同士をドラッグ&ドロップには向かないだろうから… とりあえず、今回はこの仕様で^^;