第64話で、イトはJSONのbodyだけを同じまま送り直すwheelを止めた。
method、target、field、content。
requestを一通へ戻すと、屋根を閉じるcommandは一度だけ受け付けられた。
少し遅れて届いたreceiptには、こうあった。
200 OK
Content-Type: text/html
そのcontentをブラウザ工房へ渡すと、温室の状態pageが現れた。
北棟の屋根は、もう閉じている。
ところが画面の中央には、青い文字が残っていた。
status: open
イトは時刻を見た。
午前九時十三分。
見学者用screenがこのpageへ切り替わるまで、あと七分だった。
openが六つある
イトがHTML sourceを開くと、openは一つではなかった。
<article id="north-roof" data-state="open">
<h2>North roof</h2>
<p>Status: <strong>open</strong></p>
<img src="/images/roof-open.avif" alt="North roof open">
<a href="/commands/open-roof">open roof</a>
</article>
data-state="open"
画面に見えるopen。
画像file名のopen。
画像を説明するopen。
commandの行き先にあるopen。
linkの表示にあるopen。
イトは検索欄へopenと入れた。
置換先へclosedと入れる。
六件すべてが紫色に選ばれた。
イト
屋根は閉じたんだから、openを全部closedにすれば早いよ
ユイ
全部、同じ意味のopenでしょうか
イト
画面で直したい言葉は一つだよ。七分しかないし、まとめて変える
イトの指が、replace allへ近づいた。
ピコは止めなかった。
代わりに、公開前previewのgateを一段だけ開いた。
「押す前に、置換後のrequest先まで見られるよ」
イトはまだreplace allを確定せず、previewを開いた。
状態表示はclosedになった。
画像も閉じた屋根へ変わった。
けれど、pageの下にある操作linkまで変わっていた。
<a href="/commands/closed-roof">closed roof</a>
route fixtureの返事は404 Not Found。
/commands/closed-roofというcommandはない。
閉じた屋根に次に必要なのは、もう一度閉じるlinkではない。
必要なら開けるための/commands/open-roofだった。
同じ綴りでも、現在の状態を表すopenと、次にできるactionを表すopenは、役割が違っていた。
七分前の三択
残りは五分四十秒。
preview tableに三つの案が並んだ。
A. 六つのopenを全部closedへ置換する
現在の状態は早く揃う。
ただし、次のactionまでclosed-roofへ変わり、存在しないrouteを指す。
B. CSSで古いopenを隠し、上からclosedと見せる
screenだけなら早く直ったように見える。
しかし、HTMLのtextやattributeは古いまま残る。
見え方を変えても、状態の意味やlink先は直らない。
C. HTMLをDOMとして読み、変えるtextとattributeだけを選ぶ
sourceとparse後のtreeを照合する時間がいる。
代わりに、現在の状態と次のactionを別々に守れる。
ユイは案を選ばなかった。
ピコも正解を光らせなかった。
画面切替の時刻だけが、一秒ずつ近づいた。
イトは六つの紫色を見た。
そのうち、commandの行き先とlinkの表示から選択を外した。
「Cにする。openという文字じゃなくて、何の部品に入っているかで決める」
イトはreplace-allのwheelを閉じた。
tagは印、elementは部品
ブラウザ工房の左側にHTML source、右側にDOM treeが開いた。
左側には、山かっこで囲まれたarticleがある。
始まりを示す<article ...>。
終わりを示す</article>。
このようなsource syntax上の印を、ここではtagとして見る。
右側には、article elementが一つ立っていた。
その子として、h2、p、img、aのelementが並んでいる。
elementは、tagの文字だけではない。
意味を持つ部品としてDOM treeに置かれ、子のelementやtextを持つ。
p elementの中には、Status:というtextとstrong elementがある。
strong elementの中に、現在の状態を示すopenがある。
イト
tagはsourceにある始まりと終わりの印。elementは、中身や子を持ってtreeに立つ部品なんだ
ピコ
うん。似た言葉だけど、同じ箱へ押し込まないほうが、どこを直すか見つけやすいよ
イトはstrong elementを選んだ。
DOM treeの中で、そのtextだけがシアンに光った。
attributeは部品へ付く追加情報
次に、イトはarticle elementを開いた。
横に二つの情報が付いている。
id="north-roof"
data-state="open"
これらはattributeだった。
attributeはelementへ付く追加情報で、名前と値を持つ。
同じように、img elementにはsrcとaltがある。
a elementにはhrefがある。
どれも画面に同じ形で見えるtextとは限らない。
けれど、画像の場所、画像の代わりになる説明、linkの行き先として働く。
だからopenという綴りだけを見て変えると、別の役割まで一緒に動く。
イトは変更表を作った。
| DOM上の場所 | 今 | 変更後 | 判断 |
|---|---|---|---|
articleのdata-state | open | closed | 現在の状態なので変える |
strong内のtext | open | closed | 読者へ見せる状態なので変える |
imgのsrc | roof-open.avif | roof-closed.avif | 現在の画像なので変える |
imgのalt | North roof open | North roof closed | 現在の画像説明なので変える |
aのhref | /commands/open-roof | 維持 | 次に選べるactionなので変えない |
a内のtext | open roof | 維持 | 次に選べるactionなので変えない |
一覧は、変更命令ではなかった。
何を変え、何を変えないかをイト自身が決めた記録だった。

