譯者: Flyingis
終于完成了全文的翻譯,由于時間比較參促,文章沒有過多的校正與潤色,閱讀過程中難免會有些許生硬或不準(zhǔn)確的感覺,請大家見量并指出,方便他人閱讀。
原文作者將拖放功能的實(shí)現(xiàn)分步講解,其核心的地方在于移動和放置元素時,鼠標(biāo)、移動元素、目標(biāo)元素關(guān)系的處理,只要這個問題處理好了,代碼就很容易理解,譯文僅供輔助參考之用。
整合所有的功能
最后我們使用所有代碼片斷,來創(chuàng)建一個完整的拖放函數(shù)腳本。我們所要做的第一件事情是DOM操作,如果你對此并不十分熟悉,可以閱讀《 JavaScript Primer on DOM Manipulation 》。
接下來的代碼創(chuàng)建容器和容器組,使得在這些容器中可以拖動每個元素,這在本文第二個demo的基礎(chǔ)上來完成。這段代碼能夠用來重新規(guī)劃元素的順序,將導(dǎo)航窗口放在頁面的左側(cè)或右側(cè),或再加入你所能想到的其他的功能。
我們將使用偽代碼來一步步進(jìn)行講解,將真實(shí)的代碼通過注釋的方式留給讀者查看。
1.當(dāng)文檔第一次被加載時,我們創(chuàng)建一個名為dragHelper的DIV標(biāo)簽,當(dāng)我們開始移動一個元素的時候,dragHelper將成為一個隱藏元素,可以四處移動。真實(shí)的元素并不會被拖動,僅僅使用insertBefore和appendChild來移動。我們在開始的時候隱藏dragHelper。
2.我們創(chuàng)建mouseDown和mouseUp函數(shù)。起初,所有的這些函數(shù)都假設(shè)記錄了鼠標(biāo)按鈕的狀態(tài),以至于iMouseDown變量在鼠標(biāo)按下的時候?yàn)閠rue,沒有按下的時候?yàn)閒alse。
3.我們創(chuàng)建一個全局變量DragDrops,以及一個函數(shù)CreateDragContainer。DragDrops包含一組相互關(guān)聯(lián)的容器。傳入CreateDragContainer的任何變量(代表容器)被組織成一個新的集合,使元素能夠在這些容器間自由移動。通過setAttribute,CreateDragContainer函數(shù)同樣將各容器中的元素綁定在一起。
4.現(xiàn)在我們的代碼知道每個元素所在的集合,現(xiàn)在來看mouseMove函數(shù)。mouseMove函數(shù)首先設(shè)置了一個變量target,表示鼠標(biāo)下面的目標(biāo)元素,如果這個元素在集合(用getAttribute判斷)中就繼續(xù)下面操作:
4.1.首先,在必要的時候,我們運(yùn)行一個簡單的腳本來改變目標(biāo)元素的class屬性,這樣就創(chuàng)造了一個翻動的效果。
4.2.然后我們檢查鼠標(biāo)是否點(diǎn)擊(因?yàn)槲覀兊拇a已經(jīng)運(yùn)行到這里),如果事件發(fā)生:
4.2.1.設(shè)置變量curTarget為當(dāng)前元素。
4.2.2.記錄元素當(dāng)前在文檔中的位置,以便在需要的時候可以將它的值取回。
4.2.3.將當(dāng)前元素克隆到dragHelper,使得我們能夠移動元素的隱藏備份。
4.2.4.因?yàn)樵赿ragHelper中我們完全擁有了拖動元素的一個備份,這個元素會始終在鼠標(biāo)下,我們必須移除dragObj屬性,讓代碼知道dragObj已不在集合中。
4.2.5.我們快速記錄集合中每個元素當(dāng)前的位置、寬度和高度。當(dāng)元素第一次開始被拖動時,我們僅需做一次這種工作,否則每當(dāng)鼠標(biāo)移動的時候我們都必須做一次,甚至一秒內(nèi)幾百次。
4.3.如果鼠標(biāo)沒有點(diǎn)擊,要么我們和之前擁有同樣的目標(biāo)元素,要么沒有目標(biāo)元素,不論哪種情況我們都不會做任何事情。
5.現(xiàn)在我們檢查curTarget變量。curTarget應(yīng)該僅包含一個被拖動的對象,因此如果它存在,表示我們正在拖動一個元素:
5.1.移動隱藏DIV到鼠標(biāo),這個元素和文章前面所創(chuàng)建的元素一樣能夠被拖動。
5.2.然后我們檢查鼠標(biāo)是否存在于當(dāng)前集合中每個容器中。
5.2.1.如果鼠標(biāo)在某個容器中,我們檢查容器中的每個元素,查看我們正拖動的元素屬于哪個位置。
5.2.2.然后我們將所拖動的元素放置在容器中另一個元素的前面,或容器的最后位置。
5.2.3.最后我們確定元素可見。
6.剩下的事情就是捕獲mouseUp事件:
6.1.首先需要隱藏dragHelper:它不再被需要,因?yàn)槲覀儧]有拖動任何東西。
6.2.如果拖動的元素是可見的,它已經(jīng)存在于任何它所屬的容器中,所有工作已完成。
6.3.如果拖動的元素不可見,我們將它放回它原來所在的地方。
// iMouseDown represents the current mouse button state: up or down
/**/ /**/ /**/ /*
lMouseState represents the previous mouse button state so that we can
check for button clicks and button releases:

if(iMouseDown && !lMouseState) // button just clicked!
if(!iMouseDown && lMouseState) // button just released!
*/
var mouseOffset = null ;
var iMouseDown = false ;
var lMouseState = false ;
var dragObject = null ;

