How can you make an iframe have pointer-events: none; but not on a div inside that?
Asked Answered
P

7

33

I have an iframe with an inherited pointer-events: none; but when I have inserted a <div> inside the iframe with a pointer-events: auto; the div won't react to clicks or any mouse hover events.

The reason for this is that the iframe is inside a <div style="position:fixed;"> so it is kind of like a little menu. But I would like the user to click through the iframe, but not through the links and divs in the iframe.

And yes, it absolutely needs to remain an iframe.

How could I bypass this? Can I even bypass this?

Here's a simple example: jsfiddle.net/MarcGuiselin/yLo119sw/2

Edit 2023:

Geez, time sure does fly by haha. What I wanted to do originally can now be achieved via the shadow dom. (Supported in 97% of browsers)

This allows chrome extensions or scripts to inject their css styles into a page, without accidentally changing the style of the website. For example, a chat support script like Zendenk, which injects it's own menu inside a website, must take care to not break the website itself:

Zendesk is a chat app that adds a little menu to talk to support staff in the bottom corner of websites

PS: Zendesk technically uses an iframe since it's just a rectangular menu, but you get my point.

Phraseologist answered 27/4, 2014 at 22:46 Comment(4)
Could you create a demo on jsfiddle.net that shows the problem?Incomparable
here you go: jsfiddle.net/MarcGuiselin/yLo119sw/1 forgive my feeble attempt at humorPhraseologist
Hi @marc-guiselin, I have the exact same problem. I'm struggling to find a way, but no luck. I would appreciate it if you could tell me your workaround if you have found any... Thanks.Disruption
@Disruption unfortunately I found nonePhraseologist
D
4

What we want is not allowed by the spec, at least not with an absolutely positioned iframe.

We're all wanting to do the same thing:

  • Render a container (typically for a Chrome extension), positioned over the page and filling the viewport
  • Disallow not allow the container itself from capturing pointer events
  • Allow the container's children to capture pointer events

This lets us "click through" the absolutely positioned box, but still interact with its children, which may be buttons or just boxes with hover events or whatever.

We can achieve this behavior if and only if the boxes share the same document. Disabling pointer events for an absolutely positioned iframe disables them for all of its children.

An alternative approach is rendering the content directly into the document, i.e. into a Shadow DOM for styles sandboxing. This is the only way to achieve this behavior as we are looking for it, and was how I previously approached the problem before trying to refactor to an iframe and finding it can't be replicated this way.

See https://iframe-pointer-events.vercel.app for a demo.

Dancy answered 11/6, 2022 at 15:23 Comment(0)
R
4

I've searched a lot and figured out a workaround myself.

// the iframe element
const frame = document.getElementsByTagName("iframe")[0];
frame.onload = function () {
  const frameDocument = frame.contentDocument;
  document.addEventListener("mousemove", onMouseMove, true);
  frameDocument.addEventListener("mousemove", onMouseMove, true);

  function onMouseMove(e) {
    let coord;
    if (e.currentTarget === document) {
      coord = {
        x: e.pageX - frame.offsetLeft,
        y: e.pageY - frame.offsetTop,
      };
    } else {
      coord = { x: e.clientX, y: e.clientY };
    }

    const el = frameDocument.elementFromPoint(coord.x, coord.y);
    // you can compare to your own element if needed
    frame.style.pointerEvents =
      !el || el === frameDocument.body ? "none" : "auto";
  }
};

The iframe will auto toggle its pointer-events and all events just works seamlessly.

React answered 2/9, 2020 at 7:11 Comment(0)
D
4

What we want is not allowed by the spec, at least not with an absolutely positioned iframe.

We're all wanting to do the same thing:

  • Render a container (typically for a Chrome extension), positioned over the page and filling the viewport
  • Disallow not allow the container itself from capturing pointer events
  • Allow the container's children to capture pointer events

This lets us "click through" the absolutely positioned box, but still interact with its children, which may be buttons or just boxes with hover events or whatever.

We can achieve this behavior if and only if the boxes share the same document. Disabling pointer events for an absolutely positioned iframe disables them for all of its children.

An alternative approach is rendering the content directly into the document, i.e. into a Shadow DOM for styles sandboxing. This is the only way to achieve this behavior as we are looking for it, and was how I previously approached the problem before trying to refactor to an iframe and finding it can't be replicated this way.

See https://iframe-pointer-events.vercel.app for a demo.

Dancy answered 11/6, 2022 at 15:23 Comment(0)
J
2

