HTML の img 要素と image タグ ― 2021年12月20日 23時40分
この記事は HTML アドベントカレンダーの 20 日目の分、兼 JavaScript アドベントカレンダーの 20 日目の分です。
HTML のタグといえば、要素の開始位置、終了位置、そして属性を指定する記述のことですね。開始タグは小なり記号 (<
) の後に要素名が続きます。しかしながら、タグに既述した名前とは別の名前の要素が生成される場合があります。
以下の JavaScript コードを実行すると、JavaScript コンソールには (IMAGE
ではなく) IMG
と出力されます。タグに記述された名前は image
なのに、img
要素が生成されているのです。
const div = document.createElement('div');
div.innerHTML = '<image src="" alt="">';
console.log(div.firstChild.tagName); // => "IMG"
実はこれは HTML のパース処理における例外的な扱いで、開始タグの名前が image
のとき、内部的にはパースエラーとしつつ名前を img
に読み替えることになっています。Web 製作者の記述ミスを救済しようとする慈悲の心の現れですね。
あくまでも HTML のパース処理における例外なので、JavaScript で document.createElement
メソッドを使って要素を生成するときには適用されません。以下の例では "[object HTMLUnknownElement]"
という文字列が出力されるはずです。(しかし Firefox では "[object HTMLElement]"
という文字列が出力されます。)
const image = document.createElement('image');
console.log(image.toString());
参考文献
Perl で HTML をパースするモジュール ― 2021年12月19日 23時40分
この記事は HTML アドベントカレンダーの 19 日目の分、兼 Perl アドベントカレンダーの 19 日目の分です。
Perl で HTML をパースするモジュールはいくつもあります。
HTML::Parser
そのままの名前ですね。HTML コードをパースしていき、開始タグ、終了タグ、テキストなどを認識するとそれをイベントとして知らせてくれる、プッシュ型のパーサーです。
HTML の要素の内容モデルや、ある要素のタグが省略可能かといった知識は持っていません。あくまでもタグやテキストなどの出現を知らせるだけで、文書木を構築するわけではないからです。
逐次的なパースに対応しています。HTML 文書全体を表すコードを一気に入力として与えなくてもよく、HTTP 通信中に受け取った分からパースしていくといったことが可能です。
Web 製作者の意図を汲み取ろうと努めており、「壊れた」HTML コードでもパースできます。ただし、現在の HTML 標準 (いわゆる HTML5) のパース規則には対応しておらず、現在の主要 Web ブラウザのパース結果と異なる結果になることがあります。
HTML::PullParser
HTML::Parser
をプル型のパーサーとして利用できるようにしたものです。HTML::Parser
と同じ HTML-Parser ディストリビューションに含まれています (HTML::Parser
をインストールしようとすると HTML::PullParser
もインストールされます)。
HTML::TokeParser
HTML::PullParser
を拡張し、開始タグだけを取り出す、テキストだけを取り出すといったことを簡単にできるようにしたものです。HTML::Parser
と同じ HTML-Parser ディストリビューションに含まれています。
HTML::TreeBuilder
HTML をパースし、文書木を構築してくれます。DOM にアクセスするのと似た感覚で HTML 文書を扱えます。内部的なパースには HTML::Parser
を使っています。
HTML の要素や内容モデルに関しては HTML 4 相当の知識しかないため、現在の HTML 標準に従った HTML コードを期待通りパースできないことがあります。ignore_unknown
オプションを無効にしないと HTML5 で追加された要素が無視される (パース結果の文書木に現れない) 点は、特に注意が必要です。
HTML::TreeBuilder::XML
HTML をパースし、文書木を構築してくれます。内部的なパースには libxml2 ライブラリを使っています。
一部 HTML::TreeBuilder
と同名のメソッドを持ちますが、完全に互換性があるわけではありません。
HTML::HTML5::Parser
HTML をパースし、文書木を構築してくれます。XS を使わず Pure-Perl で書かれています。
2013 年で更新が止まっているため、現在の HTML 標準には一部追従していない……と思っていたのですが、2021 年 9 月に更新されていました (この記事を書いている途中で気づきました)。現在の HTML 標準にどこまで適合しているかは未確認です。
HTML5::DOM
HTML をパースし、文書木を構築してくれます。内部的なパースには MyHTML ライブラリを使っています。
MyHTML ライブラリは開発が停止しており、後継の Lexbor ライブラリに引き継がれています。
終わりに
現在の HTML 標準に最も近いのは HTML5::DOM
だと思っていたのですが、HTML::HTML5::Parser
が更新されていくのなら今後はどうなるかわかりません。
HTML のフォーム送信データを書き換える ― 2021年12月18日 22時28分
この記事は HTML アドベントカレンダーの 18 日目の分、兼 JavaScript アドベントカレンダーの 18 日目の分です。
HTML のフォーム送信処理が実行されるときに、JavaScript を使って送信データを書き換えるにはどうしたらよいでしょうか。まず思いつくのが submit
イベント発生時にフォームコントロール要素の入力値 (value
プロパティの値など) を書き換えることでしょう。別の手段として formdata
イベントがあります。formdata
イベントを利用すれば、フォームコントロール要素に手を加えずに送信データを書き換えることができます。
formdata
イベントに紐づく FormDataEvent
オブジェクトは formData
プロパティを持ち、その値はまさに今送信されようとしているデータを表した FormData
オブジェクトとなっています。この FormData
オブジェクトに対して append
、set
、delete
メソッドを呼び出すことで、送信されるデータを変更することができるのです。
以下の例では、フォームを送信する際に time
というキーに対して現在日時を表す文字列が設定され、/search?q=keyword&time=2021-12-18T12%3A00%3A00.000Z
といった URL に移動します。
<form action="/search"
onformdata="event.formData.append('time', new Date().toISOString());">
<p>
<input type="search" name="q" aria-label="検索する語">
<button type="submit">検索する</button>
</p>
</form>
formdata
イベントは、form
要素オブジェクトを引数にして FormData
コンストラクタを呼び出したとき (new FormData(form)
) にも (引数として渡された form
要素上で) 発生します。
HTML の文書型宣言に含まれる要素名 ― 2021年12月17日 22時54分
この記事は HTML アドベントカレンダーの 17 日目の分です。
HTML 文書は文書型宣言 <!DOCTYPE html>
から始めることになっています。文書型宣言は、現在では Web ブラウザにその文書を標準準拠モードで処理してもらうための目印としてしか使われていません。しかし、XHTML や HTML 4 以前 (SGML に基づく HTML 仕様) では、その文書が従うべき規則 (どの要素はどんな内容しか持てないとか、どの要素はどの属性しか持てないとか) を参照するものでした。
SGML (および XML) の文書型宣言において、<!DOCTYPE
の次に記述される名前はルート要素の名前を表しています。<!DOCTYP html ...>
と書かれていれば、SGML パーサーはその文書のルート要素が html
要素であるという前提で文書のパースを進めていくことになります。文書型宣言における要素名は、文脈自由文法における開始記号に相当するものと言えるでしょう。
HTML 2.0 の時代から、html
、head
、body
の各要素は開始タグも終了タグも省略できました。<title>
タグから記述が始まる HTML 文書があったとして、もし SGML パーサーがルート要素の要素型を知らなかったら、title
要素をルート要素として扱ってしまいかねません。html
要素がルート要素と知っているからこそ、html
要素と head
要素が存在しているがそれらの開始タグが省略されおり、head
要素の子として title
要素が出現しているのだということがはっきりわかるわけです。
参考文献
HTML のフォーム送信に使われたボタンを判別する ― 2021年12月16日 23時00分
この記事は HTML アドベントカレンダーの 16 日目の分、兼 JavaScript アドベントカレンダーの 16 日目の分です。
HTML のフォームには複数の送信ボタンを設置できます。ある送信ボタンを実行してフォームを送信すると、その送信ボタンの name
属性と (送信ボタンが button
要素なら) value
属性の値も送信データに含まれます。
どの送信ボタンを実行してフォームを送信したのか、JavaScript からは submit
イベントに紐づく SubmitEvent
オブジェクトの submitter
プロパティで判別できます。以下の例では、「公開する」または「削除する」ボタンを実行すると、JavaScript コンソールにそのボタンが出力されます。
<form
method="post"
action="/edit"
onsubmit="console.log(event.submitter); return false;">
<p>
<button type="submit" name="publish" value="1">公開する</button>
<button type="submit" name="delete" value="1">削除する</button>
</p>
</form>
submitter
プロパティが実装される前は、各ボタンの click
イベントを基に、どのボタンが最後に実行されたのか記憶しておく必要がありました。そこからするとずいぶん楽になりましたね。
最近のコメント