// Demo 0 variables
var DragDrops = [];
var curTarget = null ;
var lastTarget = null ;
var dragHelper = null ;
var tempDiv = null ;
var rootParent = null ;
var rootSibling = null ;


Number.prototype.NaN0 = function ()
{ return isNaN( this ) ? 0 : this ;}
function CreateDragContainer()
{

/**/ /**/ /**/ /*
Create a new "Container Instance" so that items from one "Set" can not
be dragged into items from another "Set"
*/
var cDrag = DragDrops.length;
DragDrops[cDrag] = [];


/**/ /**/ /**/ /*
Each item passed to this function should be a "container". Store each
of these items in our current container
*/
for ( var i = 0 ; i < arguments.length; i ++ )
{
var cObj = arguments[i];
DragDrops[cDrag].push(cObj);
cObj.setAttribute(‘DropObj‘, cDrag);


/**/ /**/ /**/ /*
Every top level item in these containers should be draggable. Do this
by setting the DragObj attribute on each item and then later checking
this attribute in the mouseMove function
*/
for ( var j = 0 ; j < cObj.childNodes.length; j ++ )
{

// Firefox puts in lots of #text nodesskip these
if (cObj.childNodes[j].nodeName == ‘#text‘) continue ;

cObj.childNodes[j].setAttribute(‘DragObj‘, cDrag);
}
}
}
function mouseMove(ev)
{
ev = ev || window.event;


/**/ /**/ /**/ /*
We are setting target to whatever item the mouse is currently on
Firefox uses event.target here, MSIE uses event.srcElement
*/
var target = ev.target || ev.srcElement;
var mousePos = mouseCoords(ev);

// mouseOut event - fires if the item the mouse is on has changed
if (lastTarget && (target !== lastTarget))
{
// reset the classname for the target element
var origClass = lastTarget.getAttribute(‘origClass‘);
if (origClass) lastTarget.className = origClass;
}
/**/ /**/ /**/ /*
dragObj is the grouping our item is in (set from the createDragContainer function).
if the item is not in a grouping we ignore it since it can‘t be dragged with this
script.
*/
var dragObj = target.getAttribute(‘DragObj‘);

// if the mouse was moved over an element that is draggable
if (dragObj != null )
{
// mouseOver event - Change the item‘s class if necessary
if (target != lastTarget)
{
var oClass = target.getAttribute(‘overClass‘);

if (oClass)
{
target.setAttribute(‘origClass‘, target.className);
target.className = oClass;
}
}
// if the user is just starting to drag the element
if (iMouseDown && ! lMouseState)
{
// mouseDown target
curTarget = target;

// Record the mouse x and y offset for the element
rootParent = curTarget.parentNode;
rootSibling = curTarget.nextSibling;

mouseOffset = getMouseOffset(target, ev);

// We remove anything that is in our dragHelper DIV so we can put a new item in it.
for ( var i = 0 ; i < dragHelper.childNodes.length; i ++ ) dragHelper.removeChild(dragHelper.childNodes[i]);

// Make a copy of the current item and put it in our drag helper.
dragHelper.appendChild(curTarget.cloneNode( true ));
dragHelper.style.display = ‘block‘;

// set the class on our helper DIV if necessary
var dragClass = curTarget.getAttribute(‘dragClass‘);

if (dragClass)
{
dragHelper.firstChild.className = dragClass;
}
// disable dragging from our helper DIV (it‘s already being dragged)
dragHelper.firstChild.removeAttribute(‘DragObj‘);


/**/ /**/ /**/ /*
Record the current position of all drag/drop targets related
to the element. We do this here so that we do not have to do
it on the general mouse move event which fires when the mouse
moves even 1 pixel. If we don‘t do this here the script
would run much slower.
*/
var dragConts = DragDrops[dragObj];


/**/ /**/ /**/ /*
first record the width/height of our drag item. Then hide it since
it is going to (potentially) be moved out of its parent.
*/
curTarget.setAttribute(‘startWidth‘, parseInt(curTarget.offsetWidth));
curTarget.setAttribute(‘startHeight‘, parseInt(curTarget.offsetHeight));
curTarget.style.display = ‘none‘;

// loop through each possible drop container
for ( var i = 0 ; i < dragConts.length; i ++ )
{

with (dragConts[i])
{
var pos = getPosition(dragConts[i]);


/**/ /**/ /**/ /*
save the width, height and position of each container.

Even though we are saving the width and height of each
container back to the container this is much faster because
we are saving the number and do not have to run through
any calculations again. Also, offsetHeight and offsetWidth
are both fairly slow. You would never normally notice any
performance hit from these two functions but our code is
going to be running hundreds of times each second so every
little bit helps!

Note that the biggest performance gain here, by far, comes
from not having to run through the getPosition function
hundreds of times.
*/
setAttribute(‘startWidth‘, parseInt(offsetWidth));
setAttribute(‘startHeight‘, parseInt(offsetHeight));
setAttribute(‘startLeft‘, pos.x);
setAttribute(‘startTop‘, pos.y);
}
// loop through each child element of each container
for ( var j = 0 ; j < dragConts[i].childNodes.length; j ++ )
{

with (dragConts[i].childNodes[j])
{
if ((nodeName == ‘#text‘) || (dragConts[i].childNodes[j] == curTarget)) continue ;

var pos = getPosition(dragConts[i].childNodes[j]);

// save the width, height and position of each element
setAttribute(‘startWidth‘, parseInt(offsetWidth));
setAttribute(‘startHeight‘, parseInt(offsetHeight));
setAttribute(‘startLeft‘, pos.x);
setAttribute(‘startTop‘, pos.y);
}
}
}
}
}
// If we get in here we are dragging something
if (curTarget)
{
// move our helper div to wherever the mouse is (adjusted by mouseOffset)
dragHelper.style.top = mousePos.y - mouseOffset.y;
dragHelper.style.left = mousePos.x - mouseOffset.x;

var dragConts = DragDrops[curTarget.getAttribute(‘DragObj‘)];
var activeCont = null ;

var xPos = mousePos.x - mouseOffset.x + (parseInt(curTarget.getAttribute(‘startWidth‘)) / 2 );
var yPos = mousePos.y - mouseOffset.y + (parseInt(curTarget.getAttribute(‘startHeight‘)) / 2 );

// check each drop container to see if our target object is "inside" the container
for ( var i = 0 ; i < dragConts.length; i ++ )
{

with (dragConts[i])
{
if (((getAttribute(‘startLeft‘)) < xPos) &&
((getAttribute(‘startTop‘)) < yPos) &&
((getAttribute(‘startLeft‘) + getAttribute(‘startWidth‘)) > xPos) &&
((getAttribute(‘startTop‘) + getAttribute(‘startHeight‘)) > yPos))
{


/**/ /**/ /**/ /*
our target is inside of our container so save the container into
the activeCont variable and then exit the loop since we no longer
need to check the rest of the containers
*/
activeCont = dragConts[i];

// exit the for loop
break ;
}
}
}
// Our target object is in one of our containers. Check to see where our div belongs
if (activeCont)
{
// beforeNode will hold the first node AFTER where our div belongs
var beforeNode = null ;

// loop through each child node (skipping text nodes).
for ( var i = activeCont.childNodes.length - 1 ; i >= 0 ; i -- )
{

with (activeCont.childNodes[i])
{
if (nodeName == ‘#text‘) continue ;

// if the current item is "After" the item being dragged
if (
curTarget != activeCont.childNodes[i] &&
((getAttribute(‘startLeft‘) + getAttribute(‘startWidth‘)) > xPos) &&
((getAttribute(‘startTop‘) + getAttribute(‘startHeight‘)) > yPos))
{
beforeNode = activeCont.childNodes[i];
}
}
}
// the item being dragged belongs before another item
if (beforeNode)
{

if (beforeNode != curTarget.nextSibling)
{
activeCont.insertBefore(curTarget, beforeNode);
}
// the item being dragged belongs at the end of the current container
} else
{

if ((curTarget.nextSibling) || (curTarget.parentNode != activeCont))
{
activeCont.appendChild(curTarget);
}
}
// make our drag item visible
if (curTarget.style.display != ‘‘)
{
curTarget.style.display = ‘‘;
}
} else
{

// our drag item is not in a container, so hide it.
if (curTarget.style.display != ‘none‘)
{
curTarget.style.display = ‘none‘;
}
}
}
// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;

// mouseMove target
lastTarget = target;

// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;

// this helps prevent items on the page from being highlighted while dragging
return false ;
}
function mouseUp(ev)
{

if (curTarget)
{
// hide our helper object - it is no longer needed
dragHelper.style.display = ‘none‘;

// if the drag item is invisible put it back where it was before moving it
if (curTarget.style.display == ‘none‘)
{

if (rootSibling)
{
rootParent.insertBefore(curTarget, rootSibling);

} else
{
rootParent.appendChild(curTarget);
}
}
// make sure the drag item is visible
curTarget.style.display = ‘‘;
}
curTarget = null ;
iMouseDown = false ;
}
function mouseDown()
{
iMouseDown = true ;

if (lastTarget)
{
return false ;
}
}
document.onmousemove = mouseMove;
document.onmousedown = mouseDown;
document.onmouseup = mouseUp;


window.onload = function ()
{
// Create our helper object that will show the item while dragging
dragHelper = document.createElement(‘DIV‘);
dragHelper.style.cssText = ‘position:absolute;display:none;‘;
CreateDragContainer(
document.getElementById(‘DragContainer1‘),
document.getElementById(‘DragContainer2‘),
document.getElementById(‘DragContainer3‘)
);

document.body.appendChild(dragHelper);
}
<!-- the mouse over and dragging class are defined on each item -->
< div class ="DragContainer" id ="DragContainer1" >
< div class ="DragBox" id ="Item1" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #1 </ div >
< div class ="DragBox" id ="Item2" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #2 </ div >
< div class ="DragBox" id ="Item3" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #3 </ div >
< div class ="DragBox" id ="Item4" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #4 </ div >
</ div >
< div class ="DragContainer" id ="DragContainer2" >
< div class ="DragBox" id ="Item5" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #5 </ div >
< div class ="DragBox" id ="Item6" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #6 </ div >
< div class ="DragBox" id ="Item7" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #7 </ div >
< div class ="DragBox" id ="Item8" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #8 </ div >
</ div >
< div class ="DragContainer" id ="DragContainer3" >
< div class ="DragBox" id ="Item9" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #9 </ div >
< div class ="DragBox" id ="Item10" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #10 </ div >
< div class ="DragBox" id ="Item11" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #11 </ div >
< div class ="DragBox" id ="Item12" overClass ="OverDragBox" dragClass ="DragDragBox" > Item #12 </ div >
</ div >
關(guān)于作者
Mark Kahn是一位Web Developer和DBA。可以通過這個網(wǎng)址跟他聯(lián)系: http://www.jslibrary.org
原文鏈接: http://www.webreference.com/programming/javascript/mk/column2/3.html
另外兩篇: [翻譯] 如何在 JavaScript 中實(shí)現(xiàn)拖放(上) [翻譯] 如何在 JavaScript 中實(shí)現(xiàn)拖放(中)
終于完成了全文的翻譯,由于時間比較參促,文章沒有過多的校正與潤色,閱讀過程中難免會有些許生硬或不準(zhǔn)確的感覺,請大家見量并指出,方便他人閱讀。
原文作者將拖放功能的實(shí)現(xiàn)分步講解,其核心的地方在于移動和放置元素時,鼠標(biāo)、移動元素、目標(biāo)元素關(guān)系的處理,只要這個問題處理好了,代碼就很容易理解,譯文僅供輔助參考之用。
整合所有的功能
最后我們使用所有代碼片斷,來創(chuàng)建一個完整的拖放函數(shù)腳本。我們所要做的第一件事情是DOM操作,如果你對此并不十分熟悉,可以閱讀《 JavaScript Primer on DOM Manipulation 》。
接下來的代碼創(chuàng)建容器和容器組,使得在這些容器中可以拖動每個元素,這在本文第二個demo的基礎(chǔ)上來完成。這段代碼能夠用來重新規(guī)劃元素的順序,將導(dǎo)航窗口放在頁面的左側(cè)或右側(cè),或再加入你所能想到的其他的功能。
我們將使用偽代碼來一步步進(jìn)行講解,將真實(shí)的代碼通過注釋的方式留給讀者查看。
1.當(dāng)文檔第一次被加載時,我們創(chuàng)建一個名為dragHelper的DIV標(biāo)簽,當(dāng)我們開始移動一個元素的時候,dragHelper將成為一個隱藏元素,可以四處移動。真實(shí)的元素并不會被拖動,僅僅使用insertBefore和appendChild來移動。我們在開始的時候隱藏dragHelper。
2.我們創(chuàng)建mouseDown和mouseUp函數(shù)。起初,所有的這些函數(shù)都假設(shè)記錄了鼠標(biāo)按鈕的狀態(tài),以至于iMouseDown變量在鼠標(biāo)按下的時候?yàn)閠rue,沒有按下的時候?yàn)閒alse。
3.我們創(chuàng)建一個全局變量DragDrops,以及一個函數(shù)CreateDragContainer。DragDrops包含一組相互關(guān)聯(lián)的容器。傳入CreateDragContainer的任何變量(代表容器)被組織成一個新的集合,使元素能夠在這些容器間自由移動。通過setAttribute,CreateDragContainer函數(shù)同樣將各容器中的元素綁定在一起。
4.現(xiàn)在我們的代碼知道每個元素所在的集合,現(xiàn)在來看mouseMove函數(shù)。mouseMove函數(shù)首先設(shè)置了一個變量target,表示鼠標(biāo)下面的目標(biāo)元素,如果這個元素在集合(用getAttribute判斷)中就繼續(xù)下面操作:
4.1.首先,在必要的時候,我們運(yùn)行一個簡單的腳本來改變目標(biāo)元素的class屬性,這樣就創(chuàng)造了一個翻動的效果。
4.2.然后我們檢查鼠標(biāo)是否點(diǎn)擊(因?yàn)槲覀兊拇a已經(jīng)運(yùn)行到這里),如果事件發(fā)生:
4.2.1.設(shè)置變量curTarget為當(dāng)前元素。
4.2.2.記錄元素當(dāng)前在文檔中的位置,以便在需要的時候可以將它的值取回。
4.2.3.將當(dāng)前元素克隆到dragHelper,使得我們能夠移動元素的隱藏備份。
4.2.4.因?yàn)樵赿ragHelper中我們完全擁有了拖動元素的一個備份,這個元素會始終在鼠標(biāo)下,我們必須移除dragObj屬性,讓代碼知道dragObj已不在集合中。
4.2.5.我們快速記錄集合中每個元素當(dāng)前的位置、寬度和高度。當(dāng)元素第一次開始被拖動時,我們僅需做一次這種工作,否則每當(dāng)鼠標(biāo)移動的時候我們都必須做一次,甚至一秒內(nèi)幾百次。
4.3.如果鼠標(biāo)沒有點(diǎn)擊,要么我們和之前擁有同樣的目標(biāo)元素,要么沒有目標(biāo)元素,不論哪種情況我們都不會做任何事情。
5.現(xiàn)在我們檢查curTarget變量。curTarget應(yīng)該僅包含一個被拖動的對象,因此如果它存在,表示我們正在拖動一個元素:
5.1.移動隱藏DIV到鼠標(biāo),這個元素和文章前面所創(chuàng)建的元素一樣能夠被拖動。
5.2.然后我們檢查鼠標(biāo)是否存在于當(dāng)前集合中每個容器中。
5.2.1.如果鼠標(biāo)在某個容器中,我們檢查容器中的每個元素,查看我們正拖動的元素屬于哪個位置。
5.2.2.然后我們將所拖動的元素放置在容器中另一個元素的前面,或容器的最后位置。
5.2.3.最后我們確定元素可見。
6.剩下的事情就是捕獲mouseUp事件:
6.1.首先需要隱藏dragHelper:它不再被需要,因?yàn)槲覀儧]有拖動任何東西。
6.2.如果拖動的元素是可見的,它已經(jīng)存在于任何它所屬的容器中,所有工作已完成。
6.3.如果拖動的元素不可見,我們將它放回它原來所在的地方。









































































































































































































































































































































































































































關(guān)于作者
Mark Kahn是一位Web Developer和DBA。可以通過這個網(wǎng)址跟他聯(lián)系: http://www.jslibrary.org
原文鏈接: http://www.webreference.com/programming/javascript/mk/column2/3.html
另外兩篇: [翻譯] 如何在 JavaScript 中實(shí)現(xiàn)拖放(上) [翻譯] 如何在 JavaScript 中實(shí)現(xiàn)拖放(中)