Why is mouseenter event triggered when page below is scrolled? - javascript

I noticed mouseenter event triggered when mouse is untouched but the page below the cursor is scrolled.
Check out this fiddle: http://jsfiddle.net/F3EwW/
Steps to reproduce:
Click on a li
Use up/down arrow keys to scroll the items
You would notice the mouseenter event getting triggered when the li below is scrolled to the view.
Note: To notice this behavior, make sure the mouse cursor is above li and leave it untouched.
Initially, I accepted this as a default behavior and went on with a work around to handle this in my code.. but then I got curious and wanted to verify this behavior in any documentation which I couldn't find it anywhere.
Does anyone know if this behavior is documented anywhere in spec or any authentic webpage?
I looked up w3spec event scroll and mouse event order, but couldn't locate anything about this.
Also the spec description for mouseenter is as follows,
A user agent must dispatch this event when a pointing device is moved onto the boundaries of an element or one of its descendent elements. This event type is similar to mouseover, but differs in that it does not bubble, and must not be dispatched when the pointer device moves from an element onto the boundaries of one of its descendent elements.
In Chrome, you would notice mouseover to be triggered as well. I have posted a question and a bug report already on this.

You realize that you have $('li').mouseenter(function () { ?
this caused the mouseenter event to be binded to each and every one of this li elements so when you are using the up and down key to scroll and your mouse is still inside the ul it keeps entering a new li. This was not an unintended feature the mouse in entering the new element.
The behaviour you are looking for is more something like this:
$("element").bind("mousemove mouseenter", function(event) {
//...
});
Also you need to realize the DOM understands the movement of the mouse relative to the document not where your mouse is on your screen as your OS does.

Related

Preventing iPad top page drag

I'm trying for numerous days to solve the following issue.
I have a menu located on the top of the page which needed to be open using swipedown event (I'm using Hammer.js jQuery version).
Problem is, every time I try to interact using swipes I either scroll the page (swipeup) or pulling the page down same as described in the following question.
Here is what I've tried so far:
overflow: hidden; on the body element with an inner container with overflow: auto, swipe on top element still triggered document scroll.
Setting preventDefault on the document also disabled lower elements events in the DOM hierarchy and by that I had no swipe events working in the page.
Also tried using stopPropagation on the actual element when the event occurs, to prevent the bubbling up the chain for the event, the result cause the object to not respond to the events (swipes) and document scroll worked with no problems.
Any ideas how can I still keep page scroll but also when using common gestures, such as swipedown/swipeup, on specific elements that the element only will be affected?
Here is an example using JSFiddle, to better demonstrate the issue.
Would appreciate ideas/thoughts
I don't know if this will help, but I've always liked to use drag more than swipe. Using Hammer on my projects, swipes were a bit finicky. And from a UX standpoint, drag feels instantaneous vs a swipe. Much like, mousedown vs mouseup/click. So in instances where it's appropriate, and I believe in the case of showing swipey menu it is, I'd opt for drag.
Replacing your example with drag rather than swipe, and also using CSS transition, -webkit-transition, rather than jQuery's animate (drag will trigger like a mousemove, vs a click or a mouseup) seemed to make it work.
Hammer('.nav').on('dragdown', function(e){
e.gesture.preventDefault()
$(".blue").html("down")
$('.nav').css({"top":"0px"});
})
.on('dragup', function(e){
e.gesture.preventDefault()
$(".blue").html("dragup")
$('.nav').css({"top":"-150px"});
});
//Added in CSS, for .nav
.nav {-webkit-transition:0.5s top;}
Example
This does still have the page overscroll. A preventDefault() on document.ontouchstart would could fix that but that breaks scrolling. You might be able to do a selective preventDefault() by checking the scrollOffset perhaps. But I guess in the long run, I'd recommend something like iScroll.
Example
Also maybe tweak the hitbox for the drag to be a bit larger. Which I did in the last example. I attached the dragdown event on the document instead of the "menu" so the menu doesn't have to be visibly bigger.
Hammer(document).on("dragdown",function(e){
//calculate ratio of first touch from top
var pos=e.gesture.startEvent.center.pageY/window.innerHeight
if(pos<0.2){ //drag occurs in the first 20% of the screen
menu.style.marginTop="0px" //or animate here
e.gesture.preventDefault()
e.gesture.stopPropagation();
}
})
You should use the preventDefault function of the orginal gesture, to stop the browsers default behaviour, see here: https://github.com/EightMedia/hammer.js/wiki/Event-delegation-and-how-to-stopPropagation---preventDefaults
When you have a div element, on which you want to register swipe events, you would do the following:
$('#swipeDiv').hammer().on("swipe", function(ev) { ev.gesture.preventDefault(); });
That should prevent the scrolling of the page, but only if the swipe happens on the div element.

mousemove event is triggered onscroll even when mouse was not moved on Chrome

I was trying to answer an issue with custom drop down, but challenged by an inconsistent behavior in Chrome and Firefox.
DEMO: http://jsfiddle.net/fyeht/ [Added scroll event for more clarity]
See below image, The list items can be navigated using arrow keys.
To Reproduce the issue:
Open console in Chrome (F12)
Click on an item in the list (you would notice some events getting logged in the console)
Use down arrow key to navigate to the next item in the list
Finally, the issue is noticed when you reach the last item in the view and hitting down arrow would scroll. Check the log to see 'scroll', 'mouse enter' and 'mouse move' [check the new demo]
The issue is after reaching the end of items in view, it scrolls. Even though the mouse is untouched, it triggers mouseenter and mousemove events in Chrome. In FF, on scroll it triggers just the mouseenter which make sense.
Question(s):
Why is mousemove triggered when mouse is untouched?
Is this just browser inconsistency? Could not find documentation on events triggered when scrolling? (never knew it did)
Submitted a bug report: https://code.google.com/p/chromium/issues/detail?id=241476
In your example, I see that both Chrome and FF are firing mouseenter DOM events whenever the mouse is left hovering over the <ul> and pressing the key down triggers the browser to scroll in order to bring the selected <li> into view.
However, only Chrome is additionally triggering mousemove events. One obvious difference already in the mouseenter event objects that the two throw is that for Chrome, MouseEvent.offsetX and MouseEvent.offsetY values are included, whereas in FF these properties are undefined. So, when that enter is triggered Chrome has already decided the mouse "has moved".
Since the MouseEvent.screenX and MouseEvent.screenY event context values do not change between scroll-triggered MouseEvent instances, one could perhaps distinguish between an "artificial" mouseenter / mousemove event and an "authentic" one by storing these values from prior events.
DOM Event Specification
The DOM Level 2 Event Specification for mousemove reads:
The mousemove event occurs when the pointing device is moved while it is over an element.
The Level 3 spec (working draft) is essentially the same:
A user agent must dispatch this event when a pointing device is moved while it is over an element.
Seems like it could down to whether one interprets "is moved" relatively or not.
Also, in the section of the Level 3 spec on mouse event order, it states that when a pointer is moved into an element, it triggers mouseover, mouseenter, and mousemove, in that order. Every case that is specified there always has those three together, so perhaps one might interpret it that if you are going to trigger the mouseenter event, you should also be triggering the mousemove event which corresponds to entering the element.
This is a nice demo.
In Chrome mouse movement for elements is definitely relative. In chrome I can get keydown and scroll events only if the mouse pointer is over the scroll bar. I can also get scroll only events if i use the wheel to scroll and leave the mouse over the scroll bar. It is and isn't very odd that scroll by dragging causes "mouse move", and "mouse over" events.
Not only are mouse move and mouse over events produced in profusion by the browser they are not a very good indication of a users intent. Infact these events are a useful entropy source.
To the degree that there are some minor differences this is in the context of how useful these "micro events" are individually. To work with them you must devise a way to filter them for user intention you want to link to higher level actions. Any reasonable method you choose to make sense of these events will probably detect these move - on scroll events as garbage. This is where your point is really worth noting and taking under consternation.
A first stage would be to Filter out events based on the elements and values of coordinates. It might help to create a state machine model. You might register and registered handlers in response to other events. You've identified this case where you'd want to change responsive state if or reaction criteria if a key element has a scroll bar. IF an element or it's parent has a vertical scroll bar throw out mouse moves with relatively high X values.
You might want to also ignore mouse overs if it's fired with a mouse move in that context. It starts to become impractical to handle each of these micro event one at a time even if you are changing state by registering or deregistering handlers. More information can be extracted by creating an event sequence fifo buffer.
Register event handlers to add a new event to the buffer. You might also want to collect information from timer events in this buffer to establish more context. You might create an object that keeps a fifo in an array. This would be like a queue but not in the sense of it being a place where the events are waiting to be processed. Instead your program is waiting to calculate patterns in the buffer and based on the patterns fire higher level events, accept or reject different types of events and extend, contract or save the contents of the buffer. You can then evaluate move events for changes in x and y and also create conditions given the patter of scroll mouse over and mouse move events you've demonstrated.
I really doubt there's a browser inconsistency here. You should create a mousemove event that prints out the x and y coordinate. You'll probably see that the mouse has indeed moved a little bit. If that's the case, try using the plugin hoverIntent to eliminate issues like this.
EDIT:
Using the up and down arrow keys, I'm now able to replicate the issue. Yeah, it sure looks like some kind of bug! I bet the mousemove coordinate delta is tiny. Maybe the cursor moves one or two pixels? I would say, to overcome this, add a check to the mousemove function that compares previous mousemove's x-y coordinates to the current mousemove's x-y coordinates. Determine if it's more than just a few pixels. If so, you know it's a real mousemove. If it's less, you can chalk that up as a chrome bug.
FURTHER EDIT:
It seems like you uncovered a bug where mousemove is being fired in chrome when it probably shouldn't be. There may be workarounds that you could figure out if you hack it enough. But the best solution might be just to avoid using mousemove in this situation. In general, mousemove is one of those expensive events that should be used only when you really need it.
This is not a bug. The mousemove is relative to element that the event is attached to. In your case, you see that your mouse is not moving because you took the browser window as the reference. But for that scrolling list, whenever the list is scrolled, the mouse pointing over some element of the list moved to over different element
Imagine that you as the Earth, a cup of coffee stand still on a table as the mouse, the scrollable list as the Sun : if you (window) don't move, the position of the cup of coffee (mouse) is at the same place for you; but for the Sun (list), it will see that the Earth and the cup of coffee are both moving.

Trigger onMouseOver when mouse pointer already is over after page load

I have several pages that are all very similar. They have some javascript rollover links (images are preloaded, then there is a onMouseOver event that calls an image swap function and finally, there is a onMouseOut event that restores the original image).
When the user clicks on a rollover link that points to another page that has a rollover link on the exact same position, the image on the new page would be expected to load on the "over" state. This is not the case in Chrome and Safari (IE and Firefox work as expected).
So... On page load, is there a way to check if the mouse is already hovering the image to swap it right away? Something like "OnMouseAlreadyOver"?
Thank you.
If you using jQuery, it works without any problems!
http://jsfiddle.net/beuae
(not only for buttons, for divs also)
Actually, jQuery is a very good framework which assures everything goes as you expect, and cross-browser. This example confirms it.
The W3C standard says
onmouseover = script [CT]
The onmouseover event occurs when the pointing device is moved onto an element. This attribute may be used with most elements.
onmousemove = script [CT]
The onmousemove event occurs when the pointing device is moved while it is over an element. This attribute may be used with most elements.
mouseover is fired on moving over the boundary of the object. mousemove happens when the mouse is already over the element.
You may need to use onmousemove (or even both).
You may need to actually do the calculation based on the element position and the mouse cursor position.
//Get Mouse Position
document.onmousemove=getMouseCoordinates;
function getMouseCoordinates(event){
ev = event || window.event;
mouseX = ev.pageX;
mouseY = ev.pageY;
}
You can't without passing a variable to the other page or using cookies to track which was hovered (and that will fail over if people do change their mouse position)
In theory you could check the mouse position and the button position however there is no way to get the mouse position unless an event is triggered, so the mouse has to move and if it move the CSS :hover should get triggered.
It's a minor issue tho, I doubt most people are going to click a link, wait for the next page and then expect that link to be hovered and ready to click again (why wouldn't anyone one to keep clicking the same button unless it does different things)
From a UX point of view I wonder if webkit doesn't have the best approach here, why port the action of one page to another.
You can use document.getElementFromPoint(mouseX, mouseY) to get the element, but the only way to get the cursor's position is via an event. The problem is, the only events are clicks and mouse movements, which require user input from the beginning, which is what you're trying to avoid.
In short, no, it's not possible to do with JavaScript. You're left with using CSS.

onmouseover fired before click and mouseout events?

Unusable links with onmouseover() got an interesting question, when I tried to answer it. After some logging experiments, I've set up http://jsfiddle.net/RnGxP/1/. The last two examples work as expected, the hide when clicking on "Close" or leaving the "Close" div.
The first two examples set a new innerHTML to the div whenever the mouse moves in it (I'd never do that myself, but...).
So, when moving the mouse into one of them they get expanded. And moving the mouse further on a link or the "close" div, more move events get fired.
But then, clicking on the close button in the second example - without moving the mouse -, instead of a click event two mousemove events are fired! What exactly happens here? I can understand that the click event gets lost in some way (loosing its target?) when resetting innerHTML, but why is the mousemove event fired before?
You are rewriting the innerHTML of the div element in the mouseover event. This means that each time you move the mouse it is in fact moving over a new node, which triggers a new mouseover event on that node, which bubbles up to the div element, which rewrites the innerHTML etc. etc.
So by the time the mouseout event fires on the inner div, the mouseover event has already rewritten the innerHTML on the outer div, and so the inner div has no parent...
What you really want to use is the mouseenter event (and presumably the mouseleave event on the inner div), which used to be proprietary to Internet Explorer but according to MDN Firefox 10 and Opera 11.10 support it too.

JavaScript pass mouseenter event to element that is underneath another element

I'm trying to build a little calendar app sort of like Google Calendar. I'm trying to create events with a click and drag functionality so that the events align to a grid. I'm trying to
tie the dragging to TD elements below the event DIV element, which works when moving downwards (lengthening the event), but it doesn't work moving upwards (shrinking the event).
What occurs is that the mouseenter event is fired for the event DIV element, but it is never fired for the underlying TD. If you try to resize the DIV by moving upwards on the side, it works because the TDs actually receive the mouseenter event.
Google Calendar and jQuery Week Calendar use the mousemove event, but the mousemove event is fired for every pixel, which seems a waste. Is there a way to write this without using the mousemove event?
Is it possible to put the DIV element behind the table and the TDs? If the table is somewhat transparent, the user would still be able to see the DIV, but would actually be firing events on the TDs. I tried to do this with z-index, but it didn't seem to actually work.
jsFiddle example code: http://jsfiddle.net/rockymeza/8SHpA/
It sounds like you're having a similar kind of issue that I had:
you want to fire event behind an element? The answer is CSS. Set pointer-events:none; to the parent.
I had made a test where I tried to (unsuccessfully) implement the same behavior on touch devices. You can check that at: http://www.hakoniemi.net/misc/pointer-events.html if it'd help you with your issue.

Categories

Resources