Javascript drag-and-drop effect (program principle + deep analysis)

Source: Internet
Author: User

[Program principle]

The following uses simpledrag as an example to describe the basic principle.

First, you need a drag-and-drop object in the initialization program:
This. Drag = $ (drag );
Two parameters are also required to record the coordinates of the mouse relative to the drag-and-drop object at the beginning:
This. _ x = This. _ y = 0;
There are also two event object functions used to add and remove events:
This. _ fm = bindaseventlistener (this, this. Move );
This. _ FS = BIND (this, this. Stop );
They are the drag program and the Stop drag program.
The position of the drag-and-drop object must be absolute positioning:
This. Drag. style. Position = "absolute ";
Finally, bind the start drag-and-drop program to the drag-and-drop object mousedown event:
Addeventhandler (this. Drag, "mousedown", bindaseventlistener (this, this. Start ));

When you hold the mouse on a drag-and-drop object, the START program will be triggered. It is mainly used to prepare the drag. Here, the coordinates of the mouse relative to the drag-and-drop object are recorded:
This. _ x = oevent. clientx-This. Drag. offsetleft;
This. _ y = oevent. clienty-This. Drag. offsettop;
Bind the _ FM drag program and the _ fS stop drag program to the mousemove and mouseup events of the document respectively:
Addeventhandler (document, "mousemove", this. _ FM );
Addeventhandler (document, "mouseup", this. _ fS );
Binding to document ensures that the event is valid throughout the window document.

When you move the mouse over the document, the move program will be triggered. Here is the drag program.
The left and top coordinates that should be set for the drag-and-drop object can be obtained by the difference between the coordinates of the mouse and the coordinates of the mouse when the drag-and-drop is started:
This. Drag. style. Left = oevent. clientx-This. _ x + "PX ";
This. Drag. style. Top = oevent. clienty-This. _ y + "PX ";

After the mouse is opened, the Stop program is triggered to end the drag and drop operation.
The main function here is to remove the event added to the document in the Start Program:
Removeeventhandler (document, "mousemove", this. _ FM );
Removeeventhandler (document, "mouseup", this. _ fS );

This simple drag-and-drop program is ready. Next we will talk about other extensions and details.

Drag and Drop lock]

There are three types of locks: horizontal lockx, vertical locky, and full lock ).
This is relatively simple. for horizontal and vertical locking, you only need to determine whether to lock in move and then set left and top. If it is completely locked, the system will return directly.
If (! This. lockx) {This. Drag. style. Left = ...;}
If (! This. locky) {This. Drag. style. Top = ...;}

[Trigger object]

The trigger object is used to trigger the drag-and-drop program. Sometimes, you do not need to trigger the entire drag-and-drop object. In this case, you need to trigger the object.
After a trigger object is used, the trigger object is moved or dragged. The trigger object is used to trigger the drag-and-drop object. Generally, the trigger object is put in the drag-and-drop object ).

Scope restriction]

To set the range limit, you must set limit to true first. Range restrictions are divided into two types: Fixed Range and container range restriction, which are mainly set in the move program.
The principle is that when the Compare value exceeds the range, the left and top values are corrected so that the drag-and-drop object can be kept within the set range.

[Fixed range limit]

The container range limit specifies the drag and drop range between the upper and lower sides.
The meaning of each attribute is:
Upper (mxtop): top limit;
Lower (mxbottom): Top + offsetheight limit;
Left (mxleft): Left limit;
Right (mxright): Left + offsetwidth limit.

If the range setting is incorrect, it may cause the upper and lower or the left and right to exceed the range at the same time. Therefore, you must modify the value in the Start Program:
This. mxright = math. Max (this. mxright, this. mxleft + this. Drag. offsetwidth );
This. mxbottom = math. Max (this. mxbottom, this. mxtop + this. Drag. offsetheight );
Mxleft + offsetwidth and mxtop + offsetheight are the minimum values of mxright and mxbottom respectively.

