一、前言:
最近要實現一個元素拖放效果,鼠標拖動元素並且定位元素,首先想到的是HTML5中的拖放,在HTML5中,有一個draggable屬性,且有dragstart, dragover, drop等事件,主要是通過event.dataTransfer對象方法,在dragstart事件中設置數據event.dataTransfer.setData(); 在drop事件中獲取數據event.dataTransfer.getData();但是它並不能實現元素的實時拖放效果,因此還是需要用鼠標事件來模擬元素的拖放。
二、實例示圖
三、實現原理:
1、思路:鼠標依次觸發mousedown, mousemove, mouseup事件,在mousemove事件中實時計算元素新位置並且定位元素,
在mouseup事件中注銷mousemove,mouseup事件。
2、重點:如果所有事件都綁定在拖動元素上,當鼠標移動速度很快,以至於離開了拖動的元素,那么就不會執行mousemove,
mouseup事件處理程序,因此要想讓mousemove,mouseup事件處理實時執行,必須將它們綁定到document元素上;
四、插件源碼:
$.fn.extend({ /** * * 擴展jQuery原型,實現鼠標事件模擬元素拖動 * drag中的回調函數this指向被拖動元素 * @ method: drag * @ use: $( selector ).drag( dragStart, dragMove, dragEnd ) * @ param { function } 第一個參數,准備拖動處理函數 * @ param { function } 第二個參數,拖動中處理函數 * @ param { function } 第三個參數,拖動結束處理函數 * @ reutrn { jQuery( selector ) } * */ drag: function( dragStart, dragMove, dragEnd ) { function drag( dragElem, event ) { var offsetX, offsetY, beforePageX, beforePageY; if ( $.isFunction(dragStart) ) { dragStart.apply(dragElem, arguments); } // 移動前或移動中的元素位置 offsetX = parseInt( $(dragElem).css('left'), 10 ); offsetY = parseInt( $(dragElem).css('top'), 10 ); // 移動前或移動中的鼠標位置 beforePageX = event.clientX; beforePageY = event.clientY; if ( document.addEventListener ) { document.addEventListener('mousemove', moveHandle, false); document.addEventListener('mouseup', upHandle, false); } else if ( document.attachEvent ) { dragElem.setCapture(); // 將dragElem鼠標事件繼承到文檔進行捕獲 dragElem.attachEvent('onmousemove', moveHandle); dragElem.attachEvent('onmouseup', upHandle); dragElem.attachEvent('onlosecapture', upHandle); } // 鼠標移動事件處理 function moveHandle (event) { var event = event || window.event; // 更新移動中或移動終止后的元素位置 var x = offsetX + event.clientX - beforePageX; var y = offsetY + event.clientY - beforePageY; $(dragElem).css({ left: x + 'px', top: y + 'px' }); // 阻止事件傳播 if ( event.stopPropagation ){ event.stopPropagation(); } else if ( event.cancleBubble ) { event.cancleBubble = true; } if ( $.isFunction(dragMove) ) { dragMove.apply(dragElem, arguments); } } // 鼠標彈起事件處理 function upHandle (event) { if ( document.addEventListener ) { document.removeEventListener('mousemove', moveHandle, false); document.removeEventListener('mouseup', upHandle, false); } else if ( document.detachEvent ) { dragElem.detachEvent('onlosecapture', upHandle); dragElem.detachEvent('onmouseup', upHandle); dragElem.detachEvent('onmousemove', moveHandle); dragElem.releaseCapture(); } if ( event.stopPropagation ){ event.stopPropagation(); } else if ( event.cancleBubble ) { event.cancleBubble = true; } if ( $.isFunction(dragEnd) ) { dragEnd.apply(dragElem, arguments); } } } $(this).each(function(){ $(this).bind('mousedown', function(e){ var dragElem = this, event = e; drag(dragElem, event); }); }); return this; } });
五、調用實例:
(function(){ var dragEnd = false; $('.drag-elem').drag( function(){ $(this).text('准備拖動').css({ zIndex: 2 }).siblings().css({ zIndex: 1 }); }, function(){ var offset = $(this).offset(); dragEnd = true; $(this).text('拖動中(' + offset.left + ',' + offset.top + ')' ); }, function(){ if (dragEnd) { $(this).text('拖動結束'); dragEnd = false; } } ); }());
六、完整實例代碼

<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery drag</title> <script src="http://code.jquery.com/jquery-1.10.1.js"></script> <style> .drag-elem { position: absolute; left: 10px; top: 20px; z-index: 999; width: 200px; height: 50px; cursor: move; background-color: #ccc; border: 5px solid green; font-size: 24px; line-height: 50px; text-align: center; } </style> </head> <body> <div class="drag-elem"></div> <script> $.fn.extend({ /** * * 擴展jQuery原型,實現鼠標事件模擬元素拖動 * drag中的回調函數this指向被拖動元素 * @ method: drag * @ use: $( selector ).drag( dragStart, dragMove, dragEnd ) * @ param { function } 第一個參數,准備拖動處理函數 * @ param { function } 第二個參數,拖動中處理函數 * @ param { function } 第三個參數,拖動結束處理函數 * @ reutrn { jQuery( selector ) } * */ drag: function( dragStart, dragMove, dragEnd ) { function drag( dragElem, event ) { var offsetX, offsetY, beforePageX, beforePageY; if ( $.isFunction(dragStart) ) { dragStart.apply(dragElem, arguments); } // 移動前或移動中的元素位置 offsetX = parseInt( $(dragElem).css('left'), 10 ); offsetY = parseInt( $(dragElem).css('top'), 10 ); // 移動前或移動中的鼠標位置 beforePageX = event.clientX; beforePageY = event.clientY; if ( document.addEventListener ) { document.addEventListener('mousemove', moveHandle, false); document.addEventListener('mouseup', upHandle, false); } else if ( document.attachEvent ) { dragElem.setCapture(); // 將dragElem鼠標事件繼承到文檔進行捕獲 dragElem.attachEvent('onmousemove', moveHandle); dragElem.attachEvent('onmouseup', upHandle); dragElem.attachEvent('onlosecapture', upHandle); } // 鼠標移動事件處理 function moveHandle (event) { var event = event || window.event; // 更新移動中或移動終止后的元素位置 var x = offsetX + event.clientX - beforePageX; var y = offsetY + event.clientY - beforePageY; $(dragElem).css({ left: x + 'px', top: y + 'px' }); // 阻止事件傳播 if ( event.stopPropagation ){ event.stopPropagation(); } else if ( event.cancleBubble ) { event.cancleBubble = true; } if ( $.isFunction(dragMove) ) { dragMove.apply(dragElem, arguments); } } // 鼠標彈起事件處理 function upHandle (event) { if ( document.addEventListener ) { document.removeEventListener('mousemove', moveHandle, false); document.removeEventListener('mouseup', upHandle, false); } else if ( document.detachEvent ) { dragElem.detachEvent('onlosecapture', upHandle); dragElem.detachEvent('onmouseup', upHandle); dragElem.detachEvent('onmousemove', moveHandle); dragElem.releaseCapture(); } if ( event.stopPropagation ){ event.stopPropagation(); } else if ( event.cancleBubble ) { event.cancleBubble = true; } if ( $.isFunction(dragEnd) ) { dragEnd.apply(dragElem, arguments); } } } $(this).each(function(){ $(this).bind('mousedown', function(e){ var dragElem = this, event = e; drag(dragElem, event); }); }); return this; } }); </script> <script> (function(){ var dragEnd = false; $('.drag-elem').drag( function(){ $(this).text('准備拖動').css({ zIndex: 2 }).siblings().css({ zIndex: 1 }); }, function(){ var offset = $(this).offset(); dragEnd = true; $(this).text('拖動中(' + offset.left + ',' + offset.top + ')' ); }, function(){ if (dragEnd) { $(this).text('拖動結束'); dragEnd = false; } } ); }()); </script> </body> </html>
源碼更新 2014/02/19

$.fn.extend({ /** * * 擴展jQuery原型,實現鼠標事件模擬元素拖動 * drag中的回調函數this指向被拖動元素 * @ method: drag * @ use: $( selector ).drag( dragStartFn, dragMoveFn, dragEndFn ) * @ param { function } 第一個參數,准備拖動處理函數 * @ param { function } 第二個參數,拖動中處理函數 * @ param { function } 第三個參數,拖動結束處理函數 * @ reutrn { jQuery( selector ) } * */ drag: function( dragStartFn, dragMoveFn, dragEndFn ) { function drag( target, e ) { var offsetX, offsetY, beforePageX, beforePageY; if ( $.isFunction(dragStartFn) ) { dragStartFn.apply(target, arguments); } // 移動前或移動中的元素位置 offsetX = parseInt( $(target).css('left'), 10 ) || 0; offsetY = parseInt( $(target).css('top'), 10 ) || 0; // 移動前或移動中的鼠標位置 beforePageX = e.clientX; beforePageY = e.clientY; $(document).bind('mousemove', moveHandle) .bind('mouseup', upHandle); // 鼠標移動事件處理 function moveHandle (e) { // 更新移動中或移動終止后的元素位置 var x = offsetX + e.clientX - beforePageX; var y = offsetY + e.clientY - beforePageY; $(target).css({ left: x + 'px', top: y + 'px' }); if ( $.isFunction(dragMoveFn) ) { dragMoveFn.apply(target, arguments); } // 阻止瀏覽器默認行為(鼠標在拖動圖片一小段距離,會出現一個禁止的小提示,即:圖片不能再拖動) e.preventDefault(); } // 鼠標彈起事件處理 function upHandle (e) { $(document).unbind('mousemove', moveHandle) .unbind('mouseup', upHandle); if ( $.isFunction(dragEndFn) ) { dragEndFn.apply(target, arguments); } } } $(this).each(function(){ $(this).bind('mousedown', function(e){ drag(this, e); }); }); return this; } }); // 調用實例 (function(){ var dragEnd = false; $('div').drag( function(){ $(this).html('<span>准備拖動</span>').css({ zIndex: 2 }).siblings().css({ zIndex: 1 }); }, function(){ var offset = $(this).offset(); $(this).html('<span>拖動中(' + offset.left + ',' + offset.top + ')</span>' ); }, function(){ $(this).html('<span>拖動結束</span>') } ); $('img').drag(); }());