Closed18

HTMLの「定義」と「通信の流れのうちどのタイミングで登場するのか」を学ぶ

Yug (やぐ)Yug (やぐ)

DOMについては基本的に理解できているつもりだが、HTMLというものは結局何なんだ?となってきたので言語化できるレベルに知識を固めておきたい

クライアント↔サーバの流れ, ハイドレーション, レンダリング 周りでDOM構築/更新が行われるが、それに関連して「じゃあHTMLってどう関わってる?関わってない?」「そもそもHTMLって何?静的ファイルってどゆこと?」ってなったので

個人的に気になってるのは、最終的な出力つまり画面/見た目がHTMLであると言えるのか、それとも入力地点つまりインプットがHTMLなのか、ということ

HTMLというのは、スタートを指すのかゴールを指すのかどっちもなのかどっちでもないのか

Yug (やぐ)Yug (やぐ)

へぇ

以前 uhyo さんにより「HTML はプログラミング言語である」と示されたことは記憶に新しいところですが、

https://qiita.com/uhyo/items/9b830c93fa4765bdd3e5

なるほど

筆者はHTMLでプログラムを書けるプログラミング言語、その名も「The HTML Programming Language (THPL)」を作りました。なので、明らかにHTMLはプログラミング言語です。

「〇〇でプログラムを書ける言語(=プログラム言語)を作れるならば、その〇〇もプログラム言語と呼べる」という前提があるということ?

というより「プログラミング言語」の定義は「チューリング完全であること」っぽい

プログラミング言語は、情報を組織し処理するタスクについての理解を容易にし、アルゴリズムを正確に表現することができる。特に、チューリング完全であることが特徴である

https://ja.wikipedia.org/wiki/プログラミング言語

とりあえずこれを解説していく流れぽい
https://html-lang.org/

Yug (やぐ)Yug (やぐ)

ほぇ、そういう感じ?ほんとに(この書き方初見だけど)プログラム書けるのか、htmlで

<data value="1"></data>      <!-- 1 をスタックにプッシュ -->
<output id="loop"></output>  <!-- スタックの先頭要素を出力 -->
<data value="1"></data>      <!-- 1 をスタックにプッシュ -->
<dd></dd>                    <!-- 1 を追加 -->
<dt></dt>                    <!-- スタックの先頭要素を複製 -->
<data value="11"></data>     <!-- 比較対象の 11 をプッシュ -->
<small></small>              <!-- 新しい値が 11 よりも小さいかどうか確認 -->
<i>                          <!-- 上の結果が真の場合 -->
  <a href="#loop"></a>         <!-- #loop にジャンプ -->
</i>

へぇぇぇ~~

まずは公式サイトにある、1 から 10 までの数をコンソールに出力するサンプルプログラムを眺めてみましょう:
すでに述べたように、HTML はスタック指向のプログラミング言語です。つまり、スタックに値を積んでいき、各値に対して何らかの操作を加えていくというのが HTML プログラミングの基本的なスタイルです。スタックに対する各操作はコマンドと呼ばれ、<command> のようなタグにより記述されます。

Yug (やぐ)Yug (やぐ)

すげぇぇぇぇ、試してみたらほんとにコンソールに出力された!

コード

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>HTML, the programming language</title>
    <script src="https://html-lang.org/html.js"></script>
</head>

<body>
    <main>
        <data value="1"></data> <!-- 1 をスタックにプッシュ -->
        <output id="loop"></output> <!-- スタックの先頭要素を出力 -->
        <data value="1"></data> <!-- 1 をスタックにプッシュ -->
        <dd></dd> <!-- 1 を追加 -->
        <dt></dt> <!-- スタックの先頭要素を複製 -->
        <data value="11"></data> <!-- 比較対象の 11 をプッシュ -->
        <small></small> <!-- 新しい値が 11 よりも小さいかどうか確認 -->
        <i> <!-- 上の結果が真の場合 -->
            <a href="#loop"></a> <!-- #loop にジャンプ -->
        </i>
    </main>
</body>
Yug (やぐ)Yug (やぐ)

はーなるほど。スタックの中身を見てやっと理解できた