Modify the moving parameters according to the range parameter:
Ileft = math. Max (math. Min (ileft, mxright-This. Drag. offsetwidth), mxleft );
ITOP = math. Max (math. Min (iTOP, mxbottom-This. Drag. offsetheight), mxtop );
Take a larger value for the top left and a smaller value for the bottom right.

[Container range restriction]

Container range restriction refers to limiting the range to a container _ mxcontainer.
Note that the drag-and-drop object must be included in _ mxcontainer, because relative positioning is used in the program to set container range restrictions (if it is outside the container, absolute positioning is required, in this case, it is quite troublesome), and the container space is bigger than the drag-and-drop object, so it should not be clear.
The principle is similar to the fixed range, but the range parameter is set based on the container attributes.

When a container is set, the position is set to relative to locate the container:
! This. _ mxcontainer | currentstyle (this. _ mxcontainer). Position = "relative" | (this. _ mxcontainer. style. Position = "relative ");
Note that relative must be set before obtaining offsetleft and offsettop, that is, setting _ x and _ y, so that offset can get the value correctly.

Because it is relative positioning, for the container range, the upper and lower sides of the range parameter are 0, clientheight, 0, and clientwidth.
Clientwidth and clientheight are the width and height of the visible part of the container (For details, refer to here ).
To ensure that the container range can be compatible with parameters of a fixed range, the program will get a smaller value in the container range and a fixed range:
Mxleft = math. Max (mxleft, 0 );
Mxtop = math. Max (mxtop, 0 );
Mxright = math. Min (mxright, this. _ mxcontainer. clientwidth );
Mxbottom = math. Min (mxbottom, this. _ mxcontainer. clientheight );

Note that if left and top of the drag-and-drop object are set before the program is executed, but relative is not set in the container, the system will shift when relative is set automatically, therefore, do not set relative and drag and drop objects left and top to execute the program.
However, a clever method is to set margin to replace location (if margin is used, it will be automatically corrected, as described later ).
Because of the relative positioning relationship, do not cancel or modify the container _ mxcontainer settings. Otherwise, it is easy to cause a shift exception.

Mouse capture]

In a drag-and-drop instance, even if you move the mouse outside the browser, the drag-and-drop program can still be executed. After careful viewing, it is found that setcapture is used.
Setcapture is the focus of this program. It captures mouse events to the specified objects in the current document. This object receives all mouse events for the current application or the entire system.
Easy to use:
This. _ HANDLE. setcapture ();

Setcapture captures the following mouse events: onmousedown, onmouseup, onmousemove, onclick, ondblclick, onmouseover, and onmouseout.
The program mainly needs to capture onmousemove and onmouseup events.
In the msdn introduction, it is also said that setcapture has a bool parameter to set whether all mouse events in the container are captured by the container.
The container is the object that calls setcapture, which roughly means:
When the parameter is true (default), the container will capture the mouse events of all objects in the container, that is, the objects in the container will not trigger the mouse events (the same as the objects outside the container );
When the parameter is set to false, the container does not capture the mouse events of objects in the container. That is, objects in the container can trigger events normally and cancel bubbles.
Mouse events outside the container are captured no matter what the parameter is,
You can use the following simple example to test (IE ):
<HTML>
<Body onclick = "alert (2)">
<Div onmousemove = "alert (1)"> Mouseover </div>
<SCRIPT> document. Body. setcapture (); </SCRIPT>
</Body>
</Html>
The parameter here is true. At the beginning, the body captures all mouse events, and the onmousemove event is not triggered even if the mouse passes through the div.
If it is set to false, the DIV can capture the mouse event and trigger the onmousemove event.

Use releasecapture to release the mouse after the drag and drop ends. You can place the mouse in the Stop program:
This. _ HANDLE. releasecapture ();

Setcapture is the IE Mouse capture method. For ff, there are also corresponding captureevents and releaseevents methods.
However, the two methods can only be called by window, And muxrwc says the two methods have been discarded in dom2 and are no longer used in ff.
However, FF seems to automatically disable Mouse capture, but the specific situation is unclear. I cannot find a detailed description. please tell me who has such information.

