Make iframe click-through, but not the iframe's body
Asked Answered
F

2

14

How can I make an iframe click-through, but make that iframe's body still be clickable?

I tried:

iframe.style.width = '100%'
iframe.style.height = '100%'
iframe.style.display = 'block'
iframe.style.position = 'fixed'
iframe.style.backgroundColor = 'transparent'
iframe.style.pointerEvents = 'none'
iframe.style.border = '0'
iframe.frameborder = '0'
iframe.scrolling = 'no'
iframe.allowTransparency = 'true'

and inside of my I frame I'm using the following css:

html, body {
    /* background:none transparent; */
    pointer-events:auto;
}

This results in body being visible (which is what I want), but it is click-through like the rest of the iframe. I want the body of the iframe to be clickable, but all the rest of the actual iframe element should be click-through.

The iframe is always bigger than the body inside of it.

Unfortunately I cannot access the the iframe content from the main site (so accessing the scrollHeight etc isn't possible), I can only change its actual source code.

Foregoing answered 20/1, 2017 at 14:33 Comment(10)
If I got it right, you want the iframe element to be clickable, but nothing inside it?Rossetti
what click-through means? click with no event?Curacy
@Rossetti What I assume he was asking, was is it possible to have the HTML inside of the iFrame catch a mouse event, however if no HTML elements exist where the cursor fires in the iFrame, the mouse event will pass through the iFrame to the HTML on the original DOM.Deathbed
@Curacy 'click through' refers to not catching a mouse event, as if the element does not exist.Deathbed
@IanWise possibly, just want to make sure that's the correct interpretation.Curacy
@IanWise any updates?Rossetti
Ian Wise is correct with both, his assumption what I was asking and with what 'click through' means in this context.Foregoing
Maybe I should clarify what I'm needing a bit more. What I plan on doing is stacking several iFrames on top of eachother, each one with an absolute positioning of 0,0, and a width/height of 100% (essentially the entire page). I never know the contents of these iFrames AHEAD of time, however I DO have access to the code. (I'm making an electron API that loads several "widgets" into one page). Since all these iFrames are stacked on top of each other, the top frame blocks all events from bubbling down to elements in iFrames behind it. I need a way to pass these events down to the next iFrame.Deathbed
@IanWise I updated my answer accordingly.Rossetti
@IanWise added code for a more professional answer. Please lmk if that answered your needs!Rossetti
R
7

DISCLAIMER: OP created this question almost two years ago, my answer follows Ian Wise's bumping the question and elaborating on it (see comments).


What you are describing here involves logic between a document and a child document: "If a click event did nothing inside child document, apply that click event to parent document", and therefore cannot be approached using HTML/CSS.

Iframes are different documents. They do have a child-parent relationship with their containers, but an event that occurs within the iframe will be handled by the iframe.

An idea that requires some code but will work:

  • Place a transparent div above all the stacked iframes, and catch the click event pos.
  • Parent logic ->
    • Iterate through array of existing iframe elements.
    • Send click pos until one of the iframes returns a positive response.

function clickOnCover(e, i) {
	if(e && e.preventDefaule) e.preventDefault();
	if(i && i >= iframes.length) {
		console.log("No action.");
		return;
	}
	var iframe = iframes[i || 0];
	if(iframe.contentWindow && iframe.contentWindow.postMessage) {
		iframe.contentWindow.postMessage({ x: e.clientX, y: e.clientY, i: i });
	}
}
function iframeResponse(e) {
	var response = e.data, iframeIndex = response.i;
	if(response.success)
		console.log("Action done on iframe index -> " + iframeIndex);
	else 
		clickOnCover({ clientX: response.x, clientY: response.y }, iframeIndex+1);
}
  • iFrames logic ->
    • Have a function that accepts the clientX, clientY and checks for possible activies in that position (might be tricky!).
    • Will respond positively if an action occurred, and the opposite.

window.addEventListener("message", function(e) {
	// Logic for checking e.x & e.y
	
	e.success = actionExists; // Some indicator if an action occurred.
	
	if(window.parent && window.parent.postMessage) {
		window.parent.postMessage(e);
	}
});

This solution keeps managing the event within the parent document and only requires iterating through whatever amount of stacked iframes.


Found a relevant SO question to further support my claim: Detect Click in Iframe

Rossetti answered 31/10, 2018 at 9:14 Comment(3)
The real answer here is: "you can't". Not if you want the actual, original, isTrusted pointer event. You can get a mocked event, but it cannot do everything the original one can, because it bears a completely different class of restrictions at both browser and device level. It won't be a user-initiated event, it will be a generated one. I'd say your answer was complete if it also pointed this out.Jarred
@AndreiGheorghiu I stopped reading after "you can't" :) A person put a lot of time searching for information online so I'd say it's safe to assume this solution is important to him. Whether isTrusted, isSafe, isPossible, doesn't matter. If security is an issue I'm sure he'll take the necessary precautions. Anyway, I'm leaving the decision making to him, while doing my best to provide a way in which the final goal IS possible. Thank you for your feedback though.Rossetti
@AndreiGheorghiu Reading your message fully, please notice that I did not ask to "mock" the click event. I did suggest some logic of finding an element by location is preferable.Rossetti
B
6

This is not possible with CSS.
The easiest way is to resize the iframe properly. Assuming you have access to iframe content, the following solution is possible:

You might add a little JS to allow the parent page to know the iframe height

mainpage.js

var iframe = getIframe();
setIntervalMaybe(() => {
    // ask for size update
    iframe.contentWindow.postMessage({action: "getSize"}, document.location.origin);
}, 100)
window.addEventListener("message", function receiveMessage(event) {
  switch(event.data.action) {
    case "returnSize":
      // updateIFrameSize(event.data.dimensions);
      console.log(event.data.dimensions);
      break;
  }
}, false);

iframe.js

window.addEventListener("message", function receiveMessage(event) {
  switch(event.data.action) {
    case "getSize":
      event.source.postMessage({action: "returnSize", dimensions: {
        width: document.body.offsetWidth,
        height: document.body.offsetHeight
      }}, event.origin);
      break;
  }
}, false);
Badalona answered 1/11, 2018 at 9:51 Comment(8)
@fingeron, if you're implying Andrii copied your answer, I'd like to point out your answer is not proper, as it lacks a technical implementation (the code). If your answer only provides general advice without code, chances are anyone writing code following your advice will provide a more useful, hence more helpful answer than yours. You can't claim copyright on advice and if anyone is able to formulate a better answer upon your advice they're encouraged to do so.Jarred
To be technically exact, you can't even claim copyright on code. There are quite a few legal precedents to back my statement up, but that's a completely different story.Jarred
@fingeron, it is another answer, you are talking about passing click events, I'm talking about just usual resizing, this approach is simpler, bug free, while doing the sameBadalona
@fingeron, by the way, I thumbed up your answer :-) It was first thumb up on itBadalona
Simply resizing the iFrame will not suffice, as I do not know the HTML / CSS of this page before I load it, and the iFrame takes up the whole page. Assume that I can inject javascript into the source though. (I'm making an API that the code will be loaded with. The best way to describe it would be a screen that loads several "widgets" into one page, however I need the code to be isolated from each other, which is why I'm using an iFrame).Deathbed
@IanWise the possible option is: you may track an body height changes and send corresponding events to container page. Also you may open iframe out of screen(css transform: translateX(-10000px)) and wait until height tracking script is loadedBadalona
@AndriiMuzalevskyi Sorry for making ambitious assumptions. My comment was removed.Rossetti
@AndreiGheorghiu Added some code following your helpful advice.Rossetti

© 2022 - 2024 — McMap. All rights reserved.