scrollIntoView() preventing other listeners from triggering - javascript

I am using scrollIntoView() on an event target to ensure it is visible in its parent which has overflow: scroll, this is mainly for accessibility reasons.
My problem is that I also have click events associated with this element and when a click event also fires the focus e.target.scrollIntoView() is preventing other events from firing.
This is my current example:
http://jsfiddle.net/omhuorpr/
require([
"dojo/query",
"dojo/on"
], function(query, on){
var el = query('#someElement');
el.on('click', function(e){
e.preventDefault();
console.log("clicked");
});
el.on('focus', function(e){
console.log("focus");
e.target.scrollIntoView();
});
});
If you click on the element and check the console it will only fire the focus, if you re-click it then fires the click. Ideally I want to fire both at the same time or just the click.
A simple fix that works is to move this process to the end of the execution stack via a timeout however ideally I want to find out why scrollIntoView() is preventing other events from firing.

It's because the focus event fires on mouse down, and the click event fires on mouse up. If the element has scrolled so that the mouse pointer is no longer over the element by the time you release the button, the click event won't fire. However, if the mouse pointer is still over the element after the scroll, the click will fire.
If you increase the font size in your fiddle and click near the top, you'll see the click fire. If you click near the bottom and the element scrolls out from under the pointer, the click event won't fire. E.g.
#someElement {
font-size:80px;
}
http://jsfiddle.net/omhuorpr/1/

Related

jQuery clone html element dynamically on touchstart and then receive touchmove events

I have some elements (widgets) in a div named tools. When I click on those elements I want to drag and drop them but not them directly. I clone the element in a new item named newnode and this is what I want to drag around the editor.
The trick that is working on desktop is to trigger an event on the new element, just $("#newnode").trigger(e);
$("#tools").on("mousedown touchstart",".widgets", function( e ) {
InitialX=$(this).offset().left;
InitialY=$(this).offset().top;
$("#newnode").css("left",InitialX);
$("#newnode").css("top",InitialY);
$("#newnode").css("width",$(this).width());
$("#newnode").css("height",$(this).height());
$("#newnode>svg").html($($(this).find("g")[0]).clone());
$("#newnode").show();
$("#newnode").trigger(e);
});
The code is working fine for desktop/computers but not for mobiles. When I send the trigger on desktop, the new cloned items start to receive events, being the new focused item, but when I do the same on mobiles tools/widget elements keeps receiving the events
I also added this code to see what was happening with the events, and yes, the old events is being sent to the non-cloned item.
$("#tools").on("blur focus focusin focusout load resize scroll unload click " +
"dblclick mousedown mouseup mousemove mouseover mouseout mouseenter " +
"mouseleave change select submit keydown keypress keyup error" +
"touchstart touchend touchmove", ".widgets",function(e){
console.log(e.type);
});
#newnode has z-index: 100000; to force to be over the original element.
to see an example on jsfiddle:
https://jsfiddle.net/hamboy75/v0br1cq6/44/
If you on computer, change to mobile in inspector, and you will see how it is different in log. Just click over a menu (duplicated will appear in gray color), without unclicking move a bit the mouse.
When viewed as computer newnode will receive events. When viewed as mobile widget will receive the events.
Finally i found a solution that works for my needs even if it is not exactly what i wanted (i wanted to receive all events in the new cloned item). Instead of adding the next line to receive the events in the #newnode element
$("#newnode").trigger(e);
i use the next code to receive events on #tools>.widgets, and if #newnode is visible resend the events to it using trigger.
$("#tools").on("mouseup mousedown mousemove touchstart touchend touchmove touchcancel", ".widgets",function(e){
if($("#newnode").is(":visible"))
$("#newnode").trigger(e);
});

Internet Explorer leaks click event after adding an overlay in a jQuery mousedown handler

