HTML のテキスト入力欄の minlength 属性2021年12月09日 21時30分

この記事は HTML アドベントカレンダーの 9 日目の分です。


HTML の input 要素と textarea 要素には minlength 属性を指定できます。入力するテキストの最小文字数を指定するものですが、いくつか注意点があります。

まず、minlength 属性を指定していても入力が必須となるわけではありません。何も入力していなくても (0 文字の入力でも) クライアント側フォーム検証を通ってフォーム送信可能になります。入力を必須にするには別途 required 属性を指定する必要があります。

次に、minlength 属性で指定される「文字数」とは UTF-16 の符号単位の数です。絵文字など Unicode の BMP (基本多言語面) 外の文字 (U+10000 以上の文字) は 2 文字として数えられます。この文字数の扱いは maxlength 属性も同じです。

<input type="text" name="q" minlength="2" maxlength="3">

というテキスト入力欄があったとき、「🍣」1 文字だけでもフォーム送信可能ですし、「🍣🍣」と 2 文字入力することはできません。

参考文献

HTML 要素の innerText プロパティで要素が生成されうる2021年12月10日 11時13分

この記事は HTML アドベントカレンダーの 10 日目の分、兼 JavaScript アドベントカレンダーの 10 日目の分です。


HTML 標準では、HTML 文書を JavaScript などのプログラミング言語から扱うためのインターフェイスも定義しています。そのひとつが HTML 要素の innerText プロパティ、要素の内容を文字列として取得・設定するプロパティです。かつて Internet Explorer が独自に実装し、他の Web ブラウザも追従した結果として標準化されたものですね。

innerText プロパティの値の取得

innerText プロパティで取得できる値は、ブラウザにレンダリングされたようなテキスト内容となります。CSS で display: none; が指定された要素の内容は含まれませんし、display: block; が指定された要素の内容の前後には改行文字 (U+000A) が挿入されます。表のセル同士の間にはタブ文字 (U+0009) が挿入されます。

そのため、innerText プロパティの値を取得する際にはブラウザの描画処理、いわゆるリフローが走ることになります。スタイルを気にせず単に要素のテキスト内容を取得したいときは、innerText プロパティではなく textContent プロパティを使ったほうがよいでしょう。textContent プロパティの値の取得時にはリフローが走りません。

innerText プロパティの値の設定

innerText という名前からすると値の設定時には単なるテキストノードが生成されそうな気もしますが、HTML 要素が生成されることもあります。設定する値に改行文字が含まれるときは、その改行文字が br 要素に置換されるのです。

単に要素のテキスト内容を設定したいときは、innerText プロパティではなく textContent プロパティを使ったほうがよいでしょう。textContent プロパティの値の設定時には HTML 要素が生成されません。

参考文献

HTML の暗黙的なフォーム送信と CSS の :default 疑似クラス2021年12月11日 22時59分

この記事は HTML アドベントカレンダーの 11 日目の分、兼 CSS アドベントカレンダーの 11 日目の分です。


Web ページに備えられた検索機能を使うとき、検索語の入力欄で Enter キーを押下 (ソフトウェアキーボードなら「実行」といったキーを実行) して検索結果画面に進んだことのある人は多いでしょう。このようにフォーム送信ボタンを介さずにフォームを送信する機能は、HTML 標準で暗黙的なフォーム送信として定義されています。

あるフォームに含まれる送信ボタンのうち、文書順で最初に出現するものをデフォルトボタンといいます。暗黙的な送信の基本的な挙動は、あるフォームのフォームコントロール上で Enter キーを押下したりすると、そのフォームデフォルトボタンを実行したのと同じ扱いになるというものです。以下の例では、テキスト入力欄で Enter キーを押下すると、「検索する」ボタンが実行されたとみなされ、/search?q={入力した語}&feeling=so-so という URL のページに移動します。

<form action="/search">
  <p>
    <input type="search" name="q" aria-label="検索する語">
    <button type="submit" name="feeling" value="so-so">検索する</button>
    <button type="submit" name="feeling" value="lucky">いい感じに検索する</button>
  </p>
</form>

デフォルトボタンが無効化されている (disabled 属性が付与されている) 場合は暗黙的なフォーム送信が行われません。

デフォルトボタンがない (フォーム内に送信ボタンが含まれない) 場合、そのフォームに含まれるテキスト入力欄 (パスワード入力欄や日時入力欄なども含む) がひとつだけなら暗黙的なフォーム送信が行われます。テキスト入力欄がふたつ以上あるときは暗黙的なフォーム送信が行われません。これらの挙動には歴史的経緯がありそうですが調べられていません。

