joicomponents
Version:
Design patterns to build native web components
107 lines (95 loc) • 3.1 kB
JavaScript
let innerTempl = function (width, height) {
return `
<style>
html {
}
body, html {
margin: 0; /*the inner iframe takes no space in itself*/
}
</style>
<script>(function(){
var fW, fH, pW, pH;
const s = document.styleSheets[0].cssRules[0].style;
function setSize(width, height){
if (width === fW && height === fH)
return;
s.width = (fW = width) + "px";
s.height = (fH = height) + "px";
}
function sizeHasChanged(){
const html = document.children[0];
const height = html.scrollHeight;
const width = html.scrollWidth;
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);
}
window.addEventListener("message", function(e){
const dim = JSON.parse(e.data);
setSize(dim.width, dim.height);
});
document.addEventListener("DOMContentLoaded", setSize.bind(undefined, ${width}, ${height}));
document.addEventListener("DOMContentLoaded", observeSize);
})();</script>`;
};
const outerTemplate = document.createElement("template");
outerTemplate.innerHTML = `
<style>
:host, iframe {
border: none;
margin: 0;
padding: 0;
}
:host{
overflow: visible;
display: 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>`;
class ScrollsizeIframe extends 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;
}
static get observedAttributes() {
return ["srcdoc", "viewport-width", "viewport-height"];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "srcdoc") {
const width = this.getAttribute("viewport-width") || 0;
const height = this.getAttribute("viewport-height") || 0;
const src = innerTempl(parseInt(width), parseInt(height)) + (newValue || "");
let blob = new Blob([src], {type: "text/html"});
this._iframe.setAttribute("src", URL.createObjectURL(blob));
}
if (name === "viewport-width" || name === "viewport-height") {
const width = this.getAttribute("viewport-width") || 0;
const height = this.getAttribute("viewport-height") || 0;
this._iframe.contentWindow.postMessage("[" + width + "," + height + "]", "*");
}
}
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";
}
}
customElements.define("overflow-iframe-part2", ScrollsizeIframe);