152

Just been playing about with pointer-events property in CSS.

I have a div that I want to be invisible to all mouse events, except for :hover.

So all click commands go through the div to the one below it, but the div can report whether the mouse is above it or not still.

Can anyone tell me if this can be done?

HTML:

<div class="layer" style="z-index:20; pointer-events:none;">Top layer</div>
<div class="layer" style="z-index:10;">Bottom layer</div>

CSS:

.layer {
    position:absolute;
    top:0px;
    left:0px;
    height:400px;
    width:400px;
}
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Jimmery
  • 9,783
  • 25
  • 83
  • 157
  • 2
    Just a note, `pointer-events` in not well [supported in IE](http://caniuse.com/pointer-events). – Vucko Mar 04 '14 at 09:55
  • 2
    sounds like you need javascript – Pete Mar 04 '14 at 10:01
  • Agree with Pete, i know this is specifically asking for css, but had the same issue and the easiest solution for me was to just have the child initiate a click to the parent with javascript https://stackoverflow.com/questions/35872534/how-can-i-allow-a-click-to-pass-through-a-div-but-still-react-to-hover – Jerry Sha Mar 27 '18 at 23:37
  • Not that this is a solution, but in case it helps: if a parent element tracks mouse position and hover events, its children can still accept touches. I wanted to track touches on one element and hover from another, and putting the touch handler div inside of the hover div helped. – Fernando Rojo Feb 01 '23 at 22:01

7 Answers7

155

Hover only. It is very easy. No JS... Prevent link default action too.

a:hover {
 color: red;
}
a:active {
 pointer-events: none;
}
<a href="www.google.com">Link here</a>

Edit: supported in IE 11 and above http://caniuse.com/#search=pointer-events

Свободен Роб
  • 2,579
  • 2
  • 20
  • 26
  • 37
    this does 'work' - however it doesn't allow click through to an element below (at least not when that element is a YouTube video) - which is probably the only reason anybody would need that behavior in the first place – Simon_Weaver Jun 27 '16 at 20:39
  • 1
    doesn't this requires a click on the element to disable further pointer-events? – Mindwin Remember Monica Dec 02 '16 at 16:32
  • @Mindwin no, it doesn't – Свободен Роб Apr 02 '17 at 13:16
  • It won't work as user will be able to click on the button. – Priyanshu Jain Jun 13 '18 at 11:48
  • 2
    @PriyanshuJain what you think will happen if user click on the button ? – Свободен Роб Jun 13 '18 at 11:54
  • @СвободенРоб My concern was user will be able to click the button it won't take use to any other link but it misleads user due to cursor but we can change cursor sorry i got confused thanks for clarification. – Priyanshu Jain Jun 14 '18 at 04:36
  • "So all click commands go through the div to the one below it" This solution does not solve this bit (I'm not sure what it does solve). You can easily demonstrate this by absolutely positioning a – EoghanM Apr 03 '20 at 12:41
  • Not working for iframe. Too bad, it would've been the perfect solution. – EPurpl3 Apr 06 '20 at 17:38
  • @EPurpl3 you can't apply any styles outside iframe anyway. – Свободен Роб Apr 06 '20 at 18:49
  • Well, actually the iframe is inside my page. And it is easier to push CSS from the inside of the iframe into the page than in reverse (with JS). – EPurpl3 Apr 08 '20 at 19:28
  • 2
    @Simon_Weaver That's very shortsighted to say that the only reason someone would need this functionality is to click on a youtube video--or even more generically speaking, to click on something below the element. It's very probable that someone wants this behavior so that an element that is normally clickable _isn't_ clickable. – claudekennilol Mar 08 '21 at 17:22
  • 2
    @claudekennilol the question literally says "So all click commands go through the div to the one below it". That's exactly what they **did** want! I only mentioned YouTube as the example I had tested this with. Sounds like the original questioner wanted pointer-events to act like a filter for events which I don't think it can do. Anyway this was 5 years ago - I hope you found a solution to whatever brought you to this question today. – Simon_Weaver Mar 09 '21 at 03:12
  • I think that a complementary task (which is maybe what other comments referred to) is to allow child elements to be clickable, which is [answered here](https://stackoverflow.com/a/26799885/3002584). Quick example is [here](https://jsfiddle.net/onv8p36y/). – OfirD Nov 21 '21 at 23:06
  • 1
    @Simon_Weaver you are absolutely right, this solution isn't what I wanted. I feel like it's shortsighted of others not to read my question before critizing you. – Jimmery Feb 28 '22 at 07:18
28

"Stealing" Xanco's answer but without that ugly, ugly jQuery.

Snippet: Notice DIVs are in reverse order

.layer {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 400px;
  width: 400px;
}

#bottomlayer {
  z-index: 10
}

#toplayer {
  z-index: 20;
  pointer-events: none;
  background-color: white;
  display: none
}

#bottomlayer:hover~#toplayer {
  display: block
}
<div id="bottomlayer" class="layer">Bottom layer</div>
<div id="toplayer" class="layer">Top layer</div>
Andrzej Ziółek
  • 2,241
  • 1
  • 13
  • 21
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 3
    sorry, im using the ugly jquery anyway. but have an upvote for a CSS only solution! – Jimmery Mar 04 '14 at 15:15
  • this is great - just FYI unfortunately it doesn't work properly if the bottom layer contains an iframe : jsfiddle.net/ReZ9M/82 – Simon_Weaver Jun 27 '16 at 21:40
  • 5
    How does this resolve the question? The OP asked for click commands to go through the initially visible element. In your solution they do not, notice how the "Top Layer" text cannot be highlighted... – Trever Thompson Sep 24 '19 at 08:06
