Gmail handles its drag-and-drop file attachment uploading in a few clever ways:
1) Dragging a file into the browser causes the dropzone to appear. The cursor displays feedback indicating whether you are in the dropzone or not (in windows, a red crossthrough circle is used if outside the dropzone). If you drop inside the window but outside of the dropzone, the drop is intercepted such that the browser's default behavior is prevented (usually navigating away to display the dragged file).
The most obvious way to attempt this is to set dragover handler on the BODY to make the dropzone appear and preventDefault, but what about the cursor change? Is there some way to use dataTransfer.effectAllowed='none'?
2) Dragging text from one part of the window into another part does not trigger the drag-and-drop handling (ie- the dropzone does not appear)--and the preventDefault mentioned in #1 does not kick in.
If I capture the dragover event on the BODY (from #1), then intra-window text dragging is prevented. How do they accomplish both of these at the same time? It seems like this is more complex than it at first might appear.
UPDATE:
I learned two related things while trying to completely address this:
1) It appears that IE will not even trigger the drop event handler if dropEffect='none'... so I decided to only set it to none if e.dataTransfer.types exists (which it does in Chrome & FF, but not in IE). The downside is that the cursor doesn't have the red crossthrough, but at least I can intercept the drop to prevent a navaway. Your best guess for determining whether it was a file drop in IE is if e.dataTransfer.getData('Text')==null. (In my case, I wanted to be able to receive drops of either files or text, so this is how I can tell the difference in IE.)
2) It was non-obvious how Gmail hides the dropzone when you leave the browser. If you use a pure dragleave event on the page, then dragging into any child will trigger the dragleave handler even though you didn't really leave the page. I then noticed that there's a delay in Gmail before the dropzone disappears, so I would guess they use a timer to hide the dropzone (which gets reset on something like dragover). But I've come up with an alternate solution that appears to work so far:
function areXYInside(e){
var w=e.target.offsetWidth;
var h=e.target.offsetHeight;
var x=e.offsetX;
var y=e.offsetY;
return !(x<0 || x>=w || y<0 || y>=h);
}
And then:
$("#page").bind('dragleave', function(e){
if(this!=e.target) return false;
if(!areXYInside(e)){
hideBox();
}
return false;
});
I believe they are setting dataTransfer.effectAllowedon dragover, depending on the dataTransfer.types attribute.
EDIT: I was wrong the first time, here are the actual values for types(in Chrome at least):
- dragging text ["text/html","text","text/plain"]
- dragging a file ["Files"]
Here is a short jsFiddle example to play around with.
You can check more info about drag and drop at w3c or mdc.
EDIT: I managed to implement the exact behavior on Chrome and FF (see here)
Related
I have implemented the Swipe for Action Android pattern in my mobile web application (PhoneGap) using JavaScript & CSS animations/transitions.
However, there's one thing that's still eluding me.
I wish, that once the action menu is displayed fully and the user clicks anywhere outside of the action menu (labelled 3 in the figure), the menu should retract and the original item displayed (labelled 1 in the figure).
In a desktop application, one could "capture focus" and perform the transition back to (1) in lostfocus.
What is the JS equivalent of lostfocus event. I see an onfocus and onblur event, but from what I read it's really meant for things that need focus; like input, textarea, etc.
How else could I catch that event I'm interested in, other than putting some code in the touchend of every other element in the page and forcing the retraction of open actions explicitly?
I think you gave the answer yourself. focus and blur are the events to be used for this and they are not exclusively meant for input elements, as you can see here [1].
I'm even trigger the focus event manually in a layer use case: A layer opens and I want to capture the keypress of ESC to close the layer. For this I need to set the focus on the layer as my event handler would not fire otherwise.
To capture the click outside you just need to register for pointerUp or click events on an element that spans the whole screen (it must really cover the whole screen like the body element). Because of the event bubbling the handler will fire as long as nothing else captured and cancelled it.
[1] https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-blur
I have window with some divs that are draggable. I also have a second window that I created with window.open() that has divs that are draggable. Is it possible now to drag a div from one window to the other?
Thanks
Take a look at this tutorial for the DnD part.
To achieve this, it's possible to pass a temp value that both sending and receiving window can read. It could be a cookie, or a better idea could be to use localStorage if it's available, as some people voluntarily block cookies for more privacy.
Using the Modernizr's fallback test, it could goes like this:
if (Modernizr.draganddrop) {
// add drag and drop support
} else {
// custom drag and drop support or suggest the user to get a real browser (if possible)
}
Whenever a drag is started :
if (Modernizr.localstorage) {
window.localStorage['item_1_drag_started'] = true;
} else {
document.cookie = "item_1_drag_started=1";
}
Then, when drag is over / mouse is up :
if (Modernizr.localstorage) {
delete window.localStorage['item_1_drag_started'];
} else {
document.cookie = "item_1_drag_started=0";
}
Now, when the mouse enters the other window, you may check if the item was still being dragged by accessing localStorage (or the dirty cookie if it's unavailable).
The only issue I can think of is when the user releases the mouse button while between those windows. That could be a problem, since the item will be considered dragging but you can't release a button that isn't pressed. Does anyone know about a trick to check if the mouse button is still pressed when entering a window even if it doesn't fired the event ?
In the meantime, the click event in the receiving window could simply check if it's still dragging then drop the item and remove the flag.
UPDATE: Concerning the mentionned issue, after digging a little and doing some test with events it seems that neither mouseover or mousemove is fired when a mouse button is still pressed (at least in Chrome where my tests have been made).
With that in mind, I think that the best approach to Drag and Drop between two windows is to toggle it:
Click --> Drag is started
Click again --> Drop the item
From the answers provided I can see now that dragging between windows is really ugly. You have to basically transfer the mouse state from one window to the other and inject the element being dragged into the DOM of the destination window. I was hoping there was some jQuery around that you could run in both windows that would smooth all that over, but I guess not. I just won't do the drag in this project. I can just allow the user to click an element in the second window and have it appear immediately in the first window. Good enough. Thanks for looking at the question.
I guess you could set a variable "drag item = true" and store the item content somewhere. Then drop it on the mouse up in your popup.
What you could do is on drag being, set a cookie and on mouse over the target window, check for the cookie and simulate the drag.
I am working on an RTS game where you can select units and right click to make them go somewhere. You can also shift right click to queue up a list of points you would like the units to travel to.
In all browsers except FF this is working perfectly.
In Firefox however the shift right click triggers a context menu (right click without shift does not). I am handling the contextmenu event and calling preventDefault but that doesn't seem to do anything in FF when the shift button is held.
Is there any way to block this behavior in FF?
My game is here: https://mod.it/4plhXo3l and the code in question in in the RTSBoard.js file on line 36.
I managed to get this working in Firefox by setting event.shiftKey to false, then calling preventDefault(), and stopPropagation(), then returning false. I then set the document model's onclick event and ondblclick events to the same function you wound up using for yourself, plus the added setting of the shiftkey to false. I also had to do this for mouseup events, because clicking and dragging was also causing context menus to pop up.
I'm not sure it can be fully 100% disabled, but this looks to be about the closest you can get it.
Answering my own question. It appears that calling preventDefault and stopPropagation in the document.onclick event solves the issue.
See this reddit thread for more discussion: http://www.reddit.com/r/javascript/comments/1agoj8/is_it_possible_to_block_the_shift_right_click/
I'm trying to implement a "custom" combobox options popup, so that near each option on the list i can place an icon / image.
My goal is to make this as unobtrusive as possible and make it look as close to a regular combo as possible, so, for Chrome and IE, the solution of grabbing the mouse and keyboard events that cause the standard popup to appear works fine:
#el.bind 'mousedown keydown keyup click', (e) =>
(...)
e.stopPropagation()
e.preventDefault()
This basically makes it so that the control is still there, looking native, and whenever the user clicks or focuses it, it shows up the "custom" list instead of the native one.
However, in firefox, as soon as the user clicks the combobox control (< select >), a popupshowing event is triggered, but i can't find a way to cancel it before the popup with the < options > shows up, covering up my "custom" options display implementation.
The only information regarding this event i was able to find, was on the Mozilla XUL documentation.
Thanks in advance.
I looked at the source code and it doesn't appear to be possible to cancel either the mouse event that opens the drop down or the popupshowing event (I don't even know why that event is generated). However I think you might be able to capture the mouse event on a parent element and stop its propagation.
I am working on a web app in which I want to have a different action happen to an element whether I left or right click on it.
So I first added a function to handle the click event with jQuery, and then added a second function to handle the oncontextmenu attribute of my element.
This is working well in Chrome & IE but causes a problem in Firefox: When I right click on an element, my function that handles the left click is surprisingly called, and then my function that handles the right click is called.
How can I make Firefox not call the left-click function when I right click?
Yeah, browsers traditionally send right-clicks to the onclick handler, with the event.which property set to 3 instead of 1. IE used oncontextmenu instead, then Firefox picked up oncontextmenu in addition to the usual onclick. To cater for the browsers you will have to catch both events — or find a plugin that will do it for you.
Note that even with this sorted out, you are still not guaranteed to get right click events or be able to disable the standard context menu. Because many web pages abused the ability, it is disablable in many browsers, and sometimes disabled by default (eg. in Opera). If your app provides right-click actions, always ensure there is an alternative way to bring them up.
My problem came from the fact that on one side I was using the insanely great jQuery live function for click and the oncontextmenu attribute on the other. (Using onclick and oncontextmenu was not a problem).
I've just modified my $.live("click"...) function by catching the event and not firing the rest when e.which is 3.
Problem solved!