The following are my guesses. FF Mouse capture is equivalent to automatically setting and releasing document. Body. setcapture (false ).
Because I tested the following program and found that IE and FF are similar:
IE:
<HTML>
<Body>
<Div id = "AA" onmouseover = "alert (1)"> </div>
<SCRIPT>
Document. Body. onmousedown = function () {This. setcapture (false )}
Document. Body. onmouseup = function () {This. releasecapture ()}
Document. onmousemove = function () {AA. innerhtml + = 1}
</SCRIPT>
</Body>
</Html>

FF:
<HTML>
<Body>
<Div id = "AA" onmouseover = "alert (1)"> </div>
<SCRIPT>
Document. onmousemove = function () {AA. innerhtml + = 1}
</SCRIPT>
</Body>
</Html>

Unfortunately, you can only guess if you don't have any authoritative reference materials. There are still many things you haven't understood before.

Note that the Mouse capture under ff2 has a bug. When the drag-and-drop object contains no text content and is dragged outside the browser, the capture will become invalid.
Insert an empty text to the drag-and-drop object. For example, <font size = '1px '> </font> can be solved, but this bug has been fixed in ff3.

[Loss of focus]

Generally, the Mouse capture can capture events normally, but if the focus of the browser window is lost, the capture will become invalid.
Operations that result in loss of focus include switching windows (including ALT + TAB), alert, popup, and other pop-up forms.
When the focus is lost, the Stop program should be executed at the same time to end the drag and drop, but when the focus is lost, the mouseup event cannot be captured, that is, the _ fS event cannot be triggered.
Fortunately, the onlosecapture event in IE will be triggered when the capture fails. In this case, you can set it as follows:
Addeventhandler (this. _ HANDLE, "losecapture", this. _ fS );
And remove it from the Stop program:
Removeeventhandler (this. _ HANDLE, "losecapture", this. _ fS );

But FF does not have a similar method, but muxrwc finds a window. onblur event that replaces losecapture, you can set it in the Start Program:
Addeventhandler (window, "Blur", this. _ fS );
Remove from the Stop program:
Removeeventhandler (window, "Blur", this. _ fS );

There is also a window. onblur event in IE, so you can save some code by replacing losecapture with window. onblur.
Then I did some tests and found that basically all situations that trigger losecapture will trigger window. onblur at the same time. It seems that this is true.
So I used window to modify the program. onblur replaces losecapture, but there is a problem after the test. I found that if I use Alt + TAB to switch to another window, the drag can continue, but this time should have lost the focus.

So I excluded the test and program code one by one, and found that if DTD is used, window. onblur will be triggered only when the focus is obtained again.
You can use the following code to test:
<! Doctype HTML public "-// W3C // dtd xhtml 1.0 transitional // en" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<SCRIPT> window. onblur = function () {alert (1)} </SCRIPT>
After switching to another program, switching back will trigger window. onblur. There are a few strange situations that will not be mentioned. It is not ideal for IE to use window. onblur.

[Default action]

Drag and Drop the text content, connections, and images in the selected status will trigger the default action of the system. For example, if you drag an image in IE, the operation will be disabled, this will cause the drag-and-drop program to fail to run.

However, after setting setcapture for IE, the drag and drop operation and content selection are disabled on the user interface.
This means that you cannot drag and drop the document content after setcapture. Note that the drag and drop here refers to the default action of the system. For example, ondragstart will not be triggered.
However, if the setcapture parameter is false, the objects in the container can still trigger the event (For details, refer to the Mouse capture part). Therefore, the setcapture parameter must be set to true or retain the default value.

This function is not available for FF Mouse capture, but you can use preventdefault to cancel the default action of the event:
Oevent. preventdefault ();
PS: It is said that the use of preventdefault will cause the loss of mouseup, but I did not find it in the ff3 test. If you find any loss of mouseup, please tell me.

Clear Selection]

After setting setcapture for IE, content selection will be disabled, but the selected content will not be cleared before the setting, and content can be selected in other ways after the setting,
For example, press Ctrl + A to select the content.
PS: onkeydown, onkeyup, and onkeypress events are not affected by Mouse capture.
FF can clear the selected content when mousedown, but dragging the mouse, CTRL + A will continue to select the content.
However, after the system default action is discarded, this option will not affect the drag-and-drop operation. Here, the setting is mainly for better experience.

Previously, I used the method to prohibit drag-and-drop objects from being selected to achieve this goal. In IE, if onselectstart is set for the drag-and-drop object, false is returned. In ff, I set the style define userselect (CSS: -Moz-user-select) is none.
However, this method can only prevent drag-and-drop objects from being selected. Later, we can find a better method to clear the selection, which not only does not affect the selection of drag-and-drop objects, but also clears the entire document:
IE: Document. selection. Empty ()
FF: window. getselection (). removeallranges ()
To avoid selecting content during the drag-and-drop process, put it in the move program. Below is a compatible method:
Window. getselection? Window. getselection (). removeallranges (): Document. selection. Empty ();

[Margin]

In another case, when margin is set for the drag-and-drop object, the drag-and-drop object will be misplaced (set margin for the drag-and-drop object of simpledrag to be tested ).
The reason is that when the Start Program sets _ x and _ y, offset is used to obtain the value, and this value includes margin. Therefore, this margin is subtracted before left and top are set.
However, if margin is removed from the Start Program, an error will be calculated when the range limit is set in the move program,
So it is best to get the value in the Start Program:
This. _ marginleft = parseint (currentstyle (this. Drag). marginleft) | 0;
This. _ margintop = parseint (currentstyle (this. Drag). margintop) | 0;
Currentstyle is used to obtain the final style. For more information, see the final style section.

Set the value in the move program:
This. Drag. style. Left = ileft-This. _ marginleft + "PX ";
This. Drag. style. Top = iTOP-This. _ margintop + "PX ";
Note that margin must be set only after range correction; otherwise, it will be misplaced.

[Transparent background bug]

There is a transparent background bug in IE (not counting as a bug), you can test it with the following code:
<! Doctype HTML public "-// W3C // dtd xhtml 1.0 transitional // en" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HTML>
<Body>
<Div onmousedown = "alert (1)" style = "border: 10px solid # c4e3fd; width: 50px; Height: 50px; position: absolute;"> </div>
</Body>
</Html>
The event cannot be triggered when the background is clicked, but the event can still be triggered when the border is clicked.
Why? Use the following code for testing:
<! Doctype HTML public "-// W3C // dtd xhtml 1.0 transitional // en" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HTML>
<Body style = "border: 1px solid # ff0000;">
<Style> Div {width: 100px; Height: 100px; Border: 1px solid #000 ;}</style>
<Div style = "position: relative;">
<Div onclick = "alert (1)" style = "border-color: # 00f; margin: 50px;"> </div>
<Div onclick = "alert (2)" style = "border-color: #6f0; position: absolute; top: 50px;"> </div>
</Div>
</Body>
</Html>

We can see that the following two divs are beyond the body (that is, beyond the red box) and cannot trigger the event.
That is to say, when the trigger event point is outside the body, and the background is transparent, the trigger point is mistakenly considered to be a blank place outside the body, so the event cannot be triggered.
The solution is to keep the event trigger point in the body or set a non-transparent background.

In the program, you only need to set a background color for the drag-and-drop object, but sometimes the requirement is to be transparent (such as the cutting effect). What should we do?
The first thing that comes to mind is that the settings are completely transparent after the background color is added, but the border and objects in the container are completely transparent. This is not good.
One solution I have come up with is to add a layer in the container to overwrite the entire container, and set the background color and completely transparent:
With (this. _ HANDLE. appendchild (document. createelement ("Div"). Style ){
Width = height = "100%"; backgroundcolor = "# fff"; filter = "alpha (opacity: 0 )";
}
When this bug occurs in the program, setting the optional program parameter transparent to true will automatically insert such a layer.
If you have a better method, Please give more advice.

I have studied this for the time being, but I still haven't considered IFRAME, scrolling, etc. I will study it later.

Related Article

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.