Is there a `pointer-events:hoverOnly` or similar in CSS?
Asked Answered
P

8

166

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;
}
Perimorph answered 4/3, 2014 at 9:52 Comment(4)
Just a note, pointer-events in not well supported in IE.Brande
sounds like you need javascriptHugely
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 #35873034Latterly
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.Bunko
H
15

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

Horselaugh answered 4/3, 2014 at 11:5 Comment(0)
C
168

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

Cleocleobulus answered 11/7, 2015 at 9:6 Comment(15)
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 placeIlianailine
doesn't this requires a click on the element to disable further pointer-events?Bloodandthunder
@Mindwin no, it doesn'tOba
It won't work as user will be able to click on the button.Mussel
@PriyanshuJain what you think will happen if user click on the button ?Oba
@СвободенРоб 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.Mussel
"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 <button> under the link (with negative z-index). Button is not clickable while hovering over link.Olindaolinde
Not working for iframe. Too bad, it would've been the perfect solution.Gisborne
@Gisborne you can't apply any styles outside iframe anyway.Oba
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).Gisborne
@Ilianailine 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.Mucin
@Mucin 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.Ilianailine
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. Quick example is here.Pily
@Ilianailine 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.Perimorph
This solution only simulates the behavior for the styles. However the click event is still sent.Punjabi
H
30

"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>
Hedberg answered 4/3, 2014 at 11:13 Comment(3)
sorry, im using the ugly jquery anyway. but have an upvote for a CSS only solution!Perimorph
this is great - just FYI unfortunately it doesn't work properly if the bottom layer contains an iframe : jsfiddle.net/ReZ9M/82Ilianailine
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...Zulmazulu
H
15

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

Horselaugh answered 4/3, 2014 at 11:5 Comment(0)
L
10

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;
}
Luby answered 13/9, 2016 at 10:38 Comment(1)
should be the selected answer: pure css + keeps pointer-events going through if neededEspagnole
B
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.

Bloodandthunder answered 2/12, 2016 at 16:30 Comment(3)
The element just gets stuck in the "hover" state when I tried this solution.Crowther
there is a typo, should be a . not a , for the transition valuePeafowl
This would cause an infinite loop since pointer-events: none; would cause hover state to go off, then it would immediately go on again as that property is droppedAfrikander
J
2

Just pure css, doesn't need jquery:

div:hover {pointer-events: none}
div {pointer-events: auto}
Jump answered 23/1, 2018 at 6:7 Comment(5)
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 :)Perimorph
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 :)Jump
caniuse.com/#feat=pointer-events - won't work in any IE except 11... if you create a working demo of what you mean here 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 demoPerimorph
@brillout can you add a snippet? Bcz it's work for meJump
This won't work as no pointer events will cause hover to go off, then auto will cause it to get selected - in an infinite loopAfrikander
M
2

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>
Makeyevka answered 4/2, 2018 at 17:6 Comment(0)
A
1

Based on mouse position

This function checks for mouse position, and element bounding box, and check if they overlap

function onHover(E,callback){
    window.addEventListener('mousemove',e=>{
        let R = E.getBoundingClientRect(); // get the element real dimentions 
        let x = e.pageX; // mouse x position 
        let y = e.pageY; // mouse y position 
        
        // check if mouse is within the the element box
        if(
            x > R.x && x < (R.x + R.width) 
         && y > R.y && y < (R.y + R.height)
        ){
            callback(true)
        }else{
            callback(false)
        }
    })
}

how to use

let E = document.querySelector('.layer'); // get the element that needs hover effect 
onHover(E,isHovering=>{
    if(isHovering){
        E.style.color='red';//red for example to indicate hover state 
    }else{
        E.style.color='black'; //black to indicate normal state 
    }
})
Anthe answered 30/9, 2023 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.