mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-24 12:26:07 +08:00
Load JS view iframe only once in viewport (#1607)
This commit is contained in:
parent
b4696710e7
commit
3eb21f8757
2 changed files with 41 additions and 5 deletions
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue