Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 729
Forivin Avatar asked Jan 20 '17 14:01

Forivin


2 Answers

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

like image 115
fingeron Avatar answered Oct 10 '22 11:10

fingeron


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);
like image 20
Andrii Muzalevskyi Avatar answered Oct 10 '22 11:10

Andrii Muzalevskyi