Mouseenter/Mouseover events do not fire if CSS3 translate/transform is used to change element position
Asked Answered
C

1

6

I am translating ( via jQuery / CSS3 ) a #wrapper div, by updating Y axis.

I've attached mouseenter / mouseleave events to child elements of #wrapper.

When #wrapper translates, its child comes under mouse one by one ( even if mouse does not move ). And this does not fire the mouseenter , mouseleave events.

However, events are fired when element has scrollbar and scrolled by mousewheel ( instead of translating ).

Is this a default behavior ? If yes, any workaround ?

Demo

Try scrolling with mousewheel, without moving mouse. I expect to change the background of .block to red color, but it's not happening.

Consolidate answered 6/2, 2014 at 17:31 Comment(4)
Most events are triggered only by user actions, not by actions of the browser itself. E.g. change is only triggered when the user modifies an input, not when a script assigns to element.value. So it makes sense that mouseenter events would only be triggered when the user moves the mouse into the element, not when the element moves under the mouse.Cornie
You never use scrolltop(), offset() or scroll(), I could not find it in code, but it is cumbersome to search through the code in JSFIDDLE, have to paste into editor. ;)Beebe
@Barmar, but it does fire in case of default scroll. See this demoConsolidate
well, @Jashwant, scrolling is a user action, so Barmar still has a point.Elisaelisabet
R
12

Example:

document.elementFromPoint(x, y);

Definition from Here:

Returns the element from the document whose elementFromPoint method is being called which is the topmost element which lies under the given point. To get an element, specify the point via coordinates, in CSS pixels, relative to the upper-left-most point in the window or frame containing the document.

Browser support from Here:

  • Internet Explorer 5.5+
  • Mozilla FireFox 3+
  • Safari Win 5+
  • Google Chrome 4+
  • Opera 10.53+

Solution 1 Working Example*:

var currentMousePos = { x: -1, y: -1 };
$(document).mousemove(function(event) {
    currentMousePos.x = event.pageX;
    currentMousePos.y = event.pageY;
});
$(containerElement).mousewheel(function(event) {
    $(elementClass).trigger('mouseleave');
    var element = document.elementFromPoint(currentMousePos.x, currentMousePos.y);
    $(element).trigger('mouseenter');
});

* there are some bugs, but you get the idea

Solution 2:

Use debounce from lodash or underscore libraries, to reduce load on client.

var currentMousePos = { x: -1, y: -1 };
$(document).mousemove(function (event) {
    currentMousePos.x = event.pageX;
    currentMousePos.y = event.pageY;
});
var debounced = _.debounce(function () {
    $(elementClass).trigger('mouseleave');
    var element = document.elementFromPoint(currentMousePos.x, currentMousePos.y);
    $(element).trigger('mouseenter');
}, 150);
$(containerElement).mousewheel(function (event) {
    debounced();
});
Rutaceous answered 6/2, 2014 at 17:38 Comment(7)
Wow, great thinking, but I don't think it's cross browser.Consolidate
Well it's IE9+ quirksmode.org/blog/archives/2010/06/more_ie9_goodne.htmlRutaceous
Yes, I checked the quirksmode too and hence replied :(Consolidate
@Consolidate so what is the browser support that you're looking for?Rutaceous
@Consolidate What if you capture the last position of you're mouse and then on each scroll iterate over all your elements and calculate their relative position on screen, and then if an element is in range highlight it? That is basically what elementFromPoint does in its essence, why don't you try to do it manually?Rutaceous
Yeah, that would be my last way. On each scroll event, check the 'y' index and then compare that with the offset of .blockConsolidate
I would use lodash or underscore debounce function with time delay 100ms - 200ms to not overwhelm the browser with calculations.Rutaceous

© 2022 - 2024 — McMap. All rights reserved.