ECMAScript 仕様の連鎖生成規則 ― 2021年12月19日 23時58分
この記事は JavaScript アドベントカレンダーの 19 日目の分です。
JavaScript の言語仕様は ECMAScript として策定されています。ECMAScript の仕様書を読んでいくにあたって注意すべき点として、連鎖生成規則 (chain production) に対する挙動の記述が省略されているかもしれないということが挙げられます。筆者は当初このことに気づかず、「この生成規則に対する Runtime Semantics が定義されていないけど、どうなっているんだろう?」と悩みました。
連鎖生成規則とは、finally 節に対する生成規則
- Finally :
- finally Block
のように、生成規則の右辺に非終端記号がひとつだけ存在するものです。終端記号はいくつあっても (ひとつもなくても) 構いません。この場合、Finally 生成規則に対する Runtime Semantics は、その右辺に存在する非終端記号 Block に対する Runtime Semantics がそのまま呼び出されるというものになります。
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 イベントを基に、どのボタンが最後に実行されたのか記憶しておく必要がありました。そこからするとずいぶん楽になりましたね。
最近のコメント