◆ 親の div などに disabled を設定したらその内側の全要素に反映させたい
◆ MutationObserver で属性監視すればできる
◆ CustomElements で専用の要素にしたほうが見やすいかも

hyperHTML を使ってると それぞれの要素の属性ごとに条件に応じた値を設定することになります
変更が許可されてないとページ内の input や button は全部 disabled にしたい という風に特定の条件時にまとめて属性設定したいことがあります

<input disabled=${data.can_change}>

こういう設定を全部の input にしていくのはつらいです
実際にはもっと条件が長い式になったりしますし 書くのが面倒なだけでなく 可読性も落ちます

DOM を直接操作する場合なら

if (!data.can_change) {
for (const input of elem.querySelectorAll("input")) {
input.disabled = true
}
}

と 1 つ書いて全体に反映させられます
こっちのほうがスッキリしていて好きです
ただ プログラムを見ないと disabled 処理があるということがわからないデメリットもあります

DOM のツリーで子孫要素全体を disabled にできるのが見やすくわかりやすいと思います

<div disabled=${data.can_change}>
<input>
<input>
<input>
</div>

div の diabled が input に継承されるのが理想です
MutationObserver を使って属性を監視すればできそうです
どうせなら CustomElements を使って MutationObserver を毎回準備しなくても自動で子孫要素に伝播するようにしたほうが良さそうです

そういうわけで作ってみました

ctrl-element

ctrl-element という要素が内側の要素の属性を管理します

import {bind, wire} from "https://cdn.jsdelivr.net/npm/hyperhtml@2.25.5/esm.js"

const attributes = {
disabled: { type: "toggle", target: "input" },
checked: { type: "toggle", target: "input" },
readonly: { type: "toggle", target: "input" },
}

class CtrlElement extends HTMLElement {
static get observedAttributes() {
return Object.keys(attributes)
}

attributeChangedCallback(name, old_value, new_value, namespace){
const selector = attributes[name].target
for (const target of this.querySelectorAll(selector)) {
if (attributes[name].type === "toggle") {
if (new_value != null) {
target.setAttribute(name, "")
} else {
target.removeAttribute(name)
}
} else {
target.setAttribute(name, new_value)
}
}
}
}
customElements.define("ctrl-element", CtrlElement)

for (const [a, t] of Object.entries(attributes)) {
Object.defineProperty(CtrlElement.prototype, a, {
get() {
if (t.type === "toggle") {
return this.hasAttribute(a)
} else {
return this.getAttribute(a)
}
},
set(value) {
if (t.type === "toggle") {
if (value) {
this.setAttribute(a, "")
} else {
this.removeAttribute(a)
}
} else {
this.setAttribute(a, value)
}
},
})
}

const a = true

bind(document.body)`
<style>
ctrl-element { display: block }
</style>
<div>
<ctrl-element disabled=${a}>
<input>
<input>
<input>
</ctrl-element>
<ctrl-element disabled=${!a}>
<input>
<input>
<input>
</ctrl-element>
</div>
`

いい感じですね
全部の input に同じ条件を書く必要がありません

注意するところは ctrl-element で指定した属性は内側で hyperHTML の機能で属性を設定してはいけないところです
内部的には DOM を直接操作してるので hyperHTML の機能と競合します
その他の条件と組み合わされるなど input と ctrl-element に同じ属性を書きたい場合は ctrl-element を使わず直接 hyperHTML で指定が必要です

MutationObserver 版

作ってみると MutationObserver でもあまり変わらない気がしたので MutationObserver を使った版です

import {bind, wire} from "https://cdn.jsdelivr.net/npm/hyperhtml@2.25.5/esm.js"

const attributes = {
disabled: { type: "toggle", target: "input" },
checked: { type: "toggle", target: "input" },
readonly: { type: "toggle", target: "input" },
}

class CtrlElement extends HTMLElement {
constructor() {
super()
mo.observe(this, {
attributes: true,
attributeFilter: Object.keys(attributes),
})
}
}
customElements.define("ctrl-element", CtrlElement)

for (const [a, t] of Object.entries(attributes)) {
Object.defineProperty(CtrlElement.prototype, a, {
get() {
if (t.type === "toggle") {
return this.hasAttribute(a)
} else {
return this.getAttribute(a)
}
},
set(value) {
if (t.type === "toggle") {
if (value) {
this.setAttribute(a, "")
} else {
this.removeAttribute(a)
}
} else {
this.setAttribute(a, value)
}
},
})
}

const mo = new MutationObserver(mutations => {
for (const record of mutations) {
const elem = record.target
const name = record.attributeName
const value = elem.getAttribute(name)
const selector = attributes[name].target
for (const target of elem.querySelectorAll(selector)) {
if (attributes[name].type === "toggle") {
if (value != null) {
target.setAttribute(name, "")
} else {
target.removeAttribute(name)
}
} else {
target.setAttribute(name, value)
}
}
}
})

const a = true

bind(document.body)`
<style>
ctrl-element { display: block }
</style>
<div>
<ctrl-element disabled=${a}>
<input>
<input>
<input>
</ctrl-element>
<ctrl-element disabled=${!a}>
<input>
<input>
<input>
</ctrl-element>
</div>
`

ctrl-element の定義は少ないですが 外部に MutationObserver のイベント時の処理を書く必要があります
どちらもそこまで変わらないです