409

I'm attempting to capture mouse events on an element with another absolutely-positioned element on top of it.

Right now, events on the absolutely-positioned element hit it and bubble up to its parent, but I want it to be "transparent" to these mouse events and forward them on to whatever is behind it. How should I implement this?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
s4y
  • 50,525
  • 12
  • 70
  • 98

6 Answers6

654
pointer-events: none;

Is a CSS property that makes events "pass through" the HTML-element to which the property is applied. It makes the event occur on the element "below".

See for details: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events

It is supported by almost all browsers, including IE11; global support was ~98.2% in 05/'21): http://caniuse.com/#feat=pointer-events (thanks to @s4y for providing the link in the comments).

JanD
  • 7,230
  • 3
  • 23
  • 24
  • 3
    Thanks! This is a perfect solution for modern (and non-IE) browsers. At the time this question was posted, I don’t think `pointer-events` existed! I may switch the accepted answer over to this one at some point. – s4y Jun 23 '11 at 14:53
  • 2
    I just switched the accepted answer to this one. Support for `pointer-events` [still isn’t super awesome](http://caniuse.com/#feat=pointer-events), but it’s getting better. For a more-compatible option, go with @JedSchmidt’s answer. – s4y Dec 18 '12 at 01:23
  • 2
    Is there an equivalent for Touch events? touch-events: none; or the like? – netpoetica Oct 15 '14 at 03:06
  • @netpoetica I believe touch events count as pointer events, but I may be wrong. Feel free to ask a separate question and link to this one. – Shelvacu Jan 29 '16 at 01:44
  • 2
    Except I need this event to happen for both the transparent element and also the elements below it. – RaisinBranCrunch Apr 14 '17 at 16:05
  • If one clicks an element - and starts a drag - and then passes the mouse over an element supposedly marked as `"pointer-events:none;"` sadly the supposedly inert element will still stop the event chain. – twobob Jun 23 '17 at 02:35
  • 15
    But what if you just want to let some event like the scroll event to pass through but for the top element to still respond to all other events. Setting pointer-events to none kills all the events on the top element. – Johann Jul 31 '17 at 08:10
66

Also nice to know...
Pointer-events can be disabled for a parent element (probably transparent div) and yet be enabled for child elements. This is helpful if you work with multiple overlapping div layers, where you want to be able click the child elements of any layer. For this all parenting divs get pointer-events: none and click-children get pointer-events reenabled by pointer-events: all

.parent {
    pointer-events:none;        
}
.child {
    pointer-events:all;
}

<div class="some-container">
   <ul class="layer-0 parent">
     <li class="click-me child"></li>
     <li class="click-me child"></li>
   </ul>

   <ul class="layer-1 parent">
     <li class="click-me-also child"></li>
     <li class="click-me-also child"></li>
   </ul>
</div>
Allisone
  • 8,434
  • 4
  • 32
  • 54
  • 11
    Awesome, right after the high of discovering `pointer-events:none` and the imminent let-down of having children nodes affected, you save the day :) – Juan Cortés Jul 20 '15 at 11:51
  • 2
    Can it be boiled down to just: `.parent * { pointer-events:all; }` for children? – Dmitry Mar 15 '18 at 22:58
  • 3
    It should be "pointer-events: auto;". The "all" value applies only to SVGs. See https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events – Design.Garden Dec 18 '19 at 21:31
  • 2
    @Dmitry Can confirm, that did work: `.parent { pointer-events:none; }` and `.parent * { pointer-events:auto; }` – David Feb 08 '22 at 00:47
52

If all you need is mousedown, you may be able to make do with the document.elementFromPoint method, by:

  1. removing the top layer on mousedown,
  2. passing the x and y coordinates from the event to the document.elementFromPoint method to get the element underneath, and then
  3. restoring the top layer.
Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
Jed Schmidt
  • 2,907
  • 23
  • 23
  • 9
    More wordy explanation here: http://www.vinylfox.com/forwarding-mouse-events-through-layers/ – Nathan Mar 23 '12 at 19:53
  • 2
    Awesome, I didn't know that method even existed! It can be also be used quite easily to get all elements under cursor using a _while_ loop to get the topmost element and then hide it in order to find the next one. Grab my version here: https://gist.github.com/Pichan/5498404 – jpeltoniemi May 01 '13 at 21:12
  • 2
    The problem with this workaround is that it doesn't work with zooming the page. For mobile devices or desktop browsers zoomed in X Y coordinates no longer mean anything, and there is no clear way to calculate zoom. i believe for pinch zooming it's actually impossible. – Impossibear Sep 16 '14 at 23:27
10

The reason you are not receiving the event is because the absolutely positioned element is not a child of the element you are wanting to "click" (blue div). The cleanest way I can think of is to put the absolute element as a child of the one you want clicked, but I'm assuming you can't do that or you wouldn't have posted this question here :)

Another option would be to register a click event handler for the absolute element and call the click handler for the blue div, causing them both to flash.

Due to the way events bubble up through the DOM I'm not sure there is a simpler answer for you, but I'm very curious if anyone else has any tricks I don't know about!

Loktar
  • 546
  • 3
  • 11
  • Thanks for the response, Loktar. Unfortunately, on the real page I'm not going to know what's underneath the absolutely-positioned element, and there may be more than one possible target. The only workaround I can think of is grabbing the mouse coordinates and comparing them against possible targets to see if they intersect. That would be just plain nasty. – s4y Jun 17 '09 at 22:32
4

There is a javascript version available which manually redirects events from one div to another.

I cleaned it up and made it into a jQuery plugin.

Here's the Github repository: https://github.com/BaronVonSmeaton/jquery.forwardevents

Unfortunately, the purpose I was using it for - overlaying a mask over Google Maps did not capture click and drag events, and the mouse cursor does not change which degrades the user experience enough that I just decided to hide the mask under IE and Opera - the two browsers which dont support pointer events.

Mikepote
  • 6,042
  • 3
  • 34
  • 38
1

If you know the elements that need mouse events, and if your overlay is transparent, you can just set the z-index of them to something higher than the overlay. All events should of course work in that case on all browsers.

ricosrealm
  • 1,616
  • 1
  • 16
  • 26