In a mousedown event-handler of a div another new div is created and appended to the body.
This new div has position:fixed (can also be position:absolute) and has 100% width and 100% height. Therefore it immediately covers the source div which triggered the mouse down event.
Now with the latest Google Chrome (v30), latest Firefox (v24), Opera v12.16 and even with a older Safari v5.1.1 (on Windows) after the mousedown event no click event gets fired on an event listener attached to the body.
Only Internet Explorer (both 9 and 10) does fire the click event on the body afterwards! Why? And how can this be prevented? Is this actually a bug in IE?
The HTML:
<div class="clickme">Click me</div>
The CSS:
.clickme {
background-color: #BBB;
}
.overlay {
position: fixed; /* or absolute */
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: #000;
}
The JavaScript:
$(document).on('click', function(event) {
console.log('body click');
});
$('.clickme').on('mousedown', function(event) {
console.log('div mousedown');
var mask = $('<div></div>');
mask.attr('class', 'overlay');
mask.appendTo('body');
});
Here is a the example with some additional comments: http://jsfiddle.net/Fh4sK/5/
After clicking the "Click me" div, only
div mousedown
should be written to the console, but in Internet Explorer it actually is
div mousedown
body click
I appreciate any help!
EDIT 1:
I found some resources describing the conditions when to trigger a click event:
http://www.quirksmode.org/dom/events/click.html:
"click - Fires when a mousedown and mouseup event occur on the same element."
http://www.w3.org/TR/DOM-Level-3-Events/#events-mouseevent-event-order
"...in general should fire click and dblclick events when the event target of the associated mousedown and mouseup events is the same element with no mouseout or mouseleave events intervening, and should fire click and dblclick events on the nearest common ancestor when the event targets of the associated mousedown and mouseup events are different."
I'm not 100% sure what the "correct" behaviour now actually should be (maybe IE is the only browser which handles it right?). From the last sentence, it seems that it is correct to fire the click event on the body, because the body is the "nearest common ancestor" of both div elements. There are some other statements on the referenced w3.org page above, which describe the behaviour if an element gets removed, but again I'm not sure if this applies here, as no element gets removed, but covered by an other element.
EDIT 2:
#Evan opened a bug report asking Microsoft to drop the described behaviour: https://connect.microsoft.com/IE/feedback/details/809003/unexpected-click-event-triggered-when-the-elements-below-cursor-at-mousedown-and-mouseup-events-are-different
EDIT 3:
In addition to Internet Explorer, Google Chrome recently started to have the same behaviour: https://code.google.com/p/chromium/issues/detail?id=484655
I bumped into this issue too. I decided I'd make a jQuery plugin to solve this issue and put it on GitHub.
It's all here, feedback is welcome : https://github.com/louisameline/XClick
#mkurz : thanks for finding that W3 directive, you saved me a lot of time.
#vitalets : I solved this issue because I use select2 like you (you led me to this topic). I'll fork the select2 repo and leave a message for the people interested in this.
I'll see if I can ask the Microsoft people to take a look at it and hopefully change that annoying click behavior.
I also struggled with such behavior. I've modified your fiddle to find out how all mouse events are triggered with dynamically created overlay:
http://jsfiddle.net/Fh4sK/9/
So, when mousedown handler of some element shows overlay on the same place where mousedown occured:
Chrome, FF:
mousedown triggered on element
mouseup triggered on overlay
click does not trigger
IE:
mousedown triggered on element
mouseup triggered on overlay
click triggered on BODY(!)
The same behavior if you hide mask in mousedown.
This issue can lead to weird things in IE with modals, masks, overlays etc..
Intresting thing: if you show overlay in mouseup instead of mousedown - everything works
The solution I found is to use mouseup instead of mousedown.
It can't be explained normally because both these events are triggered before click.
In that case all mouse events are triggered on element:
http://jsfiddle.net/Fh4sK/11/
mousedown triggered on element
mouseup triggered on element
click triggered on element
Hope this helps.
You could filter the document click by the target to exclude clicks on that div:
$(document).on('click', function(event) {
if(!$(event.target).hasClass('clickme')) {
console.log('body click');
}
});
If you want to stop bubbling of the click event, try this : (I don't have IE to test)
$(document).on('click', function(event) {
console.log('body click');
});
$('.clickme').on('mousedown', function(event) {
console.log('div mousedown');
});
$('.clickme').on('click', function(event) {
event.stopPropagation();
});
Click, mousedown and mouseup are differents events, independant.
here is a similar question

Blur event stops click event from working?

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.

Events-- 'mouseup' not firing after mousemove

I am trying to drag an image with Javascript (no libraries). I am able to listen to mousedown and mousemove events. For some reason, I am not able to capture the mouseup event after mousemove. (I can capture mouseup if it is a click but not if it is a drag).
I have tried to listen to the event on document, window, and the image.
Here's the url to my test page:
https://dl-web.dropbox.com/get/Public/move.html?w=74a0d498
Any help on this would be greatly appreciated!
Found the issue, if it is going to be of help to anyone:
I added event.preventDefault(); in the mousedown event and now I am getting mouseup notifications.
Strangely, I've found that when I set my text as unselectable using the below CSS, that inhibits the mouseup event from firing as well -- perhaps this will help someone else.
-moz-user-select: none;
-khtml-user-select: none;
user-select: none;
I have seen the mouseup event not fire on my target div because its parent div began consuming drag events. I clicked my target div and this caused my mousedown and mousemove handlers to run. I was expecting to see a mouseup but I did not.
In my case, I had a parent div living beneath my target div in the same location where I launched my mouse button click, and instead of bubbling up the mouseup event on my document once I let go of the left mouse button, I instead got a dragend event fire on its parent div.
The solution for me was simply to set user-select: none; CSS property on the parent div which houses my target div and to make sure I set user-select: text on my target div. This property seems to disable dragging for my parent div and because it has stopped consuming drag events, my mouseup event now properly bubbles its way up.
I presume the reason why this might happen is because the browser starts thinking your mouse move is actually part of a drag event on another element, at which point it seems to stop reporting the standard mouse events and switches to drag events instead.
I was running into this exact same issue! Adding event.preventDefault(); worked for me but I was forced to add it to both the mousedown and mousemove functions.
Neither of the above answers reliably worked to ensure a mouseup event.
Here's what I discovered works consistently:
document.querySelector('html').addEventListener('mouseup', function (e) {
console.log("html mouseup");
var evt = document.createEvent("MouseEvents");
evt.initEvent("mouseup", true, true);
document.getElementById('drag-me').dispatchEvent(evt);
});
If mouseup fires on target element, it does not fire on html, and if it did not fire on target, it will fire on html.
For above two methods:
add e.preventDefault() in mouseup event
add user-select: none CSS rule
Tried on Chrome 87 but both useless. Finally I add a additional mouseout event listener and it fired when drag out of element.
The event which you want to fire in mouseup , You can fire in mousedown and inside the function write event.stopPropagation().
It will stop the drag event.
It is going to be of help to anyone:
I recommended you use window instead of document when adding Event mouseup
The problem is in selection. Some elements are draggable, when user - having unknowingly uncollapsed selection (likely across many non-text elements), starts moving his mouse with mouse button pushed down he drags these elements alongside the movement of his mouse.
Try setting draggable="false" on html elements where mousedown event starts or if it doesn't work try clearing or collapsing selection on mousedown.
Clear:
getSelection().setPosition(null);
A bit cleaner, collapse:
const selection = getSelection();
selection.setPosition(selection.anchorNode, selection.anchorOffset)
Setting draggable="false" worked for me when working with mousedown event starting with an <img> element and clearing selection worked when I had selections spanning over multiple elements on a control panel an this made some input[type="range"] elements unusable (click event worked but not thumbnail dragging).

Drop Down Box keeps scrolling up

I implemented this to my site from an earlier question but for some reason on Firefox and IE the drop-down box scrolls up by itself. I can't figure out why!
Just click News Feed and as the box drops down, it automatically drops up. It's supposed to drop down and if I click on newfeed again or outside, it's supposed to drop up. But it doesn't do that, it just springs back up.
I am using the JavaScript. What is going on here?
$('#bottom').click(function() {
$('#content').slideDown();
});
$(document).click(function(e) {
if (e.target.id !='bottom') {
$('#content').slideUp();
}
});
Change your #bottom event handler a bit to prevent the bubbling of the click event all the way up to the document:
//it is important to declare the `event` variable as a parameter of this anonymous function so it can be accessed inside the function
$('#bottom').click(function(event) {
event.stopPropagation();
$('#content').slideDown();
});
What's happening with your code is that the event handler for the #bottom element is being triggered, then after that the event handler for clicking on the document fires since the click event bubbles up the DOM. event.stopPropagation() will stop the event from bubbling.
Docs for event.stopPropagation(): http://api.jquery.com/event.stoppropagation/

Categories