◆ 要素の属性がクエリパラメータに反映される

WebComponents が使えるようになって結構経ちますが いまでも iframe を使うシーンが 0 ではありません
iframe はフレーム内で完全に独立したページを開きますが WebComponents は同じページ内で JavaScript 空間などは共有です
グローバルに影響するとか コンポーネント化したいもののいろいろな都合で iframe のほうが良いこともあります
ただ iframe は使い勝手がそんなに良くないのでカスタムエレメントでラップします

iframe ではコンポーネントに設定を伝える方法は URL しかないのでクエリパラメータです
これをカスタムエレメントの属性で指定できるようにします
どっちも文字列になるので キャスト不要なところは相性いいです

class IFrameWrapperElement extends HTMLElement {
static get observedAttributes() {
return []
}

static get src() {
return "about:blank"
}

static get frame_style() {
return "display: block; border: 0;"
}

static get sheet() {
if(this._sheet) {
return this._sheet
} else {
const sheet = new CSSStyleSheet()
sheet.replaceSync(`iframe { ${this.frame_style} }`)
this._sheet = sheet
return sheet
}
}

connectedCallback() {
if(this.shadowRoot) return

const ifr = document.createElement("iframe")
this.ifr = ifr
this.attachShadow({mode: "open"}).append(ifr)
this.shadowRoot.adoptedStyleSheets = [this.constructor.sheet]
this.render()
}


attributeChangedCallback(name, old_value, new_value) {
this.render()
}

render() {
const url = new URL(this.src, location)
for(const attr of this.constructor.observedAttributes) {
if(this.hasAttribute(attr)) {
url.searchParams.append(attr, this.getAttribute(attr))
}
}
this.ifr.src = url.href
}
}

これがベースとなるクラスです
IFrame から始まるとインターフェースぽい名前になりました

ロードしたい HTML が /foo/bar.html だった場合はそのコンポーネントをこういうふうに作ります

customElements.define(
"foo-bar-element",
class FooBarElement extends IFrameWrapperElement {
get src() {
return "/foo/bar.html"
}

static get observedAttributes() {
return ["xx", "yy"]
}
}
)

document.body.append(document.createElement("foo-bar-element"))

HTML の URL を src に書きます
動的なクエリパラメータがある場合は パラメータ名を observedAttributes に書きます
これはそのまま要素の属性になって監視されます
属性に変更があれば自動で URL に反映されて iframe の src が書き換わります

iframe は名前通り inline ですが baseline 周りのせいでずれて見えることがあるのでデフォルトは display: block にしてます
また border も邪魔なのでなしにしています
変更したい場合は frame_style に iframe のスタイルを記述すれば可能ですが 基本的に iframe ではなく外側のカスタムエレメント側にスタイルをつけることを想定しています