上のプログラムでは、まず <data> タグにより 1 をスタックにプッシュしています。次に、<output> タグによりスタックの先頭要素をコンソールに出力します。そしてインクリメント用の 1 をスタックにプッシュし、<dd> (add) タグによりそれを最初の値に加算します。さらに、<dt> (duplicate top) タグにより足し算の結果を複製し、ループを終了すべきか確認するために 11 をプッシュします。<smalll> タグにより、スタックから値を 2 つポップし、両者の大小関係を比較してその結果をプッシュします。最後に、<i> (if) タグにより、直前の演算結果に応じた条件分岐をおこない、条件が真の場合に <a> タグによりプログラム冒頭の #loop にジャンプします。このようにして、1 から 10 までの数を順に出力できます。

初回のループにおけるスタックの状態遷移を図示すると以下のようになります:

1
1 1
2
2 2
2 2 11
2 true
2
...

<small>で比較するときに先頭2つをpopするつまり消されてしまうので複製しておく必要があるのね

確かにソースコードであるhtml.jsを見てみるとシンプルにpop()を2回実行してるな

html.js
"small": function (elt, env) { // next < top
  let top = env.stack.pop();
  let next = env.stack.pop();
  env.stack.push(next < top);
},
Yug (やぐ)Yug (やぐ)

とはいえ、html.jsの中身がソースコードなので見てみると、まぁ当然JSであって、大いにJSに手助けされているのは否めないが、それでもそれを利用しつつ動く時点でHTMLはプログラミング言語と言っていいだろう、ということなのだろう

Yug (やぐ)Yug (やぐ)

まず定義:「ウェブサイトのコンテンツの構造を知るために使うコード」

HTML (HyperText Markup Language、ハイパーテキスト・マークアップ・ランゲージ)は、ウェブサイトのコンテンツの構造を作るために使うコードです。例えば、コンテンツは段落、箇条書きのリスト、画像の使用、データテーブルなどの組み合わせで構成されています。タイトルが示すように、この記事では HTML とその機能の基本的な理解ができるように説明します。

Yug (やぐ)Yug (やぐ)

  • HTMLでは実際の値と言うか文字列の部分はただの「コンテンツ」と呼ぶだけなのか
    • DOMに変換されるときに「テキストノード」になる
  • 開始タグから終了タグまでの間に含まれるすべてを「要素」と呼ぶ
Yug (やぐ)Yug (やぐ)
  • 属性の名前と属性の値を含めて属性と呼ぶ


class が属性の名前で、 editor-note が属性の値

  • 値が無い属性もある

一部の属性、たとえば required には値がありません。

Yug (やぐ)Yug (やぐ)

これは初耳だ...!!属性の値の引用符は消しても機能するのか!

