custom drag and drop HTML5 - javascript

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.

Related

HTML5 Nested DnD interface only allowing nested elements to be dropped within form fields using sortable.js

I'm using Sortable.JS for sortable drag and drop within a form builder. I want sections to be draggable (which is more or less working) and the questions within the sections to be draggable within the same section, or over into other sections.
What I'm running into is that I make the class card-text within each section tag a Sortable element, with the draggable option set to .formQuestion, which as far as I can tell from the docs should work perfectly.
However when the questions start dragging, the only place they're allowed to be dropped is within the form fields of themselves or another question.
Am I selecting the wrong thing to drag? Is there something wrong with the section.querySelector of the card-text class as targets?
https://jsfiddle.net/robertgreenstreet/y9pgqfxt/12/
Also, for some reason, it's not behaving the same on jsfiddle as on my computer, especially the ondragstart and ondragend events.
I have most of an answer finally:
I got the sorting to work within the sections by removing the Sortable assignment for each section, and instead assigning a new ondragover function to the .collapse class (this is the same element as the .card-text in the original question, the actual area that questions will be dragged within) within the sections that (very slowly) calculates whether the dragged element needs to be put above or below the child element it's being dragged over (Fiddle has been updated to reflect this).
I also assigned the existing dragEnd function to each section's ondragover event, which collapses all sections, then uncollapses the .collapse within the section that is currently being dragged over, and then that .collapse's ondragover event gets fired to allow sorting within that section.
This isn't perfect or fast, so if anyone can figure out why the nested/grouped functionality from Sortable wasn't working as expected, that part is still in the Fiddle, commented, within the loop through sections.

HTML5 Drag and Drop with insertBefore

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!

Binding Multiple dragenter/ dragleave Events in HTML5 Drag 'n Drop?

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.

why not use mouseup event target rather than position for "old school" drag-n-drop?

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.

z-index and Javascript events

I have a couple of divs overlaid on each other with differing z-index values.
The default behaviour for browsers seems to be the event bound to the top-most z-index div gets fired. I.e. I have multiple onclicks one for each div but only the top one is fired when the area is clicked.
Is there a way to fire the events attached to all divs no matter what the z-index of each is, so long as the action is 'over' that div without regard to z-index?
The event doesn't actually occur on the element that is obscured by another unless the other element is contained in the first, then it will bubble up. The only way that I can think of to achieve what you want is to go through all of the potential elements and see if any of them contain the point at which the click occurred and trigger a click on that element (if it's not the current one).
If you are using an javaScript framework event bubbling might be included. (ExtJS I know for sure has this kind of event feature.)

Categories