テレビのアニメはどこから来る? / 第65話

第65話:イトは、HTMLのopenを全部closedにしなかった|変えるのは文字?属性?

ピコルート第65話。イトがHTMLの一括置換を止め、タグ・要素・属性とDOMを分けて、本当に変える場所だけを選び直します。

HTMLのタグ・要素・属性HTML sourceとDOMの違い親子関係と意味のある構造一括置換とtargeted update11分更新

第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-stateopenclosed現在の状態なので変える
strong内のtextopenclosed読者へ見せる状態なので変える
imgのsrcroof-open.avifroof-closed.avif現在の画像なので変える
imgのaltNorth roof openNorth roof closed現在の画像説明なので変える
aのhref/commands/open-roof維持次に選べるactionなので変えない
a内のtextopen roof維持次に選べるactionなので変えない

一覧は、変更命令ではなかった。

何を変え、何を変えないかをイト自身が決めた記録だった。

イトが六本へ分岐する一括置換wheelを閉じ、DOM tree上の状態text、data attribute、画像source、代替説明の四箇所だけをシアンのholderへ移し、操作linkの二本を青い経路へ残している
同じopenでも役割は違う。一括置換を止め、現在の状態だけを更新し、次の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が四つの結果を並べた。

試した案状態textstate 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話。

イトは、全部の文字を大きくしなかった。


参考にした一次資料

この物語を、解説で深める

第65話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

HTMLとは|Webページの骨組みを表すマークアップ言語を読む