This is mostly an 'is this possible' type of question, because after a day of testing options, I am not sure that it is.
We have a list of item within a scrollable DIV, each of which needs to be touch draggable. Just turning on drag and drop for each time will break the ability to scroll the list, as a swipe to scroll gets interpreted as a drag event as well. The easiest option if to add a drag handle to each item, but we have very little UI space available on these items, and the powers that be were hoping to not squeeze things even tighter to add a touch hit area for said handle.
The option I am looking into is a 'touch and hold to release'. The user touched and holds the touch for one second, and the item 'unlocks' to be draggable. This sounded easiest enough when I set out, using HammerJS for the Press event, then calling a start for the drag and drop event process. This is not working out however, as none of the browser drag events can be manually started. I can call 'touchstart' with a trigger or dispatchEvent, which will do the setup we need, but no drag events every occur. 'dragstart' normally gets fired as soon as the touchstart occurs, and since I am needing to wait, there is never and dragstart to kick off the process.
I assume the browser's drag and drop events work only under specific circumstances. Triggering dragstart manually wont be a real drag event (its missing all the dataTransfer stuff), and most importantly, nothing actually gets dragged, despite the element being a valid, draggable element. Using an alternate event like 'dragover' works, but since its there was never a real dragstart, there is no actual drag and drop occurring.
Is this just something that browsers do not support short of dropping the HTML drag and drop for javascript? Or is there something I am missing?
Related
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!
Short version: Can mouse wheel events be fired while in the middle of a drag+drop operation?
Long version:
I'm currently in the design phase for this particular feature, so I don't have any code yet. I'm asking this so I know if it's a waste of time to pursue this path, so I can design something else instead.
Basically, I have a list of items on one side, and a basket on the other. As it stands right now, each item has an input box and a button so you can type the quantity and add it to the basket (and same thing in reverse). I want to add drag and drop functionality so you can just drag items one way or the other. This works fine if you only want one of the item, but I'd like to add a way to adjust the quantity while dragging. The mouse wheel came to mind, since you're already using the mouse to drag in the first place.
So before I dive into the code, I need to know if it's actually possible to receive mouse wheel events during a drag, and if so where should I add the listener?
If you want to check if the primary mouse button is being pressed during a wheel event, you can use the following:
addEventListener('wheel', function (event) {
if (event.buttons & 1) {
alert("You scrolled while the primary mouse button was down!");
}
}
Unfortunately, if you're using the Drag and Drop API with the draggable='true' attribute, wheel and scroll events are not fired during a drag, so you will be unable to fire and handle a scroll event.
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.
I have an info overlay that works great in Chrome and FF. It is a div containing a table (for border image layout) and then a central content div. I trigger mousedown on the appropriate border table cells.
Once this happens, a different div is brought to the front with z-index, and that passes along the mousemove and mouseup events to handle dragging the info bubble around. Once the mouseup is fired, the info bubble puts the "event" div back to where it was.
I also follow the same process for dragging the lower right corner of the bubble to resize it. Again, works in Chrome and FF, but fails in IE.
IE seems to be restricting the event triggers to the info div. If the mouse manages to move outside the div (from dragging faster then the events fire/update), the info overlay no longer receives mousemove events. However, if I move the mouse back over the overlay (without releasing the button) it continues to receive mouse events.
Edit: In creating some example code (the current functionality is split across several JS modules), it worked in IE. As soon as I find the difference between my example code and the actual code, I will update again.
Edit/Answer: (SO wont let a new user answer their own question in this time span...)
Still not sure what the actual problem was. (If you ask me, a div with a z-index of 100 should be receiving mouse events just fine?)
My solution was to fix my mouse event handling such that I could attach my mousemove and mouseup to the parent div (as should have been done in the first place) for all dragging/resizing behaviors I wanted to set up.
The problem was due to a newbie approach to the events and having stopPropagation() in too many locations preventing me from taking such an approach. (I wanted text, etc in my info box to be selectable).
I adjusted the code so that my text containers only had to stop propagation on mousedown instead of all the mouse events.
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.