It seems the drop event is not triggering when I would expect.
I assume that the drop event fires when an element that is being dragged is releases above the target element, but this doesn't seem to the the case.
What am I misunderstanding?
http://jsfiddle.net/LntTL/
$('.drop').on('drop dragdrop',function(){
alert('dropped');
});
$('.drop').on('dragenter',function(){
$(this).html('drop now').css('background','blue');
})
$('.drop').on('dragleave',function(){
$(this).html('drop here').css('background','red');
})
In order to have the drop event occur on a div element, you must cancel the ondragenter and ondragover events. Using jquery and your code provided...
$('.drop').on('drop dragdrop',function(){
alert('dropped');
});
$('.drop').on('dragenter',function(event){
event.preventDefault();
$(this).html('drop now').css('background','blue');
})
$('.drop').on('dragleave',function(){
$(this).html('drop here').css('background','red');
})
$('.drop').on('dragover',function(event){
event.preventDefault();
})
For more information, check out the MDN page.
You can get away with just doing an event.preventDefault() on the dragover event. Doing this will fire the drop event.
In order for the drop event to fire, you need to assign a dropEffect during the over event, otherwise the ondrop event will never get triggered:
$('.drop').on('dragover',function(event){
event.preventDefault();
event.dataTransfer.dropEffect = 'copy'; // required to enable drop on DIV
})
// Value for dropEffect can be one of: move, copy, link or none
// The mouse icon + behavior will change accordingly.
This isn't an actual answer but for some people like me who lack the discipline for consistency. Drop didn't fire for me in chrome when the effectAllowed wasnt the effect I had set for dropEffect. It did however work for me in Safari. This should be set like below:
ev.dataTransfer.effectAllowed = 'move';
Alternatively, effectAllowed can be set as all, but I would prefer to keep specificity where I can.
for a case when drop effect is move:
ev.dataTransfer.dropEffect = 'move';
Related
I am trying to attach a drop event to an HTML div:
document.getElementById('sub-main').addEventListener("drop",
() => {console.log('DROP')});
but it does not fire. Adding a click event for test purposes worked - this click event fires:
document.getElementById('sub-main').addEventListener("click",
() => {console.log('Click')});
I have read that returning false from ondragover will help:
document.getElementById('sub-main').addEventListener("ondragover",
() => {return false});
document.getElementById('sub-main').addEventListener("drop",
() => {console.log('Drop')});
But this does not work either. I tried setting draggable to true:
document.body.setAttribute('draggable', true);
But also no luck!
Logging the event listeners to the console with getEventListeners() shows all the events, even any random event name I chose:
getEventListeners(document.getElementById('sub-main'));
But the drop event still does not fire. Any ideas?
To enable dragging you should first of all disable the default behavior of the browser using the dragover event.
add this piece of code and it will work.
document.addEventListener("dragover", function(event) {
event.preventDefault();
});
I did some trial and error in plunker and came to the conclusion that you first need to set the eventListener on the document(dont use.getElementById('sub-main') gives error in plunker at least) and then specify in the eventlistener on which target to fire on. And for it to detect that it can be dropped you need to have other eventListeners like: drag, dragstart, dragover, dragleave and dragenter. Just follow what they did here on MDN.
Is it possible to get the dom element that was clicked from the blur event.
myTxtBox.blur(function (e) {
var myTxtBoxClass = e.target.className
var getClassOfElementclicked == //get the class of clicked element
});
I think you should use .click(function(){}); to get clicked object. Then you can set it to blur. Right now it is unclear what initiates the blur event in the first place.
If you want to see which object that is currently "blurring" is clicked, you could assign a class to the objects when they blur and assign click event to this class.
blur can be called for more than just clicking away from a control--the user could have tabbed away. If all you're interested in is the target of a click event, then you can register a handler for clicks.
However, if you're more interested in the elements that gain & lose focus in close proximity to one another (they are two separate events, so you can't really consider a blur to have a "newly-focused target" attribute, you can use something like this:
$('input').blur(function (e) {
console.log('lost focus: ', e.target);
});
$('input').focus(function (e) {
console.log('gained focus: ', e.target);
});
http://jsfiddle.net/Palpatim/QUDED/
Also, be sure to see the discussion of blur() in the jQuery documentation: the event doesn't bubble in IE, so depending on your use case, you may wish to use the focusout event instead.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
jQuery - How can I bind all events on a DOM element?
Imagine, if we want to make some element completely initeractable.
We could, of course bind a prevent default for a click event as follows:
$('form *').bind('click', function(event) {
event.preventDefault();
});
But that's just one event, and there are many more, like hover, focus, selectstart, and many more.
We could specify them all in one line like 'click focus hover dblclick blur selectstart' but that doesn't make much sense and is not easy to maintain.
So, is it possible to bind an event listener without discriminating for the type of the event? Maybe some native JavaScript listeners allow it?
No such possibility because not all elements support same events and not all events behave in the same way. You always have to explicitly provide a list of events whether defined statically or dynamically by a script that spits out event names.
Even though I linked to a script that creates an array of event names, these are made on one element only. You should of course be generating this with a more complex and slower script that enumerates over all elements in question and adds missing events. Using Javascript objects as associative array for faster searching whether a particular event has been added or not.
A better suggestion
What you're trying to do is likely a highly over-engineered solution. When I'm creating a demo clickable interface that should disable some elements (be it links, buttons or anything else) I rather do it by defining a CSS class that disables an element in question and have a simple script that does disabling afterwards.
You could leverage this even further by also providing which events you'd like to disable on particular element (with default being a click event).
<!-- no events; use defaults -->
No-follow link
<button class="disable">Nothing happens</button>
<!-- provide events -->
No-follow link
<form class="disable" data-events="submit">...</form>
Script
$(function() {
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(ctx.data("events") || "click", disable);
});
});
Using smart defaults
Upper example defines one single event default. click event. This is fine and works in majority of cases, but not in all. form elements for instance would always have to define submit event that should be disabled. So. Smart defaults then. We should also consider the fact that list events that need supression is usually short. And if we cover majority of cases using defaults we only have a small overhead on those elements that actually do deviate from defaults.
$(function() {
// click is still default event
// this object defines per element events that aren't just click
var extraDefaults = {
form: "submit"
};
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(
// use inline-defined events
ctx.data("events") ||
// use extra defaults if present
extraDefaults[this.tagName.toLower()] ||
// just use default click event
"click",
disable);
});
});
You can bind most jQuery events like this :
$("#elementID").on(Object.keys(jQuery.event.fixHooks).join(" "), function(e) {
e.preventDefault();
});
This will preventDefault on the following events :
click dblclick mousedown mouseup mousemove mouseover mouseout
mouseenter mouseleave keydown keypress keyup contextmenu
FIDDLE
Well after considering all the options, it still does not look convenient for all this event hustling. As it also has to bind the handlers for each event individually the script will hit the performance as well.
I am going to stick with a much simpler solution, just putting a div with transparent bg on top to cover our element.
$('form').css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url(1px_transparent.png);"></div>'));
Which is going to automatically fill the whole area of the element, alternatively, we can use a half-transparent picture so it will be also understood by a user that this is locked element, and would not cause confusion.
And to unlock we simply remove the .mask div from our element.
EDIT
New Fiddle: http://jsfiddle.net/YAdXk/8/
Actually we can disable tabbing by setting tabindex attribute to -1
.find('input,textarea,select').attr('tabindex','-1');
The updated fiddle prevents from tabbing as well.
EDIT2
OR, we can extend jQuery to use our custom lock() and unlock() functions on any element.
See the last fiddle: http://jsfiddle.net/YAdXk/13/
(function($) {
$.fn.lock= function() {
return this.each(function() {
$(this).css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url('+transparent_picture+');"></div>')).find('input,textarea,select').attr('tabindex','-1');
});
};
$.fn.unlock= function() {
return this.each(function() {
$(this).find('*').removeAttr('tabindex').filter('.mask').remove();
});
};
})( jQuery )
var all_events = "click blur focus mouse"; //etc...
$('form *').bind(all_events, function(event) {
event.preventDefault();
});
Now is easier to maintain ;)
jQuery defines all shortcut event types here, so you can use that string to store all events for re-use:
var events = "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu";
$('button').bind(events, function() {
// hey
});
Yes, it is possible, to catch all events of one type at once! But you'll need to specify all the event types explicitly.
Your code example of "form *" is inefficient, and would not catch events on elements that are added after your code executes.
Because of the bubbling effect of javascript events, you can assign a catch all event handler on the most parent element, eigther $("form") or $("body"), and add preventDefault() to that.
Example code:
$("a").on("click", function() {
$("body").append("<p>Clicked...</p>");
});
$("body").on("click", function(e) {
e.preventDefault();
});
with:
<div>
<p>Click on me</p>
</div>
On JSfiddle: http://jsfiddle.net/erlang/EHeBK/
The concept of catching all events on a parent element, is often referred to as event delegation.
It appears that the Blur event stops the click event handler from working? I have a combo box where the options only appear when the text field has focus. Choosing an option link should cause an event to occur.
I have a fiddle example here: http://jsfiddle.net/uXq5p/6/
To reproduce:
Select the text box
Links appear
Click a link
The blur even occurs and the links disappear
Nothing else happens.
Expected behavior:
On step 5, after blur occurs, the click even should also then fire. How do I make that happen?
UPDATE:
After playing with this for a while, it seems that someone has gone to great lengths to prevent an already-occurred click event from being handled if a blur event makes the clicked element Un-clickable.
For example:
$('#ShippingGroupListWrapper').css('left','-20px');
works just fine, but
$('#ShippingGroupListWrapper').css('left','-2000px');
prevents the click event.
This appears to be a bug in Firefox, since making an element un-clickable should prevent future clicks, but not cancel ones that have already occurred when it could be clicked.
Other things that prevent the click event from processing:
$('#ShippingGroupListWrapper').css('z-index','-20');
$('#ShippingGroupListWrapper').css('display','none');
$('#ShippingGroupListWrapper').css('visibility','hidden');
$('#ShippingGroupListWrapper').css('opacity','.5');
I've found a few other questions on this site that are having similar problems. There seem to be two solutions floating around:
Use a delay. This is bad because it creates a race condition between the hiding and the click event handler. Its also sloppy.
Use the mousedown event. But this isn't a great solution either since click is the correct event for a link. The behavior of mousedown is counter-intuitive from a UX perspective, particularly since you can't cancel the click by moving the mouse off the element before releasing the button.
I can think of a few more.
3.Use mouseover and mouseout on the link to enable/disable the blur event for the field. This doesn't work with keyboard tabing since the mouse is not involved.
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the body element, not the one that was clicked. But maybe if there was a reliable way to know either 1. which element now has focus or two, which element caused the blur (not which element is blurring) from within the blur handler. Also, is there any other event (besides mousedown) that fires before blur?
click event triggers after the blur so the link gets hidden. Instead of click use mousedown it will work.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
Other alternative is to have some delay before you hide the links on blur event. Its upto you which approach to go for.
Demo
You could try the mousedown event instead of click.
$('.ShippingGroupLinkList').live("mousedown", function(e) {
alert('You wont see me if your cursor was in the text box');
});
This is clearly not the best solution as a mousedown event is not achieved the same way for the user than a click event. Unfortunately, the blur event will cancel out mouseup events as well.
Performing an action that should happen on a click on a mousedown is bad UX. Instead, what's a click effectively made up of? A mousedown and a mouseup.
Therefore, stop the propagation of the mousedown event in the mousedown handler, and perform the action in the mouseup handler.
An example in ReactJS:
<a onMouseDown={e => e.preventDefault()}
onMouseUp={() => alert("CLICK")}>
Click me!
</a>
4.The best solution would be something like:
$('#ShippingGroup').blur(function()
{
if($(document.activeElement) == $('.ShippingGroupLinkList'))
return; // The element that now has focus is a link, do nothing
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
Unfortunately, $(document.activeElement) seems to always return the
body element, not the one that was clicked. But maybe if there was a
reliable way to know either 1. which element now has focus or two,
which element caused the blur (not which element is blurring) from
within the blur handler.
What you may be looking for is e.relatedTarget. So when clicking the link, e.relatedTarget should get populated with the link element, so in your blur handler, you can choose not to hide the container if the element clicked is within the container (or compare it directly with the link):
$('#ShippingGroup').blur(function(e)
{
if(!e.relatedTarget || !e.currentTarget.contains(e.relatedTarget)) {
// Alt: (!e.relatedTarget || $(e.relatedTarget) == $('.ShippingGroupLinkList'))
$('#ShippingGroupListWrapper').css('display','none'); // hide it.
}
}
(relatedTarget may not be supported in older browsers for blur events, but it appears to work in latest Chrome, Firefox, and Safari)
If this.menuTarget.classList.add("hidden") is the blur behavior that hides the clickable menu, then I succeeded by waiting 100ms before invoking it.
setTimeout(() => {
this.menuTarget.classList.add()
}, 100)
This allowed the click event to be processed upon the menuTarget DOM before it was hidden.
I know this is a later reply, but I had this same issue, and a lot of these solutions didn't really work in my scenario. mousedown is not functional with forms, it can cause the enter key functionality to change on the submit button. Instead, you can set a variable _mouseclick true in the mousedown, check it in the blur, and preventDefault() if it's true. Then, in the mouseup set the variable false. I did not see issues with this, unless someone can think of any.
I have faced a similar issue while using jQuery blur, click handlers where I had an input name field and a Save button. Used blur event to populate name into a title placeholder. But when we click save immediately after typing the name, only the blur event gets fired and the save btn click event is disregarded.
The hack I used was to tap into the event object we get from blur event and check for event.relatedTarget.
PFB the code that worked for me:
$("#inputName").blur(function (event) {
title = event.target.value;
//since blur stops an immediate click event from firing - Firing click event here
if (event.relatedTarget ? event.relatedTarget.id == "btnSave" : false) {
saveBtn();
}
});
$("#btnSave").click(SaveBtn)
As already discussed in this thread - this is due to blur event blocking click event when fired simultaneously. So I have a click event registered for Save Btn calling a function which is also called when blur event's related Target is the Save button to compensate for the click event not firing.
Note: Didnt notice this issue while using native onclick and onblur handlers - tested in html.
I have a div that acts like a drop-down. So it pops-up when you click a button and it allows you to scroll through this big list. So the div has a vertical scroll bar. The div is supposed to disappear if you click outside of the div, i.e. on blur.
The problem is that when the user clicks on the div's scrollbar, IE wrongly fires the onblur event, whereas Firefox doesn't. I guess Firefox still treats the scrollbar as part of the div, which I think is correct. I just want IE to behave the same way.
I've had a similar problem with a scrollbar in an autocomplete dropdown. Since the dropdown should be hidden when the form element it is attached to loses focus, maintaining focus on the correct element became an issue. When the scrollbar was clicked, only Firefox (10.0) kept focus on the input element. IE (8.0), Opera (11.61), Chrome (17.0) and Safari (5.1) all removed focus from the input, resulting in the dropdown being hidden, and since it was hidden, click events would not fire on the dropdown.
Fortunately, the shift of focus can be easily prevented in most of the problem browsers. This is done by canceling the default browser action:
dropdown.onmousedown = function(event) {
// Do stuff
return false;
}
Adding a return value to the event handler sorted out the problem on all browsers except IE. Doing this cancels the default browser action, in this case the focus shift. Also, using mousedown instead of click meant that the event handler would be executed before the blur event fired on the input element.
This left IE as the only remaining problem (no surprise there). It turns out that there is no way to cancel the focus shift on IE. Fortunately, IE is the only browser that fires a focus event on the dropdown, meaning focus on the input element can be restored with an IE-exclusive event handler:
dropdown.onfocus = function() {
input.focus();
}
This solution for IE is not perfect, but while the focus shift is not cancelable, this is the best you can do. What happens is that the blur event fires on the input, hiding the dropdown, after which focus fires on the now hidden dropdown, which restores focus on the input and triggers showing the dropdown. In my code it also triggers repopulating the dropdown, resulting in a short delay and loss of the selection, but if the user wants to scroll the selection is probably useless anyway, so I deemed this acceptable.
I hope this is helpful, even though my example is slightly different than in the question. From what I gathered, the question was about IE firing a blur event on the dropdown itself, rather than the button that opened it, which makes no sense to me... Like my use of a focus event handler indicates, clicking on a scrollbar should move focus to the element the scrollbar is part of on IE.
Late answer, but I had the same issue and the current answers didn't work for me.
The hover state of the popup element works as expected, so in your blur event you can check to see if your popup element is hovered, and only remove/hide it if it isn't:
$('#element-with-focus').blur(function()
{
if ($('#popup:hover').length === 0)
{
$('#popup').hide()
}
}
You'll need to shift focus back to the original element that has the blur event bound to it. This doesn't interfere with the scrolling:
$('#popup').focus(function(e)
{
$('#element-with-focus').focus();
});
This does not work with IE7 or lower - so just drop support for it...
Example: http://jsfiddle.net/y7AuF/
I'm having a similar problem with IE firing the blur event when you click on a scrollbar. Apparently it only happens n IE7 and below, and IE8 in quirksmode.
Here's something I found via Google
https://prototype.lighthouseapp.com/projects/8887/tickets/248-results-popup-from-ajaxautocompleter-disappear-when-user-clicks-on-scrollbars-in-ie6ie7
Basically you only do the blur if you know the person clicked somewhere on the document other than the currently focused div. It's possible to inversely detect the scrollbar click because document.onclick doesn't fire when you click on the scrollbar.
This is an old question but since it still applies to IE11 here is what I did.
I listen to the mousedown event on the menu and set a flag on this event. When I catch the blur event, if the mousedown flag is on, I set the focus back. Since Edge, FF and Chrome won't fire the blur event but will fire the mouseup event (which IE won't), I reset the mousedown flag on the mouseup for them (on the blur for IE).
mousedown: function (e) {
this.mouseddown = true;
this.$menu.one("mouseup", function(e){
// IE won't fire this, but FF and Chrome will so we reset our flag for them here
this.mouseddown = false;
}.bind(this));
}
blur: function (e) {
if (!this.mouseddown && this.shown) {
this.hide();
this.focused = false;
} else if (this.mouseddown) {
// This is for IE that blurs the input when user clicks on scroll.
// We set the focus back on the input and prevent the lookup to occur again
this.skipShowHintOnFocus = true; // Flag used to avoid repopulating the menu
this.$element.focus();
this.mouseddown = false;
}
},
That way the menu stays visible and user doesn't loose anything.
Use focusout, and focusin (IE specific events)
$(document).bind('focusout', function(){
preventHiding = false;
//trigger blur event
this.$element.trigger('blur');
});
$(document).bind('focusin', function(){
preventHiding = true;
});
$(document).bind('blur', function(){
// Did anyone want us to prevent hiding?
if (this.preventHiding) {
this.preventHiding = false;
return;
}
this.hide();
});
I had the same probleme. Resolved by putting the menu in a wrapping (bigger) div. With the blur applied to the wrapper, it worked!
Perhaps try adding the tabindex attribute set to -1 to the div node.
I don't think this is an IE issue.
It's more a case of how to design your interaction and where to handle which event.
If you have a unique css-class-accessor for the related target, canceling a blur event can be done by checking the classList of the event.relatedTarget for the element you want to allow or disallow to initiate the blur event. See my onBlurHandler from a custom autocomplete dropdown in an ES2015 project of mine (you might need to work around contains() for older JS support):
onBlurHandler(event: FocusEvent) {
if (event.relatedTarget
&& (event.relatedTarget as HTMLElement).classList.contains('folding-select-result-list')) {
// Disallow any blur event from `.folding-select-result-list`
event.preventDefault();
} else if (!event.relatedTarget
|| event.relatedTarget
&& !(event.relatedTarget as HTMLElement).classList.contains('select-item')) {
// If blur event is from outside (not `.select-item`), clear the suggest list
// onClickHandler of `.select-item` will clear suggestList as configured with this.clearAfterSelect
this.clearOptions(this.clearAfterBlur);
}
}
.folding-select-result-list is my suggestions-dropdown having 'empty spots' and 'possibly a scrollbar', where I don't need this blur event.
.select-item has it's own onClickHandler that fires the XHR-request of the selection, and closes the dropdown when another property of the component this.clearAfterSelect is true.