メモ: ASCII のホワイトスペース(または " ' ` = < > のいずれかの文字)を含まない単純な属性値は引用符を省略することができますが、コードを一貫性のあるものにし、理解を容易にするため、すべての属性値を引用符で囲むことをお勧めします。

ほんまや!(ただcolor:のあとにスペースを入れるとエラーになるので慣習通り書くことはできない)

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タイトルだよ</title>
</head>
<body>
    <h1 style=color:aqua;>h1だよ</h1>
</body>
</html>
Yug (やぐ)Yug (やぐ)

要素は確実に他の要素の中もしくは外で開始し、終了する必要があります。上記の例のように要素が重複してしまうと、ウェブブラウザーは言おうとしていることを推測してもっとも良いと思われる解釈をするため、予期せぬ結果になることがあります。そうならないよう気を付けましょう!

これ試してみたら、多分ブラウザが良い感じに解釈してくれたことで、</p>の前に</strong>が来るように変更されてた。おもしろ~~~

とはいえこのブラウザの賢い解釈によって事故ることもあるから正しい書き方してね~ということ

Yug (やぐ)Yug (やぐ)

コンテンツを持たない要素もあります。そのような要素を 空要素 (void element) と呼びます。すでに HTML ページにある <img> 要素を例に見ていきましょう

<img src="images/firefox-icon.png" alt="My test image" />

この要素は 2 つの属性を持っていますが、終了タグ </img> がありませんし、内部にコンテンツもありません。これは画像要素は、その機能を果たすためにコンテンツを囲むものではないからです。画像要素の目的は、画像を HTML ページの表示させたいところに埋め込むことです。

  • コンテンツ(DOMでいうテキストノード)を持たない要素もある
    • Reactのコンポーネントと見た目は同じだな
Yug (やぐ)Yug (やぐ)

あー確かにaltって1のアクセシビリティの意味もあるのか。2だけしか考えてなかった

また、 alt (alternative、代替) 属性も指定しています。 alt 属性は、以下のような理由で画像を見られない人に向けて説明するテキストを指定するものです。

  1. 目が不自由な人。著しく目の不自由な人はよくスクリーンリーダーと呼ばれるツールを使っていて、それが画像の alt 属性の内容を読み上げます。
  2. 何らかの理由で画像の表示に失敗した場合。例えば、 src 属性の中のパスをわざと正しくないものに変更してみてください。ページを保存したり再読み込みしたりすると、画像の場所に下記のようなものが表示されるでしょう。

積極的に、わかりやすいものをつけていかねば

Yug (やぐ)Yug (やぐ)

とりあえずざっと読み終わったが、やはりHTMLの抽象的な定義と実際の書き方に終始している

クライアント・サーバーの間の流れやハイドレーション周りでどう関わってくるのか、という流れでHTMLを説明している記事は無いだろうか

Yug (やぐ)Yug (やぐ)

いや、でも、うーん...!?
https://zenn.dev/ak/articles/c28fa3a9ba7edb

この記事を再度読んで考えてみたけど、もう答えは出ているかもしれない...。

最初の自分の疑問に答えると、HTMLはただのインプット、というか一番最初にあるものだな。

本当にそれだけの役割かも。

出力としてHTMLに戻された上で画面表示されるとかそういうのは多分無い。

復習としてHTML解析(パース)の一連の流れをちゃんとまとめたい


  • まずサーバーからHTMLが渡ってくる(ここが最初で最後のHTMLの役割!終了!のはず
    • だがこれは実質0/1のビットでしかないデータ構造になっている
      • サーバから送られてきたHTMLファイルは、「0」と「1」でできたデータになっています。

      • これがちょっと違和感なんだよなぁ。HTMLなのに0/1のビットってことはもうそれHTMLじゃないじゃん!?
      • サーバにある時点ではちゃんとHTMLなんだけど、渡ってくる経過で0/1になっちゃう、というかならざるを得ないという感じなのかもしれない。
      • あ、だからもうこれはHTMLではなくBytesというもので捉えるってことか、なるほど
      • ブラウザは、サーバから受け取ったデータをそのままHTMLとして解釈することはできないので、自分で扱うことができる形、つまりDOMに変換する必要があります。この作業を 解析 ( Parse ) と言います。

      • やはりそうか。HTMLはサーバでしか生存しない。で、渡ってきたときにはもう死んでる。Bytesになっちゃってる。んでそれをじゃあHTMLに戻してあげるのかというと、そういう訳でもなく、扱いやすいようにDOMってやつにして終了。だからHTMLはほんとに最初に生存してるだけって感じかと
  • そのBytesがCharactersという人間にもわかる所謂「文字」になる
    • 多分このCharactersがふつう想像するHTMLの姿そのもの、もしくは似てるものだと思う
    • だからまぁこのCharactersをHTMLとして捉えるのであれば、「一度死んだHTMLを再度復元している」と言えるな。まぁ結局最終的にはDOMになる訳だが
  • Charactersを1文字ずつTokenとして解釈して見ていき、判別が確定できるTokenに来た瞬間、Start Tag TokenやCharacter Tag Token, End Tag Tokenなどにひとまとまりで変換する
    • 変換というか最初から「次のTokenからはStart Tag Tokenだな」とか絶対わかる気がするので変換の必要が無い、というか変換の余地が無いような気もしているが...
    • なので判別確定できる瞬間、はじめて「ひとまとまりで解釈できるようになる」というだけの気もする
  • TokensがNodeになる
    • Nodeとはオブジェクトである

    • オブジェクトなので、要はまぁやっと人間が扱えそうな形になると言って良いかも
  • NodesがDOMという親子関係を表現した集合体になって終了
    • これは多分、ただくっつけてるだけなので、一番軽いフェーズな気がしている
    • 理由は、そもそもHTMLのネストから分かる親子関係はずっと継承してきているつまりBytesやらNodesやらはその情報をずっと持っているので、Nodesの時点ですでにDOMの情報は内包されていると言える。appendChildやらのメソッドも既にNodesの時点で用意されてる。
    • だが内包されているだけであって形としてはできあがってないので、組み立てていくのがDOM構築のフェーズということ
    • つまり例えるなら、「Nodesの中にパズルの答えはあるので、それを見ながらただただ組み立てていく」というだけのことをしている。なのでNodes→DOMのフェーズが一番やってること少ない感が個人的にしている
    • ちゃんと言い換えるなら、このフェーズだけ唯一 「変換」ではなく「そのまま組み立てるだけ」 という作業であるという感じかな
このスクラップは2025/01/16にクローズされました