ジャンルから探す

Web Design English

HTML form要素の新機能と活用法【2023年のアプデ】

フォームのバリデーション表示は、:invalidではなく:user-invalidを使うだけで体験が大きく変わります。入力する前から赤枠になる、あの不快な挙動が消えます。2023年10月に Baseline 入りし、現在は全モダンブラウザで使えます。

ひとつ前置きすると、type="email"required は2023年の新機能ではありません。HTML5(2011年前後)から存在するものです。この記事では、その「もう当たり前の部分」を手早く整理したうえで、2023年以降に実際に使えるようになった機能:user-invalidshowPicker()requestSubmit()field-sizingaccent-color)に紙面を割きます。

スポンサーリンク

フォームで今も土台になる機能(HTML5から)

まず前提の確認です。以下は全ブラウザで長年使えており、JavaScriptを書く前にこれで足りないかを先に考えるべきものです。

機能 役割 登場
type="email" / url / tel 形式チェックとモバイルキーボードの切替 HTML5
required 必須入力 HTML5
pattern 正規表現による形式指定 HTML5
<datalist> 入力候補の提示 HTML5
<output> 計算結果の表示 HTML5
novalidate ブラウザ標準の検証を止める HTML5

基本形はこうなります。

<form>
  <label for="email">メールアドレス</label>
  <input type="email" id="email" name="email"
         autocomplete="email" required>

  <label for="zip">郵便番号</label>
  <input type="text" id="zip" name="zip"
         inputmode="numeric" autocomplete="postal-code"
         pattern="\d{3}-?\d{4}"
         title="123-4567 または 1234567 の形式で入力してください">

  <button type="submit">送信</button>
</form>

ここで押さえておきたい注意点が3つあります。

  • patternは前後が暗黙にアンカーされます^$を書かなくても全体一致です。逆に部分一致にはできません
  • patternには必ずtitleを添える:エラーメッセージにこの文言が使われます。無いと「形式が違います」としか出ません
  • placeholderlabelの代わりにしない:入力を始めた瞬間に消えるので、何を入れる欄だったか分からなくなります

そして最も重要な点として、ブラウザ側のバリデーションはセキュリティ対策ではありません。DevToolsでnovalidateを足せば誰でも回避できます。サーバー側の検証は必ず別途実装してください。

:user-invalid で「入力前に赤くなる」問題を解決する

:invalidの最大の欠点は、ページを開いた瞬間から未入力の必須項目が全部エラー表示になることです。まだ何もしていないユーザーを叱っている状態になります。

:user-invalidは、ユーザーが実際に操作して離れたあとにしかマッチしません。

/* ❌ 開いた瞬間から赤くなる */
input:invalid {
  border-color: #d33;
}

/* ✅ ユーザーが触ったあとだけ赤くなる */
input:user-invalid {
  border-color: #d33;
}
input:user-valid {
  border-color: #2a7;
}

エラーメッセージの出し分けも、CSSだけで書けます。

<div class="field">
  <label for="mail">メールアドレス</label>
  <input type="email" id="mail" name="mail" required>
  <p class="error">正しいメールアドレスを入力してください</p>
</div>
.error { display: none; color: #d33; font-size: .875rem; }

input:user-invalid ~ .error { display: block; }
セレクター マッチする条件 使いどころ
:invalid 値が不正なら常に 送信ボタンの:has()判定など
:user-invalid ユーザーが操作したあとで不正なとき 入力欄の見た目
:user-valid ユーザーが操作したあとで正しいとき チェックマーク表示
:placeholder-shown プレースホルダーが見えているとき フローティングラベル

スポンサーリンク

showPicker() で日付・色・候補リストを任意のタイミングで開く

<input type="date">のカレンダーは、既定では小さなアイコンを正確にクリックしないと開きません。showPicker()を使うと、入力欄のどこを押してもピッカーを開けます。2022年9月から全モダンブラウザで使えます。

const dateInput = document.getElementById('date');

dateInput.addEventListener('click', () => {
  if (!('showPicker' in HTMLInputElement.prototype)) return;
  try {
    dateInput.showPicker();
  } catch (e) {
    // NotAllowedError など。既定の挙動に任せる
  }
});

対応するのは date / month / week / time / datetime-local / color / file、そして<datalist>を持つ入力欄です。

制約が3つあるので、必ずtryで囲んでください。

  • ユーザー操作が起点でないとNotAllowedErrorsetTimeoutから呼ぶことはできません
  • クロスオリジンのiframe内ではSecurityErrorfilecolorは例外)
  • readonlydisabledだとInvalidStateError