You're working with position:absolute, according to the example you've uploaded on jsfiddle... Adding a z-index to the "you can't click me because i'm behind the iframe" button allows me to click it.

Z-Index

EDIT: If you want to do a pointer-events: none; everywhere except in one div, you can add the pointer-events in each element instead of the iframe. According to your example in the fiddle, if you want to save the Nicholas Cage but block the other events, you can do something like this:

switchbutton.onclick = function () {
  this.innerHTML = "reload to reset";
  //iframe.style.pointerEvents="none";
  cantclick.innerHTML = "You can click me now! :)";
  iframe.contentDocument.body.innerHTML =
    "<button>You can't click me now because my parent iframe is click-through! :(</button><br>or save this gorgeous image of your favorite actor which may or may not be relavant to the problem.<br><img id='nicholasCage' src='http://media2.popsugar-assets.com/files/2014/01/06/008/n/1922283/131b4126c7b8738f_thumb_temp_image333458751389045360.jpg.xxxlarge/i/Best-Nicolas-Cage-Memes.jpg'/>";

  var iframeBody = iframe.contentDocument,
    elements = iframeBody.querySelectorAll("*"),
    i,
    len = elements.length;

  for (i = 0; i < len; i++) {
    elements[i].style.pointerEvents = "none";
  }

  iframeBody.getElementById("nicholasCage").style.pointerEvents = "all";
};

If you can use jQuery you can do it faster just using $("iframe *").css("pointer-events","none");

Joceline answered 15/7, 2016 at 11:22 Comment(6)
obviously. the goal isn't to click the button over the iframe, but instead to figure out how to allow mousevents to penetrate through the iframe when the mousevent isn't on something inside the iframePhraseologist
Sorry, I think I'm not getting it (my fault 'cos my English sucks a bit...). Ok now I've checked deeply your jsfiddle, when NCage appears, you want to click on the button "You can't click me..."? You can do it using javascript, is that are you asking for? (Apologies again)Joceline
OK so you want to disable all the things inside the iframe except (example given) the image of NCage. Are you able to use jQuery or just javascript?Joceline
no this is a css/html thing. for example if i wanted a chrome extension to insert a menu into each page that is inside an iframe(so it doesn't mess up the website it is on) and that menu covers the entire page. if i were to click in the middle of the iframe (where none of the menu is) i would still be clicking the iframe instead of the stuff behind it. you can't do this efficiently with javascript. It would be monstrously complex and wouldn't work on most sites. but if some way you could make the iframe click-through, you could fix all those problems. that is what i am looking for.Phraseologist
Aaah OK sorry, I didn't understand! Well, in this case I don't know if you can do it just with CSS... I got it using JS. In this example you can click both of them, but it's a bit tricky... jsfiddle.net/Kamae/e51zykkh it's just making the iframe "z-index -1", getting the mouse position, triggering a click, and making the iframe "z-index 0" again... It's all I can do, sorry if this doesn't help you at all. Good luck!Joceline
There are other methods how to not mess with the page content. You can eg superprefix your classes. But i think that this is case for using something like html5 shadow DOM (shadow-root). I haven't worked with it myself though.Seroka
H
0

There's no way you can do it, since it would be another security issue along with clickjacking.

Styles inside iframe don't cooperate in any way with styles inside of host site. So if you even set z-index/pointer-events or something else on iframe and would try to override the rule inside of it, it won't apply to host site rules in any way.

So what's the solution? You have to split your iframe into multiple ones, and tinker with theirs position.

Hanrahan answered 19/4, 2019 at 8:9 Comment(0)
D
0

if u can revert the thing, put the click element as the iframe wrapper:

<button><iframe></iframe></button>

button {
  display : block; /* or inline-block */
}
button iframe {
  pointer-events : none;
  position: relative;
  z-index : 0;
}
Duad answered 15/11, 2023 at 10:33 Comment(0)
C
-3

Have you tried this?

pointer-events: inherit;

I know you already got your answer, but I thought this might work, too.

Cachou answered 4/8, 2016 at 17:25 Comment(0)
P
-3

Don't give the whole iframe pointer-events: none. Just inside the iframe put a CSS rule body * { pointer-events: none;}

This way the iframe does not block events, however elements inside the iframe are not clickable.

Prosy answered 23/9, 2016 at 8:43 Comment(1)
The goal is to figure out how to allow clicks to penetrate through the iframe when the click isn't on something inside the iframe. You want to be able to click on stuff inside the iframe, otherwise that ruins the purpose.Phraseologist

© 2022 - 2024 — McMap. All rights reserved.