Skip to content

Commit

Permalink
[amp-iframe] iframe viewability (#36131)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmanek committed Oct 1, 2021
1 parent db1659d commit f23e77d
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 5 deletions.
29 changes: 29 additions & 0 deletions examples/bento/amp-iframe-send-intersections-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<h3>Intersection Ratios:</h3>
<p id="intersections"></p>
<script>
window.parent.postMessage(
{
sentinel: 'amp',
type: 'send-intersections',
},
'*'
);
window.addEventListener('message', (event) => {
if (event.data.sentinel !== 'amp' && event.data.type !== 'intersection') {
return;
}
const node = document.getElementById('intersections');
event.data.changes.forEach((change) => {
node.innerHTML += '<p>' + change.intersectionRatio + '</p>';
});
});
</script>
</body>
</html>
65 changes: 61 additions & 4 deletions extensions/amp-iframe/1.0/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import {toWin} from '#core/window';
import {ContainWrapper, useIntersectionObserver} from '#preact/component';
import {setStyle} from '#core/dom/style';
import {useMergeRefs} from '#preact/utils';
import {
DEFAULT_THRESHOLD,
cloneEntryForCrossOrigin,
} from '../../../src/utils/intersection-observer-3p-host';
import {postMessage} from '../../../src/iframe-helper';
import {dict} from '#core/types/object';

const NOOP = () => {};

Expand All @@ -28,6 +34,57 @@ export function BentoIframe({
const dataRef = useRef(null);
const isIntersectingRef = useRef(null);
const containerRef = useRef(null);
const observerRef = useRef(null);
const targetOriginRef = useRef(null);

const viewabilityCb = (entries) => {
const iframe = iframeRef.current;
const targetOrigin = targetOriginRef.current;
if (!iframe || !targetOrigin) {
return;
}
postMessage(
iframe,
MessageType.INTERSECTION,
dict({'changes': entries.map(cloneEntryForCrossOrigin)}),
targetOrigin
);
};

const handleSendIntersectionsPostMessage = useCallback((event) => {
const iframe = iframeRef.current;
if (!iframe) {
return;
}
if (
event.source !== iframe.contentWindow ||
event.data?.type !== MessageType.SEND_INTERSECTIONS
) {
return;
}
targetOriginRef.current = event.origin;
const win = toWin(iframe.ownerDocument.defaultView);
observerRef.current = new win.IntersectionObserver(viewabilityCb, {
threshold: DEFAULT_THRESHOLD,
});
observerRef.current.observe(iframe);
}, []);

useEffect(() => {
const iframe = iframeRef.current;
if (!iframe) {
return;
}
const win = toWin(iframe.ownerDocument.defaultView);
win.addEventListener('message', handleSendIntersectionsPostMessage);
let observer = observerRef.current;

return () => {
observer?.unobserve(iframe);
observer = null;
win.removeEventListener('message', handleSendIntersectionsPostMessage);
};
}, [handleSendIntersectionsPostMessage]);

const updateContainerSize = (height, width) => {
const container = containerRef.current;
Expand Down Expand Up @@ -70,7 +127,7 @@ export function BentoIframe({
}
}, [requestResize]);

const handlePostMessage = useCallback(
const handleEmbedSizePostMessage = useCallback(
(event) => {
if (event.data?.type !== MessageType.EMBED_SIZE) {
return;
Expand Down Expand Up @@ -98,12 +155,12 @@ export function BentoIframe({
return;
}

win.addEventListener('message', handlePostMessage);
win.addEventListener('message', handleEmbedSizePostMessage);

return () => {
win.removeEventListener('message', handlePostMessage);
win.removeEventListener('message', handleEmbedSizePostMessage);
};
}, [handlePostMessage]);
}, [handleEmbedSizePostMessage]);

const ioCallback = useCallback(
({isIntersecting}) => {
Expand Down
24 changes: 24 additions & 0 deletions extensions/amp-iframe/1.0/storybook/Basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,27 @@ export const WithResizableIframe = () => {
};

WithResizableIframe.storyName = 'Resizable iframe outside viewport';

export const WithSendIntersectionsPostMessage = () => {
return (
<div>
<div
style={{
width: '100%',
height: '110vh', // so that iframe is outside viewport
backgroundColor: 'blue',
}}
></div>
<Iframe
style={{width: 500, height: 500}}
iframeStyle={{border: '1px solid black'}}
sandbox="allow-scripts allow-same-origin"
src="/examples/bento/amp-iframe-send-intersections-example.html"
>
<div placeholder>Placeholder</div>
</Iframe>
</div>
);
};

WithSendIntersectionsPostMessage.storyName = 'Send intersections';
2 changes: 1 addition & 1 deletion src/utils/intersection-observer-3p-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function calculateChangeEntry(element, hostViewport, intersection, ratio) {
* @param {!IntersectionObserverEntry} entry
* @return {!IntersectionObserverEntry}
*/
function cloneEntryForCrossOrigin(entry) {
export function cloneEntryForCrossOrigin(entry) {
return /** @type {!IntersectionObserverEntry} */ ({
'time': entry.time,
'rootBounds': entry.rootBounds,
Expand Down

0 comments on commit f23e77d

Please sign in to comment.