自動送信は this.submit() ではなく requestSubmit()

絞り込みセレクトの変更で自動送信する、という実装はよくありますが、this.submit()を使ってはいけません

<!-- ❌ バリデーションが全て無視される、submitイベントも発火しない -->
<form onchange="this.submit()">
  ...
</form>

form.submit()バリデーションを飛ばし、submitイベントも発火させません。そのため、他のスクリプトが仕掛けた送信前処理(二重送信防止、トークン付与など)が全て素通りします。

正しくはrequestSubmit()です。こちらは「送信ボタンを押したのと同じ」扱いになります。

<form id="filter-form" action="/search">
  <select name="category">
    <option value="all">すべて</option>
    <option value="book">本</option>
  </select>
  <noscript><button type="submit">絞り込む</button></noscript>
</form>
const form = document.getElementById('filter-form');

form.addEventListener('change', () => {
  form.requestSubmit(); // 検証もsubmitイベントも通る
});

あわせて、submitイベントではどのボタンで送信されたかevent.submitterで取れます。「下書き保存」と「公開」を1つのフォームで捌くときに便利です。

form.addEventListener('submit', (e) => {
  if (e.submitter?.value === 'draft') {
    // 下書き保存の処理
  }
});

スポンサーリンク

入力欄を中身の量に合わせて伸縮させる(field-sizing)

テキストエリアを入力量に応じて自動で広げる処理は、これまでJavaScriptでscrollHeightを測って書いていました。CSSのfield-sizing: contentで1行になります。 2026年6月に Baseline 入りしました。

textarea {
  field-sizing: content;
  min-height: 4lh;   /* 最低4行分は確保する */
  max-height: 20lh;  /* 伸びすぎを防ぐ */
  width: 100%;
}

input[type="text"] {
  field-sizing: content;
  min-width: 8ch;
}

min-heightmax-heightをセットで指定するのがコツです。指定しないと、空のときに1行分まで縮み、長文で画面いっぱいまで伸びます。lh単位(1行の高さ)を使うと行数で指定できて分かりやすいです。

チェックボックスやスライダーの色を1行で変える

チェックボックス・ラジオ・rangeprogressの色は、長らくappearance: noneで作り直すしかありませんでした。accent-colorを使えば1行です。

:root {
  accent-color: #2a7fff;
}

ブラウザ標準の見た目・キーボード操作・アクセシビリティをそのまま保ったまま、ブランドカラーだけ差し替えられます。見た目のためだけにappearance: noneで自作するのは、フォーカスリングやスクリーンリーダー対応まで自分で再実装することになるので、まずこちらを試してください。

実装時のチェックリスト

フォームを作るときに、私が毎回確認している項目です。

  • labelidを必ず結ぶplaceholderaria-labelで代用しない(aria-labelは視覚的なラベルが本当に置けない場合の最後の手段)
  • autocompleteを正しく指定するname / email / tel / postal-code / street-address / one-time-code など。入力の手間が目に見えて減ります
  • inputmodeでモバイルキーボードを合わせるnumeric(数字のみ)とdecimal(小数点あり)を使い分ける
  • エラー表示は:user-invalid
  • 自動送信はrequestSubmit()
  • サーバー側の検証を必ず実装する

入力欄そのものの属性についてはHTML input要素の新機能と活用法に、確認ダイアログの実装はdialog要素の使い方にまとめています。入力状態に応じたレイアウト制御はCSS :has()セレクターが便利です。

まとめ

  • type="email"requiredはHTML5からの機能で、2023年の新機能ではない
  • エラー表示は:user-invalid / :user-validを使う(2023年10月 Baseline)
  • showPicker()で日付・色・候補リストを任意のタイミングで開ける。tryで囲む
  • 自動送信はthis.submit()ではなくrequestSubmit()。検証もsubmitイベントも通る
  • テキストエリアの自動リサイズはfield-sizing: content(2026年6月 Baseline)
  • フォーム部品の色はaccent-colorappearance: noneで自作しない
  • ブラウザ側の検証はセキュリティ対策ではない

フォームは「JavaScriptを書かずに済ませられる範囲」がこの数年でかなり広がりました。自作の検証ライブラリを入れる前に、標準機能で足りないかを一度確認してみてください。