デフォルトボタンを Web 製作者側で指定できるようにしようという提案もありますが、あまり議論が進んでいません。

デフォルトボタンは CSS の :default 疑似クラスにマッチします。以下の CSS を適用した文書では、フォーム内の最初に登場する送信ボタンに水色のアウトラインが表示されます。

:default { outline: thick solid #0ff; }

:default 疑似クラスはデフォルトボタン以外にも、チェックボックスまたはラジオボタンで checked 属性を持つものと、option 要素で selected 属性を持つものにもマッチします。ユーザーがチェックを外したり選択肢を変更したりしても、:default 疑似クラスがマッチする要素は変わりません。

HTML 標準における段落の概念2021年12月12日 21時41分

この記事は HTML アドベントカレンダーの 12 日目の分です。


以下の HTML コードを見て、なにか居心地の悪さを感じるでしょうか?

<h1>12日の日記</h1>
天気: 晴れ
<p>今日は動いた。</p>

筆者はどうも居心地の悪さを感じます。「天気: 晴れ」の部分が暗黙的な段落になっているのがその原因です。

HTML 標準において段落 (paragraph) とは、文や記述コンテンツの塊を表す概念です。p 要素は段落を明示しますが、p 要素だけでなく li 要素や div 要素なども段落を表現します。段落の内容が文章である必要はなく、「天気: 晴れ」といった行や住所表記、フォームの入力欄を含む行なども段落となります。

何らかの要素を使って段落を明示しないといけないという決まりはどこにもないのですが、HTML 4 Strict の時代は body 要素直下にブロック要素しかこれなかったこともあり、暗黙的な段落があると p 要素をつけたくなってしまいます。

HTML 4 時代を引きずっているという点では、form 要素の内容も気になります。HTML 4 Strict では form 要素直下にもブロック要素しかこれませんでした。以下のように form 要素直下に記述コンテンツ (phrasing content) が来ていると、

<form aciton="/search">
  <p><label>検索語: <input type="search" name="q"></label></p>
  <button type="submit">検索</button>
  <input type="hidden" name="token" value="deadbeef">
</form>

すべて p 要素で囲みたくなります。

<form aciton="/search">
  <p><label>検索語: <input type="search" name="q"></label></p>
  <p>
    <button type="submit">検索</button>
    <input type="hidden" name="token" value="deadbeef">
  </p>
</form>

HTML の透過的内容モデル2021年12月13日 22時46分

この記事は HTML アドベントカレンダーの 13 日目の分です。


その昔、HTML 4 の時代には以下のようなコードは仕様違反でした。a 要素はインライン要素であり、その内容にブロックレベル要素である h3 要素は取れなかったからです。

<a href="url"><h3>見出し</h3></a>

現在の HTML 標準では、a 要素、ins 要素、del 要素などは内容モデルが透過的 (transparent) であるとされ、上述のコードも仕様に適合しています。

透過的内容モデルの要素は、その子要素から見ると透過的です。言い換えると、透過的内容モデルの要素が取れる内容は、その親要素が取れる内容を引き継ぎます。ある a 要素の親要素が div 要素なら、その a 要素の内容はフローコンテンツ (flow content) となります。親要素が p 要素なら、内容は記述コンテンツ (phrasing content) となります。

しかしながら、透過的内容モデルの要素は、その親要素から見ると透過的ではありません。透過的内容モデルの要素はどこにでも出現できるわけではないということです。a 要素、ins 要素、del 要素はいずれもフローコンテンツおよび記述コンテンツのカテゴリに属しており、フローコンテンツまたは記述コンテンツが出現できる個所にしか出現できません。以下のように表の行を囲んだりはできないのです。

<table>
  <tbody>
    <!-- a 要素の位置が不適切 -->
    <a href="url">
      <tr>
        <td>cell</td>
        <td>cell</td>
      </tr>
    </a>
  </tbody>
</table>

a 要素がフローコンテンツを内容に取りうるといっても、a 要素に対するデフォルトスタイルシートは display: inline のままであることに注意が必要です。ブラウザによっては display: inline な要素の子要素に display: block な要素があり、かつ flexbox や grid が複雑に絡み合ったときに、要素の高さの計算がまれに意図しない結果になるようです。子要素に display: block な要素がくるときは、a 要素自身にも display: block を指定しておいたほうが安全かもしれません。