14

I don't think it's possible to achieve your aims in CSS alone. However, as other contributors have mentioned, it's easy enough to do in JQuery. Here's how I've done it:

HTML

<div
  id="toplayer"
  class="layer"
  style="
    z-index: 20;
    pointer-events: none;
    background-color: white;
    display: none;
  "
>
  Top layer
</div>
<div id="bottomlayer" class="layer" style="z-index: 10">Bottom layer</div>

CSS (unchanged)

.layer {
    position:absolute;
    top:0px;
    left:0px;
    height:400px;
    width:400px;
}

JQuery

$(document).ready(function(){
    $("#bottomlayer").hover(
        function() {
            $("#toplayer").css("display", "block");
        },
        function() {
            $("#toplayer").css("display", "none");
        }
    );
});

Here's the JSFiddle: http://www.jsfiddle.net/ReZ9M

diedu
  • 19,277
  • 4
  • 32
  • 49
Xanco
  • 874
  • 1
  • 10
  • 15
9

You can also detect hover on different element and apply styles to it's child, or using other css selectors like adjacent children, etc.

It depends on your case though.

On parent element hover. I did this:

.child {
    pointer-events: none;
    background-color: white;
}

.parent:hover > .child {
    background-color: black;
}
maciejmatu
  • 572
  • 1
  • 8
  • 14
  • 1
    should be the selected answer: pure css + keeps pointer-events going through if needed – sab Feb 22 '22 at 21:32
2

Pure CSS solution to your request (the opacity property is there just to illustrate the need for the transitions):

.hoverOnly:hover {
    pointer-events: none;
    opacity: 0.1;
    transition-delay: 0s;
}
.hoverOnly {
    transition: ,5s all;
    opacity: 0.75;
    transition-delay: 2s;
}

What it does:

When the mouse enters the box, it triggers the :hover state. However, in that state, the pointer-events are disabled.

But if you do not set the transitions timers, the div will cancel the hover state when the mouse moves; the hover state will flicker while the mouse is moving inside the element. You can perceive this by using the code above with the opacity properties.

Setting a delay to the transition out of the hover state fixes it. The 2s value can be tweaked to suit your needs.

Credits to transitions tweak: patad on this answer.

Community
  • 1
  • 1
Mindwin Remember Monica
  • 1,469
  • 2
  • 20
  • 35
2

Just pure css, doesn't need jquery:

div:hover {pointer-events: none}
div {pointer-events: auto}
Hiren Gohel
  • 4,942
  • 6
  • 29
  • 48
Bariq Dharmawan
  • 755
  • 4
  • 16
  • 26
  • Hi - many thanks for this - can you add a demo to your answer (like a code snippet or a link to this in codepen or jsfiddle?) - and do you know what kind of compatibility this has? is it a new CSS3 feature? If this works I will definitely give you a tick :) – Jimmery Jan 23 '18 at 11:29
  • sorry I can't create a demo. But in my logic, it definitely working all browser (I don't know about ie, because I don't have ie). And, it support from css1 :) – Bariq Dharmawan Jan 23 '18 at 14:27
  • https://caniuse.com/#feat=pointer-events - won't work in any IE except 11... if you create a working demo of what you mean here https://codepen.io/ I will test it for you and if it works, I will award you with the correct answer - sorry, I cant award this to theoretical code - only a working demo – Jimmery Jan 23 '18 at 16:53
  • @brillout can you add a snippet? Bcz it's work for me – Bariq Dharmawan May 15 '20 at 23:57
1

I use the :hover pseudo-element of an equal-sized parent/container to simulate a hover over my overlay div, then set the overlay's pointer-events to none to pass through clicks to elements below.

let button = document.getElementById('woohoo-button');
button.onclick = () => console.log('woohoo!');

let overlay = document.getElementById('overlay');
overlay.onclick = () => console.log(`Better change my pointer-events property back to 'none'`);
#container {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background-color: green;
  width: 300px;
  height: 300px;
}

#overlay {
  background-color: black;
  width: 100%;
  height: 100%;
  opacity: 0;
  z-index: 1;
  /* Pass through clicks */
  pointer-events: none;
}


/* 
   Set overlay hover style based on
   :hover pseudo-element of its  
   container
*/
#container:hover #overlay {
  opacity: 0.5;
}

#woohoo-button {
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: red;
}
<div id="container">
  <div id="overlay"></div>
  <button id="woohoo-button">
    Click Me
  </button>
</div>