Load JS view iframe only once in viewport (#1607)

This commit is contained in:
Jonatan Kłosko 2022-12-25 14:31:40 +01:00 committed by GitHub
parent b4696710e7
commit 3eb21f8757
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 5 deletions

View file

@ -3,7 +3,12 @@ import {
getAttributeOrThrow,
parseInteger,
} from "../lib/attribute";
import { isElementHidden, randomId, randomToken } from "../lib/utils";
import {
isElementHidden,
isElementVisibleInViewport,
randomId,
randomToken,
} from "../lib/utils";
import { globalPubSub } from "../lib/pub_sub";
import {
getChannel,
@ -72,7 +77,7 @@ const JSView = {
this.channel = getChannel(this.props.sessionId, this.props.clientId);
this.removeIframe = this.createIframe();
this.iframeActions = this.createIframe();
// Setup child communication
this.childReadyPromise = new Promise((resolve, reject) => {
@ -89,7 +94,9 @@ const JSView = {
this.hiddenInput.style.display = "none";
this.el.appendChild(this.hiddenInput);
this.loadIframe();
this.iframeActions.visibilityPromise.then(() => {
this.loadIframe();
});
// Channel events
@ -154,7 +161,7 @@ const JSView = {
destroyed() {
window.removeEventListener("message", this._handleWindowMessage);
this.removeIframe();
this.iframeActions.remove();
this.unsubscribeFromChannelEvents();
this.channel.push("disconnect", { ref: this.props.ref });
@ -248,11 +255,36 @@ const JSView = {
);
});
return () => {
// We detect when the placeholder enters viewport and becomes visible,
// based on that we can load the iframe contents lazily
let viewportIntersectionObserver = null;
const visibilityPromise = new Promise((resolve, reject) => {
if (isElementVisibleInViewport(this.iframePlaceholder)) {
resolve();
} else {
viewportIntersectionObserver = new IntersectionObserver((entries) => {
if (isElementVisibleInViewport(this.iframePlaceholder)) {
viewportIntersectionObserver.disconnect();
resolve();
}
});
viewportIntersectionObserver.observe(this.iframePlaceholder);
}
});
// Cleanup
const remove = () => {
resizeObserver.disconnect();
intersectionObserver.disconnect();
viewportIntersectionObserver && viewportIntersectionObserver.disconnect();
this.iframe.remove();
this.iframePlaceholder.remove();
};
return { visibilityPromise, remove };
},
repositionIframe() {

View file

@ -21,6 +21,10 @@ export function isElementHidden(element) {
return element.offsetParent === null;
}
export function isElementVisibleInViewport(element) {
return !isElementHidden(element) && isElementInViewport(element);
}
export function clamp(n, x, y) {
return Math.min(Math.max(n, x), y);
}