185

I'm trying to overlay a element on top of a webpage (to draw arbitrary graphics), and I've come to the point where I can stack it inside of a element on top of everything, but this prevents the user from clicking on any links/buttons/etc. Is there a way to have its content float on top of everything (it's semi-transparent, so you can still see what is behind) and have the user interact with the layer below it?

I've found a lot of information on the DOM event model, but none of it addresses the problem where the buttons and other "native" controls never seem to get the clicks in the first place.

Steven Schlansker
  • 37,580
  • 14
  • 81
  • 100
  • See also: http://stackoverflow.com/questions/3015422/forwarding-mouse-events-through-layers-divs/11935377 – kitchin Aug 13 '12 at 13:39
  • `$("div").click(function(e){e.preventDefault();});` I use this most of the times – Uday Hiwarale Sep 03 '14 at 10:42
  • This is something that should be (but hasn't been yet) addressed in the [w3c dom spec](https://dom.spec.whatwg.org/) (rather than with a hack) as it has many potential applications. – Mario Mar 12 '18 at 17:07

9 Answers9

137

A silly hack I did was to set the height of the element to zero but overflow:visible; combining this with pointer-events:none; seems to cover all the bases.

.overlay {
    height:0px;
    overflow:visible;
    pointer-events:none;
    background:none !important;
}
Flatliner DOA
  • 6,128
  • 4
  • 30
  • 39
73

Add pointer-events: none; to the overlay.


Original answer: My suggestion would be that you could capture the click event with the overlay, hide the overlay, then refire the click event, then display the overlay again. I'm not sure if you'd get a flicker effect though.

[Update] Exactly this problem and exactly my solution just appeared in this post: "Forwarding Mouse Events Through Layers". I know its probably a little late for the OP, but for the sake of somebody having this problem in the future, I though I would include it.

Kev
  • 15,899
  • 15
  • 79
  • 112
Russell Leggett
  • 8,795
  • 3
  • 31
  • 45
  • 4
    The link you provided is awesome. In particular, it allowed me to learn about Mozilla "pointer-events" property that does exactly the trick. http://hacks.mozilla.org/2009/12/pointer-events-for-html-in-firefox-3-6/ – Nicolas Dumazet May 17 '10 at 03:15
  • 14
    Basically you need to add `pointer-events: none;` CSS to the element above so it is "invisible" to the events. – lepe Aug 18 '15 at 08:30
  • 1
    @lepe: <3 All this complications, when all that was really needed was your awesome concise solution. – VSO Mar 17 '17 at 13:55
  • cant believe it was this easy! – pcarvalho Dec 30 '22 at 16:11
6

For the record an alternative approach might be to make the clickable layer the overlay: you make it semi-transparent and then place the "overlay" image behind it (somewhat counterintuitively, the "overlay" image could then be opaque). Depending on what you're trying to do, you might well be able to get the exact same visual effect (of an image and a clickable layer semi-transparently superimposed on top of each other), while avoiding clickability problems (because the "overlay" is in fact in the background).

Nick F
  • 9,781
  • 7
  • 75
  • 90
5

In case anyone else is running in to the same problem, the only solution I could find that satisfied me was to have the canvas cover everything and then to raise the Z-index of all clickable elements. You can't draw on them, but at least they are clickable...

Steven Schlansker
  • 37,580
  • 14
  • 81
  • 100
4

My team ran into this issue and resolved it very nicely.

  • add a class "passthrough" or something to each element you want clickable and which is under the overlay.
  • for each ".passthrough" element append a div and position it exactly on top of its parent. add class "element-overlay" to this new div.
  • The ".element-overlay" css should have a high z-index (above the page's overlay), and the elements should be transparent.

This should resolve your problem as the events on the ".element-overlay" should bubble up to ".passthrough". If you still have problems (we did not see any so far) you can play around with the binding.

This is an enhancement to @jvenema's solution.

The nice thing about this is that

  • you don't pass through ALL events to ALL elements. Just the ones you want. (resolved @jvenema's argument)
  • All events will work properly. (hover for example).

If you have any problems please let me know so I can elaborate.

guy mograbi
  • 27,391
  • 16
  • 83
  • 122
  • Interesting, but I guess I don't really understand. However, it brought to another idea: Make the real layer fully transparent and put it above the overlay. Make a non-transparent copy and put it below. All events are guaranteed to work, the display may be wrong if the copy gets out of sync. WDYT? – maaartinus Jul 02 '14 at 11:00
  • I can see how this could be used to pass the events from the transparent ".element-overlay" to the ".passthrough" element, which is below the actual overlay, but does the actual overlay also receive those events? I'm trying to find a way to capture the events on the overlay, and again on the lower layer, preferably without nesting the overlay inside the other layers or vice-versa. – Brett L Mar 11 '21 at 23:24
3

You can use an overlay with opacity set in order to the buttons/anchors in the back stay visible, but once you have that overlay over an element, you can't click it.

Mark Biek
  • 146,731
  • 54
  • 156
  • 201
yoda
  • 10,834
  • 19
  • 64
  • 92
  • I think that if the overlay is transparent (as in no background, a transparent image as background will not work), clicks will fall through. I'm not sure if this works for all browsers though. – mbillard Sep 12 '09 at 10:19
  • Correct, as this is how clickjacking occurs (transparent fullscreen iframe sits on top of a page). – Corey Ballou Nov 09 '09 at 18:15
2

Generally, this isn't a great idea. Taking your scenario, if you had evil intentions, you could hide everything underneath your "overlay". Then, when a user clicks on a link they think should take them to bankofamerica.com, instead it triggers the hidden link which takes them to myevilsite.com.

That said, event bubbling works, and if it's within an application, it's not a big deal. The following code is an example. Clicking the blue area pops up an alert, even though the alert is set on the red area. Note that the orange area does NOT work, because the event will propagate through the PARENT elements, so your overlay needs to be inside whatever element you're observing the clicks on. In your scenario, you may be out of luck.

<html>
<head>
</head>
<body>
    <div id="outer" style="position:absolute;height:50px;width:60px;z-index:1;background-color:red;top:5px;left:5px;" onclick="alert('outer')"> 
        <div id="nested" style="position:absolute;height:50px;width:60px;z-index:2;background-color:blue;top:15px;left:15px;">
        </div>
    </div>
    <div id="separate" style="position:absolute;height:50px;width:60px;z-index:3;background-color:orange;top:25px;left:25px;">
    </div>
</body>
</html>
Jerod Venema
  • 44,124
  • 5
  • 66
  • 109
  • 5
    "Taking your scenario, if you had evil intentions, you could hide everything underneath your "overlay". Then, when a user clicks on a link they think should take them to bankofamerica.com, instead it triggers the hidden link which takes them to myevilsite.com." -- That doesn't even make sense. Why would he need to go to the trouble. He could just use js to go there, why would he need to trigger a hidden link? – Russell Leggett Sep 09 '09 at 20:02
  • 1
    No evil intentions here! This is strictly in-house; it only really has to work on one of Safari or Firefox on exactly one computer :) I care nothing about portability at the moment. Your idea is interesting, but it requires nesting within the element - which does not work for things like form buttons or links, which is the use case I described in the original post :( – Steven Schlansker Sep 09 '09 at 21:13
  • 1
    @Steven, I know, which is why I mentioned that I think you're out of luck on that approach. @Russell, if you consider then that you can have a user perform actions without them knowing it, you can do things like start redirecting keyboard events, etc, which, in turn, would let you enter filenames for upload, and so on. This was a big deal with flash not too long ago, because without knowing it, you could turn on your flash webcam and/or audio controls and send audio/video without realizing it. – Jerod Venema Sep 10 '09 at 01:02
0

How about this for IE?:

onmousedown: Hide all elements which could overlay the event. Because display:none visibility:hidden not realy works, push the overlaying div out of the screen for a fixed number of pixels. After a delay push back the overlaying div with the same number of pixels.

onmouseup: Meanwhile this is the event you like to fire.

     //script
     var allclickthrough=[];         
     function hidedivover(){
              if(allclickthrough.length==0){
                allclickthrough=getElementsByClassName(document.body,"clickthrough");// if so .parentNode
                }
              for(var i=0;i<allclickthrough.length;i++){
                 allclickthrough[i].style.left=parseInt(allclickthrough[i].style.left)+2000+"px";
                 }
              setTimeout(function(){showdivover()},1000);   
              }
    function showdivover(){
             for(var i=0;i<allclickthrough.length;i++){
                allclickthrough[i].style.left=parseInt(allclickthrough[i].style.left)-2000+"px";
                }
             }       
    //html
    <span onmouseup="Dreck_he_got_me()">Click me if you can.</span>
    <div  onmousedown="hidedivover()" style="position:absolute" class="clickthrough">You'll don't get through!</div>
B.F.
  • 477
  • 6
  • 9
-2

I was having this issue when viewing my website on a phone. While I was trying to close the overlay, I was pretty much clicking on anything under the overlay. A solution that I found working for myself is to just add a tag around the entire overlay

Mike Szyndel
  • 10,461
  • 10
  • 47
  • 63
stefanplc
  • 419
  • 9
  • 22