UNPKG

joicomponents

Version:

Design patterns to build native web components

182 lines (166 loc) 6.61 kB
<script type="module"> const innerTemplate = ` <style> html { } * { box-sizing: border-box; margin: 0; padding: 0; } body { display: inline-block; } </style> <script>(function(){ var pW = undefined; var pH = undefined; const s = document.styleSheets[0].cssRules[0]; window.addEventListener("message", function(e){ const dim = JSON.parse(e.data); s.style.width = dim.width + "px"; s.style.height = dim.height + "px"; // sizeHasChanged(); }); function sizeHasChanged(){ const html = document.children[0]; const body = html.children[1]; //scrollHeight is always as big as offsetHeight. If the offsetHeight of the body is capped at the offsetHeight of the parent, then use scrollHeight let height = body.offsetHeight === html.offsetHeight ? body.scrollHeight : body.offsetHeight; let width = body.offsetWidth === html.offsetWidth ? body.scrollWidth : body.offsetWidth; debugger; if (pW === width && pH === height) return false; pW = width; pH = height; parent.postMessage(JSON.stringify({width, height}), "*"); return true; } function observeSize() { sizeHasChanged() ? requestAnimationFrame(observeSize): setTimeout(observeSize, 150); } document.addEventListener("DOMContentLoaded", observeSize); })();</scrip` + `t>`; const outerTemplate = document.createElement("template"); outerTemplate.innerHTML = ` <style> :host, iframe { box-sizing: border-box; border: none; margin: 0; padding: 0; } :host{ overflow: visible; display: inline-block; } iframe { overflow: hidden; height: 0; /*initial value only, will be overwritten later*/ width: 0; /*initial value only, will be overwritten later*/ } </style> <iframe sandbox="allow-scripts" scrolling="no" frameborder="0"></iframe>`; import {LayoutAttributesMixin} from "https://unpkg.com/joicomponents@1.2.31/src/layout/LayoutAttributesMixin.js"; class OverflowIframe extends LayoutAttributesMixin(HTMLElement) { constructor() { super(); this.attachShadow({mode: "open"}); this.shadowRoot.appendChild(outerTemplate.content.cloneNode(true)); this._iframe = this.shadowRoot.children[1]; window.addEventListener("message", this.onMessage.bind(this)); this._ready = false; this._cbObj = this.autoUpdateInnerFrame.bind(this); this._iframeWidth; this._iframeHeight; } static get observedAttributes() { return ["srcdoc"/*, "_layout-width", "_layout-height"*/]; } attributeChangedCallback(name, oldValue, newValue) { // if (name === "_layout-width" || name === "_layout-height") { // if (!this.hasAttribute("_layout-width") || !this.hasAttribute("_layout-height")) // return console.log(false, name, newValue); // let width = Math.min(window.innerWidth, this.offsetWidth); // let height = Math.min(window.innerHeight, this.offsetHeight); // this.sendMessage(JSON.stringify({width, height})); // } // if (name === "_layout-width" || name === "_layout-width") { // console.log(name, newValue); // this.sendMessage(JSON.stringify({width: newValue})); // } if (name === "srcdoc") { const src = innerTemplate + (newValue || ""); let blob = new Blob([src], {type: "text/html"}); this._iframe.setAttribute("src", URL.createObjectURL(blob)); } } onMessage(e) { if (e.source !== this._iframe.contentWindow) return; const dim = JSON.parse(e.data); this._iframe.style.width = dim.width + "px"; this._iframe.style.height = dim.height + "px"; this.updateWidthHeight(); } autoUpdateInnerFrame() { this.updateWidthHeight() ? requestAnimationFrame(this._cbObj) : setTimeout(this._cbObj, 250); } updateWidthHeight() { const [width, height] = this.getMyPotentialContentArea(); if (this._iframeHeight === height && this._iframeWidth === width) return false; this._iframeHeight = height; this._iframeWidth = width; this._iframe.contentWindow.postMessage(JSON.stringify({width, height}), "*"); return true; } getMyPotentialContentArea() { //alternative approach here would be to make a different element, copy over the padding and border from this element //then append this at the end of the parent of this element. //then read that elements width and height, and then remove it again const oldDisplay = this.style.display; const oldWidth = this.style.width; const oldHeight = this.style.height; this.style.display = "block"; this.style.width = "100%"; this.style.height = "100%"; const width = this.clientWidth; const height = this.clientHeight; this.style.display = oldDisplay; this.style.width = oldWidth; this.style.height = oldHeight; return [width, height]; } } customElements.define("overflow-iframe-part2", OverflowIframe); </script> <style> * { margin: 0; padding: 0; box-sizing: border-box; } overflow-iframe-part2 { border: 100px solid orange; } </style> <h1>Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine!</h1> <hr> <div style="width: 100%; height:100%; border: 5px solid red;"> <!--<overflow-iframe-part2 auto-layout="w; h" srcdoc="<h1 style='height: 60%; border: 3px dotted pink;'>Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! Hello sunshine! </h1>"></overflow-iframe-part2>--> <!--<hr>--> <!--<overflow-iframe-part2 auto-layout="w; h" srcdoc="<h1 style='padding-right: 2000px; border: 2px dotted blue;'>I'm also toooooo long......</h1>"></overflow-iframe-part2>--> <!--<hr>--> <!--<overflow-iframe-part2 auto-layout="w; h" srcdoc="<h1 style='height: 100%; border: 5px dotted green'>Hello sunshine! </h1><script>setTimeout(()=> {const h1 = document.querySelector('h1'); h1.style.height = '10%'; h1.style.width = '100%';}, 1000);</script>"></overflow-iframe-part2>--> <hr> <overflow-iframe-part2 auto-layout="w; h" srcdoc="<h1 style='height: 100%; width: 110%; border: 5px dotted green'>Hello sunshine! </h1>"></overflow-iframe-part2> </div> <!-- This code is untested. I have only done superficial tests from within devtools in Chrome. The code should only work in Chrome, Safari, Firefox as template does not work in IE and Edge. -->