Draggable Elements implementation code

Source: Internet
Author: User

Of course, we can study the source code of the js library, or try to invent the wheel on our own. The process is quite interesting... now I will implement the drag-and-drop function of the page elements.
Now let's start implementation. Let's start with the top-level method, which is used to initialize a drag object. The method declaration is as follows:
Function DragObject (cfg)
The cfg here is passed in with an object, a bit like configuring attributes in Extjs
Copy codeThe Code is as follows:
Var dragObj = new DragObject ({
El: 'exampleb ',
AttachEl: 'examplebhandle ',
LowerBound: new Position (0, 0), // position indicates a point, which has the property x. The following describes the value of y.
UpperBound: new Position (500,500 ),
StartCallback:..., // The callback triggered when you start dragging is omitted here
MoveCallback:..., // callback triggered during drag and drop
EndCallback:..., // The callback triggered when the drag ends.
AttachLater:... // whether to enable the drag/drop event listener immediately
});

In the configuration parameter, el can be the id of a specific element, or a dom object attachEl is the handle element in the example. Drag it to drag the element, lowerBound and upperBound are used to limit the drag range. They are both Position objects. We will analyze the encapsulation and function of this object. don't worry:). If not, there is no limit on the drag range. startCallback, moveCallback, and endCallback are all callback functions. The value of attachLater is true or false. if you do not understand the analysis below, I think you will be clear soon ..
The following code writes Position:
Copy codeThe Code is as follows:
Function Position (x, y ){
This. X = x;
Thix. Y = y;
}
Position. prototype = {
Constructor: Position,
Add: function (val ){
Var newPos = new Position (this. X, this. Y );
If (val ){
NewPos. X + = val. X;
NewPos. Y + = val. Y;
}
Return newPos;
},
Subtract: function (val ){
Var newPos = new Position (this. X, this. Y );
If (val ){
NewPos. X-= val. X;
NewPos. Y-= val. Y;
}
Return newPos;
},
Min: function (val ){
Var newPos = new Position (this. X, this. Y );
If (val ){
NewPos. X = this. X> val. X? Val. X: this. X;
NewPos. Y = this. Y> val. Y? Val. Y: this. Y;
Return newPos;
}
Return newPos;
},
Max: function (val ){
Var newPos = new Position (this. X, this. Y );
If (val ){
NewPos. X = this. X <val. X? Val. X: this. X;
NewPos. Y = this. Y <val. Y? Val. Y: this. Y;
Return newPos;
}
Return newPos;
},
Bound: function (lower, upper ){
Var newPos = this. max (lower );
Return newPos. min (upper );
},
Check: function (){
Var newPos = new Position (this. X, this. Y );
If (isNaN (newPos. X ))
NewPos. X = 0;
If (isNaN (newPos. Y ))
NewPos. Y = 0;
Return newPos;
},
Apply: function (el ){
If (typeof el = 'string ')
El = document. getElementById (el );
If (! El) return;
El. style. left = this. X + 'px ';
El. style. top = this. Y + 'px ';
}
};

A simple encapsulation of coordinate points, which stores two values: x and y coordinates. we can use the add and substract methods to perform + operations and-operations with other coordinate points, and return a new coordinate point that has been computed. the min and max functions are used to compare with other coordinate points and return smaller and larger values. the bound method returns a coordinate point within the specified range. the check method is used to ensure that the value of attribute x and y is of the numerical type. Otherwise, 0 is set. the final apply method is to apply the attribute x and y to the element style. left and top. then I took out most of the remaining code and looked at it at 1.1 points:
Copy codeThe Code is as follows:
Function DragObject (cfg ){
Var el = cfg. el,
AttachEl = cfg. attachEl,
LowerBound = cfg. lowerBound,
UpperBound = cfg. upperBound,
StartCallback = cfg. startCallback,
MoveCallback = cfg. moveCallback,
EndCallback = cfg. endCallback,
AttachLater = cfg. attachLater;
If (typeof el = 'string ')
El = document. getElementById (el );
If (! El) return;
If (lowerBound! = Undefined & upperBound! = Undefined ){
Var tempPos = lowerBound. min (upperBound );
UpperBound = lowerBound. max (upperBound );
LowerBound = tempPos;
}
Var cursorStartPos,
ElementStartPos,
Dragging = false,
Listening = false,
Disposed = false;
Function dragStart (eventObj ){
If (dragging |! Listening | disposed) return;
Dragging = true;
If (startCallback)
StartCallback (eventObj, el );
CursorStartPos = absoluteCursorPosition (eventObj );
ElementStartPos = new Position (parseInt (getStyle (el, 'left'), parseInt (getStyle (el, 'top ')));
ElementStartPos = elementStartPos. check ();
HookEvent (document, 'mousemove ', dragGo );
HookEvent (document, 'mouseup', dragStopHook );
Return cancelEvent (eventObj );
}
Function dragGo (e ){
If (! Dragging | disposed) return;
Var newPos = absoluteCursorPosition (e );
NewPos = newPos. add (elementStartPos)
. Subtract (cursorStartPos)
. Bound (lowerBound, upperBound );
NewPos. apply (el );
If (moveCallback)
MoveCallback (newPos, el );
Return cancelEvent (e );
}
Function dragStopHook (e ){
DragStop ();
Return cancelEvent (e );
}
Function dragStop (){
If (! Dragging | disposed) return;
UnhookEvent (document, 'mousemove ', dragGo );
UnhookEvent (document, 'mouseup', dragStopHook );
CursorStartPos = null;
ElementStartPos = null;
If (endCallback)
EndCallback (el );
Dragging = false;
}
This. startListening = function (){
If (listening | disposed) return;
Listening = true;
HookEvent (attachEl, 'mousedownload', dragStart );
};
This. stopListening = function (stopCurrentDragging ){
If (! Listening | disposed)
Return;
UnhookEvent (attachEl, 'mousedownload', dragStart );
Listening = false;
If (stopCurrentDragging & dragging)
DragStop ();
};
This. dispose = function (){
If (disposed) return;
This. stopListening (true );
El = null;
AttachEl = null;
LowerBound = null;
UpperBound = null;
StartCallback = null;
MoveCallback = null;
EndCallback = null;
Disposed = true;
};
This. isDragging = function (){
Return dragging;
};
This. isListening = function (){
Return listening;
};
This. isDisposed = function (){
Return disposed;
};
If (typeof attachEl = 'string ')
AttachEl = document. getElementById (attachEl );
// If this Dom object is not configured or is not found, use el
If (! AttachEl) attachEl = el;
If (! AttachLater)
This. startListening ();
}

Some of the methods are not provided. In the process of further analysis, we will provide one by one ....
We first use cfg to direct el and attachEl to actual Dom objects. If attachEl is not configured or corresponding elements are not found, use el instead. we also set some variables to be used in the drag and drop operation. cursorStartPos is used to save the coordinates of the mouse when the mouse is pressed and started to drag. elementStartPos is used to save the starting point when an element is dragged. dragging, listening, and disposed are some state variables. listening: indicates whether the drag object is listening for the drag start event. dragging: whether the element is being dragged. disposed: The drag object is cleared and cannot be dragged.
At the end of the code, we can see that if attachLater is not true, call startListening. This is a public method defined in the drag object. Let's take a look at its implementation.
Copy codeThe Code is as follows:
This. startListening = function (){
If (listening | disposed) return;
Listening = true;
HookEvent (attachEl, 'mousedownload', dragStart );
};

The first two rows are a judgment. If you have already listened to or cleared the drag and drop events, you will not directly return anything. otherwise, the listening status is set to true, indicating that the listener is started and the dragStart function is associated with the mousedown event of attachEl. here we come across a hookEvent function. Let's take a look at it:
Copy codeThe Code is as follows:
Function hookEvent (el, eventName, callback ){
If (typeof el = 'string ')
El = document. getElementById (el );
If (! El) return;
If (el. addEventListener)
El. addEventListener (eventName, callback, false );
Else if (el. attachEvent)
El. attachEvent ('on' + eventName, callback );
}

In fact, there is nothing, that is, the listening of element events is encapsulated across browsers. The same unhookEvent method is as follows:
Copy codeThe Code is as follows:
Function unhookEvent (el, eventName, callback ){
If (typeof el = 'string ')
El = document. getElementById (el );
If (! El) return;
If (el. removeEventListener)
El. removeEventListener (eventName, callback, false );
Else if (el. detachEvent)
El. detachEvent ('on' + eventName, callback );
}

Next let's take a look at the implementation of the dragStart function, which is a private function of the drag object.
Copy codeThe Code is as follows:
Function dragStart (eventObj ){
If (dragging |! Listening | disposed) return;
Dragging = true;
If (startCallback)
StartCallback (eventObj, el );
CursorStartPos = absoluteCursorPosition (eventObj );
ElementStartPos = new Position (parseInt (getStyle (el, 'left'), parseInt (getStyle (el, 'top ')));
ElementStartPos = elementStartPos. check ();
HookEvent (document, 'mousemove ', dragGo );
HookEvent (document, 'mouseup', dragStopHook );
Return cancelEvent (eventObj );
}

This function is called after the dom object specified by attachEl captures the mousedown event. first, make sure that the drag object is in a suitable drag-and-drop State. If the drag-and-drop is in progress, or the drag-and-drop event is not monitored, or the "post-event" process has been completed, then do nothing. if everything is okay, we set the dragging status to true, and then "started". If startCallback is defined, we will call it with the mousedown event and el parameters. next, locate the absolute position of the mouse and save it to cursorStartPos. then, we can get the current top and left of the drag element and encapsulate it into the Position object and save it to elementStartPos. to be safe, check whether the attributes in elementStartPos are valid. let's look at two hookEvent calls. One is the mousemove event, which indicates that dragging is in progress and the dragGo function is called. one is the mouseup event, which indicates that the drag is over and the dragStopHook function is called. you may ask why the event is bound to the document, not the elements to be dragged, such as el or attachEl. because events are directly bound to elements, some browser latency may affect the effect, so events are directly bound to the document. if you do not really understand it, it may have little impact: P .... view cancelEvent (eventObj) In the last sentence)
Copy codeThe Code is as follows:
Function cancelEvent (e ){
E = e? E: window. event;
If (e. stopPropagation)
E. stopPropagation ();
If (e. preventDefault)
E. preventDefault ();
E. cancelBubble = true;
E. returnValue = false;
Return false;
}

It is used to stop bubbling and prevent default events. It can be understood as a security consideration... some methods in dragStart need to be introduced. Let's take a look at absoluteCursorPosition, and then let's look at getStyle.
Copy codeThe Code is as follows:
Function absoluteCursorPosition (e ){
E = e? E: window. event;
Var x = e. clientX + (document.doc umentElement | document. body). scrollLeft;
Var y = e. clientY + (document.doc umentElement | document. body). scrollTop;
Return new Position (x, y );
}

This method is only used to obtain the absolute position of the mouse in the browser and take the scroll bar into consideration.
Copy codeThe Code is as follows:
Function getStyle (el, property ){
If (typeof el = 'string ')
El = document. getElementById (el );
If (! El |! Property) return;
Var value = el. style [property];
If (! Value ){
If (document. defaultView & document. defaultView. getComputedStyle ){
Var css = document. defaultView. getComputedStyle (el, null );
Value = css? Css. getPropertyValue (property): null;
} Else if (el. currentStyle ){
Value = el. currentStyle [property];
}
}
Return value = 'auto '? '': Value;
}

The getStyle method is used to obtain the css attribute value of an element. no matter whether the style is written in inline form or defined in css, we can get the correct value. Of course, we only need to obtain the top element, left attribute .. the following is a real method to handle Drag and Drop Operations: dragGo
Copy codeThe Code is as follows:
Function dragGo (e ){
If (! Dragging | disposed) return;
Var newPos = absoluteCursorPosition (e );
NewPos = newPos. add (elementStartPos)
. Subtract (cursorStartPos)
. Bound (lowerBound, upperBound );
NewPos. apply (el );
If (moveCallback)
MoveCallback (newPos, el );
Return cancelEvent (e );
}

This method is not complex. Like other methods, let's first check the status. If it is not being dragged or cleaned up, do nothing. if everything goes well, we use the current cursor position, the initial element position, the initial mouse position, and the limited range (if upperBound and lowerBound are configured) to calculate a result point, the apply method is used to assign the coordinates of the calculation to the element style. top and style. left, let the drag element determine its position. if moveCallback is configured, call the next cancelEvent... the new coordinate operation here is similar to the jquery operation, because each method of the Position object returns a Position object... there is also a method in dragStopHook in dragStart.
Copy codeThe Code is as follows:
Function dragStopHook (e ){
DragStop ();
Return cancelEvent (e );
}
Function dragStop (){
If (! Dragging | disposed) return;
UnhookEvent (document, 'mousemove ', dragGo );
UnhookEvent (document, 'mouseup', dragStopHook );
CursorStartPos = null;
ElementStartPos = null;
If (endCallback)
EndCallback (el );
Dragging = false;
}

The key is to look at the dragStop method and determine the status first. If everything is OK, we will remove the BIND of the event mousemove and mouseup, and release the values of cursorStartPos and elementStartPos, A drag is over .. if endCallback is configured, call and set the dragging status to false ...... finally, the public method will be used.
Copy codeThe Code is as follows:
This. stopListening = function (stopCurrentDragging ){
If (! Listening | disposed)
Return;
UnhookEvent (attachEl, 'mousedownload', dragStart );
Listening = false;
If (stopCurrentDragging & dragging)
DragStop ();
};
This. dispose = function (){
If (disposed) return;
This. stopListening (true );
El = null;
AttachEl = null;
LowerBound = null;
UpperBound = null;
StartCallback = null;
MoveCallback = null;
EndCallback = null;
Disposed = true;
};
This. isDragging = function (){
Return dragging;
};
This. isListening = function (){
Return listening;
};
This. isDisposed = function (){
Return disposed;
};

StopListening removes the drag-and-drop mousedown event from the listener and sets the listener status listening to false. Here, the stopCurrentDragging parameter is known. the dispose method is used for processing. If you do not want the drag object to be dragged, call dispose. The following three small methods are available: isDragging, isListening, isDisposed returns the relevant status at a glance. finally, click the drop-down link of the source code to download a message!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.