mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-11-01 00:06:04 +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.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