I am working on a drag and drop project.
How: I am using the HTML5 drag events.
Code: See fiddle here
Problem: It only works sometimes, and I checked the code a million times
Idea:
- Get element and drag it over a div with id: LayerN (N=number)
- Create a new layer before LayerN when dropping
- AppendChild to new Layer.
- Remove empty layers if there are any.
Is there anything I am doing wrong or too complex? I don't want to use a JQuery framework, i want to understand what I am doing. Thanks a lot for reading, any help will be highly appreciated.
I can't get your fiddle to work so I'm giving general comments rather than a full 'answer' so hope it helps!
For D&D I find what works best is attach a mousedown event to the document on page load (or the containing element of the draggable objects if that's more appropriate).
Don't attach any events to the draggable elements themselves at this stage.
On mousedown, check the classname/id of the target and if it's draggable:
If you want to show it dragging, clone the element and append to a position:fixed or :absolute DIV - you can move this on mousemove relative to the cursor. Keeping this element offset to the cursor means you can still detect what's under the cursor.
Hide the element you're dragging. You can put some sort of image showing where it came from at this stage if you wish using insertBefore.
I hide the element rather than move it because if you cancel the drag it's very easy to restore it.
Attach mousemove and mouseup events to the window or document. The function you call on mousemove can, based upon the event.target/event.srcElement, decide whether the dragged object can be dropped or not (again, I generally look for a className here).
This way you've only got a maximum of two event listeners running everything rather than multiple events on every single element. I suspect your event listeners may be tripping over each other.
I don't know if that gets you any further, but good luck!
Related
I am trying to use drag and drop to reorder elements in the list (pure javascript). I would like to stylise the "ghost element" during the drag operation. Since I read that there is no way how to do that standartly, I am creating duplicate element (styled as I want) positioned exactly where original element should be during drag operation and its position is updated inside ondrag event.
Problem is that since this duplicate element is always exactly under cursor it is impossible to capture dragover or dragenter events on other elements. No matter where in the DOM is the "duplicate" element linked it always blocks ondragover. If I move the duplicate element so its not under the cursor it works but that defeats the purpose of seamless duplicate element.
Is there some way how this duplicate element would be ignored so elements that are underneath it would get the ondragover event instead? Normally event just bubbles towards parent of where the duplicate element is linked.
Got it working. It seems that it works (ghost element is not blocking the events) if its class has :
.duplicateGhostElementClass {
pointer-events: none;
}
Perhaps it saves someone some time.
I am building an app that allows you to move list items from one list to another by simply clicking on them. However, in order for the user to know what the intended action for a clik is, I set up a :hover state in the CSS which shows an instruction such as "<< move"
The problem I have found however is that in Internet Explorer (tested versions 7-9), when I move a DOM element the :hover state of that element remains (becomes sticky), even when the mouse is moved around. The :hover state only disappears when a user hovers over the item in it's new location and then moves their mouse away. This is an Internet Explorer only issue it seems.
You can see the problem if you are using IE by going to http://jsfiddle.net/hc2Eu/32/
There is of course a workaround which is to not use CSS :hover state and use a JQuery hover event instead, but this is certainly not the best way of doing things, and keeping elements :hover state controlled in CSS is by far and away the most robust way of doing this. The workaround can be seen at http://jsfiddle.net/hc2Eu/29/
Has anyone figured out how I can tell Internet Explorer somehow that an element is no longer under the mouse, and it should release the :hover state?
Matt
Try cloning the element instead of appending it directly. When you append, you're taking the element from it's current position and state in the DOM and placing it in its new position - basically just moving it. IE is clearly not repainting the element when this happens, or resetting its state until you mouseover.
By cloning it, you force IE to create a new element, which, since it's not on the page, can't have the hover state applied to it anyway. Then just append it in its new container, remove the original, and you're done.
See an example in this fiddle: Two lines of code, cross-browser, and you'll remain concise and not pollute your code. :)
http://jsfiddle.net/hc2Eu/36/
this will handle your issue. clone the clicked item(add true if you want to save click event and other handlers) insert it after itself so it has the same place in the dom. then remove it. the clone will not be stuck with the hover state stuck. All references are relative (this) so it'll work anywhere without changing selectors.
$("#elementwithhover").click(function() {
// code that makes element or parent slide or
// otherwise move out from under mouse.
$(this).clone(true).insertAfter($(this));
$(this).remove();
});
Here's a basic example of what I have going on here:
http://jsfiddle.net/kgqkM/2/
What I'm trying to do is dedicate the area outside of this list to be a "delete" area where I can set up the appropriate logic to remove the dragged element from the page. I'm attempting to show when the user drags the element off the list it would color the outside red/ semi-transparent. When I set events on the outer-wrapper, they seem to take over all the dragenter/ dragleave events.
Guessing my issue has to do something with setting the event on the parent div? I'm starting to try and perhaps have one master event on top and deciding what to do based on the e.target and .parents('.switch'), but insofar it's resulting in buggy behavior.
It would seem that I had to do some (correct) logic on the event target. I have to refactor my code a bit, but it's working out.
So, from what I've seen nearly all the IE compatible drag-n-drop use positioning to determine where something is dropped. Doing something like mousedown, determine the position of all droppable, mouseup determine if we are in a droppable position. Why? I made a quick prototype, and it seems to work, which uses the event.target on mouseup (in jquery, so whatever that translates to elsewhere) to determine the drop element.
Is there a compelling reason not to do this? (use the e.target on mouseup). So, mousedown determines what is being dragged, and mouseup determines where it is dropped. Add some variable to make sure we're dragging, and remember what is dragged.
My guess: Because e.target on mouseup can refer to the element you're dragging (or its drag-ghost). If you drag an element, and it (or a translucent ghost-element) follows your cursor like when dragging a file on your desktop, your mouse will always be over the element you're dragging, when you mouse-up.
Alternatively, if there's no cursor-following, and no ghosting, e.target might refer to an element inside the "dropzone element" and not the dropzone itself.
For instance:
<div id="dropzone_element">
<div id="previously_dropped_element" />
<div>
<div id="draggable_element" />
So if you drag the draggable element over the dropzone element and release the mouse, you're actually releasing the mouse over the previously dropped element inside the dropzone; not the dropzone itself.
In both cases, checking the mouse position is the only way to get the proper dropzone element.
Those would be my guesses, but I don't have IE to check the actual behavior.
Edit: In the 1st case position-checking is the only way. In the 2nd case, you could also check the target's ancestors to find the dropzone element, as pointed out by aephus in the comments. If, that is, the previously dropped element has actually be inserted into the dropzone's hierarchy, and not just been positioned to look like it is - although that would be a really weird thing to do :)
The first quess: there is always lagging when you drag elements, especialy in IE. And mouse pointer can outrun or fall behind the dragging element. So on drop, pointer is not actualy over the dragging element, and - for small drop zones - not over the drop zone. Ajax libraries have to take this fact into account. And the only way to make it work predictably is to compare coordinates of dragging target and the drop zone.
The second quess: ajax libraries may give you an opportunity to use the drop handler. The drop handler is an element which is not child of the dropzone but it is covering the dropzone. And in this case there is no way to catch events by dropzone. But comparing the coordinates is still working. Why would someone cover the dropzone? Let's assume one has table. And one of cells is the dropzone. And now one wants to catch mousewheel event. One creates div (#scroller) with the same size as table and places it over the table. Then he puts another div (#eventgrabber) inside scroller. Eventgrabber is heigher then scroller. Now one is able to catch scroll event on scroller. But in order to be able to drag cells one have to assign eventgrabber as drag&drop handler.
In a web application I am making I want to create the same effect that GMail has for dragging and dropping labels and conversations.
Basically I want to be able to make an ondrag function that attaches a "new" html element to the cursor, and upon releasing it over certain elements I want to execute my given functionality that is aware of both the dragged element and the element over which the drop occured.
I don't want to move any elements around. I want to make a function that let's the original elements stay in place and only react to the dragging of some new element.
How can this be done?
you mean something like this : http://www.finrik.at/temp/drag_drop/index.html
here is the how to. and you'll need jQuery plgin (www.jquery.com)