一箇所ずつ変える
イトは最初に、data-stateの値だけをclosedへ変えた。
DOM inspectionは、articleの状態をclosedと読んだ。
まだ画面のtextはopenのまま。
次に、strong elementの中のtextだけをclosedへ変えた。
状態表示が一致した。
三つ目に、imgのsrcを閉じた屋根の画像へ変えた。
四つ目に、altを現在の画像内容へ合わせた。
a elementには触れなかった。
そのhrefは、変わらず/commands/open-roofを指している。
linkのtextも、変わらずopen roofだった。
完成したHTMLはこうなった。
<article id="north-roof" data-state="closed">
<h2>North roof</h2>
<p>Status: <strong>closed</strong></p>
<img src="/images/roof-closed.avif" alt="North roof closed">
<a href="/commands/open-roof">open roof</a>
</article>
同じ親の中に、見出し、状態、画像、actionが子として残っている。
入れ子は、箱をかわいく積むためではない。
どの部品が、どのまとまりに属するかを表している。
articleの外へactionだけが飛び出していない。
pの状態textが、画像のattributeへ混ざっていない。
親子関係を追うと、pageの骨組みと変更範囲を一緒に読めた。
見た目ではなくtreeで照合する
公開前fixtureが四つの結果を並べた。
| 試した案 | 状態text | state attribute | 現在画像とalt | 次のaction | 意図しない置換 |
|---|---|---|---|---|---|
| 変更前 | 不一致 | 不一致 | 不一致 | 一致 | 0 |
| replace all | 一致 | 一致 | 一致 | 不一致 | 2 |
| CSSで隠す | 見た目のみ | 不一致 | 不一致 | 一致 | 0 |
| DOMで対象を分ける | 一致 | 一致 | 一致 | 一致 | 0 |
最後の行だけが、現在の状態と次のactionを同時に守った。
route fixtureは/commands/open-roofを既知のtargetとして認識した。
ただし、commandは送っていない。
今回の確認はHTMLとDOMまで。
実行requestは0件のままにした。
残り一分二十秒。
イトは公開前screenへ切り替えた。
画面にはstatus: closed。
閉じた北棟の画像。
その下には、次に開けるためのopen roof link。
inspection tableには、別の結果も残った。
expected state targets: 4
matched state targets: 4
unintended replacements: 0
action target preserved: 1
commands sent: 0
200でHTMLが届いただけでは、pageの意味が正しいとは限らない。
画面の一語が合っただけでも、DOM全体が正しいとは限らない。
tag、element、attribute、text、親子関係。
役割を分けると、変える場所だけでなく、変えてはいけない場所も見える。
最初にする一手
HTMLを直すときは、同じ言葉を一括置換する前に、parse後のDOMでその言葉がtextなのかattribute値なのか、どのelementへ属するのかを見る。
そのうえで、現在の状態と次のactionを別々に照合し、意図した対象だけを変える。
sourceだけで判断できないときは、DOM inspectorやvalidatorへ進む。
見た目だけを合わせるCSSは、HTMLの意味やlink先を直す代わりにはしない。
次回:骨組みは合った。でも、読みにくい
見学者用screenへの切替は間に合った。
ただし、pageは白い背景に黒い文字が縦へ並んだだけだった。
状態もactionも正しい。
親子関係も崩れていない。
それでもイトは、少し離れた場所からscreenを見て眉を寄せた。
「どこが状態で、どこが押せるlinkなのか、一目では分かりにくい」
ピコがDOM treeの横へ、まだ空のrule holderを置いた。
骨組みは変えず、色、余白、文字の大きさ、並びを重ねる場所だった。
次回、ピコルート第66話。
イトは、全部の文字を大きくしなかった。

