joicomponents
Version:
Design patterns to build native web components
165 lines (151 loc) • 5.9 kB
HTML
<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";
alertSize();
});
function alertSize() {
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 setTimeout(alertSize, 150);
// debugger;
pW = width; pH = height;
parent.postMessage(JSON.stringify({width, height}), "*");
requestAnimationFrame(alertSize);
}
document.addEventListener("DOMContentLoaded", alertSize);
})();</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._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;
// debugger;
const dim = JSON.parse(e.data);
this._iframe.style.width = dim.width + "px";
this._iframe.style.height = dim.height + "px";
if (!this._ready) //first time only
this.observeStrangeCousin();
this._ready = true;
}
observeStrangeCousin() {
const omg = this.parentNode.children[0]; //todo
const width = omg.clientWidth;
const height = omg.clientHeight;
if (this._iframeHeight === height && this._iframeWidth === width)
return setTimeout(this.observeStrangeCousin.bind(this), 150);
this._iframeHeight = height;
this._iframeWidth = width;
this._iframe.contentWindow.postMessage(JSON.stringify({width, height}), "*");
requestAnimationFrame(this.observeStrangeCousin.bind(this));
}
}
customElements.define("overflow-iframe-part2", OverflowIframe);
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
overflow-iframe-part2 {
border: 4px 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;">
<div id="omg"
style="position: absolute; border: none; margin: 0; padding: 0; box-sizing: content-box; height: 100%; width: 100%; visibility: hidden;"></div>
<overflow-iframe-part2 auto-layout="w; h"
srcdoc="<h1 style='height: 60%; border: 3px solid 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="<span style='padding-right: 2000px; border: 2px solid blue;'>I'm also toooooo long......</span>"
></overflow-iframe-part2>
<hr>
<overflow-iframe-part2 auto-layout="w; h"
srcdoc="<h1 style='height: 100%; border: 5px solid green'>Hello sunshine! </h1>"
></overflow-iframe-part2>
</div>
<!--
<script>setTimeout(()=> {const h1 = document.querySelector('*'); h1.style.height = '10%'; h1.style.width = '110%';}, 1000);</script>
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.
-->