So I have a document that contains an iframe
element.
My document and the iframe
have different sources.
I want to make the contents of this iframe
look like parallax when loaded in the document.
For example, the document looks like this:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>blah</p>
<p>blah</p>
<p>blah</p>
<p>blah</p>
<iframe src="parallax.html" width="200" height="200">
<p>blah</p>
<p>blah</p>
<p>blah</p>
</body>
</html>
And the parallax.html contains just an image that fits to the window size.
I thought that parallax.html should have script that determines the position of the window relatively to the screen and adjusts the image position. I've tried something, for example:
document.body.scrollTop
, but it always returns 0getBoundingClientRect()
parameters, but top
also returns 0Is this even possible? Am I missing something? And yes, I have to make it with vanilla JS, no jQuery or other libraries.
As you note, the cross-origin restrictions makes this somewhat tricky. Your parent frame won't be able to access the internal variables or set listeners in its child frame, and vice versa. There's a very limited set of properties it can access, viewable here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Cross-origin_script_API_access
Furthermore, I believe document.body.scrollTop
and getBoundingClientRect()
inside an iframe are relative to the frame itself, as if the frame were a mini-window. You probably won't be able to access the information you want in the iframe, but should be able to get it from the parent frame.
The traditional way of passing information between cross-origin frames is to use iframe.contentWindow.postmessage
.
In this case, a simple implementation is to attach your scroll listener to your parent frame that sends a message to your child frame. It'd look something like this:
In the parent document:
var iframeElement = document.getElementById('my-parallax'); // give your iframe this id
window.addEventListener('scroll', () => {
var rect = iframeElement.getBoundingClientRect();
var dataToSend = {
scrollY: window.scrollY,
left: rect.left, right: rect.right,
bottom: rect.bottom, top: rect.top
};
var targetOrigin = '*'; // can change this if you know it
iframeElement.contentWindow.postMessage(dataToSend, targetOrigin);
});
Then in parallax.html:
window.addEventListener('message', function (e) {
var data = e.data;
if (e.source === window.parent) { // check if the sender was the parent
// use data.scrollY, data.top... etc. here
}
});
The caveat to this approach (and with postMessage in general) is that there is no guarantee that the UI of the iframe will update in sync of the parent window. The difference should be slight, unless there is significant computation on either frame. As far as I know, this is unavoidable when working with cross-domain iframes.