HTMLの新しいinput要素の機能とその活用法
<input> は type の選び方で挙動が大きく変わります。この記事ではタイプ別に「そのまま使えるもの」「実務では避けるべきもの」を判断できるように整理します。
この記事は2024年の公開後、2026年9月に見直しました。 公開当時のコードに、<textarea> に pattern 属性を指定するという誤りがありました。訂正したうえで、タイプ別の解説を追加しています。
pattern属性は<input>専用です。<textarea>では効きません。最小文字数を求めるならminlengthを使いますinputmode/autocomplete/list/patternはいずれも2024年の新機能ではありません
type 別・使ってよいものと避けるべきもの
| type | 判断 | 理由 |
|---|---|---|
text / email / tel / url / password |
そのまま使う | 挙動が安定している |
search |
使う | クリアボタンが付く |
date / time / datetime-local |
条件付き | UIがブラウザごとに大きく違う |
number |
用途を限定 | ホイールで値が変わる、先頭の0が落ちる |
range |
条件付き | 正確な値の入力には向かない |
color |
使う | OSのカラーピッカーが開く |
file |
使う | accept と capture が効く |
month / week |
避ける | Firefox が未対応でただのテキスト欄になる |
type=”date”:使う前に知っておくこと
表示形式はOSとブラウザのロケールで決まり、CSSでも属性でも変えられません。 日本語環境では「2024/06/09」、英語環境では「06/09/2024」のように表示されます。
<label for="birthday">生年月日</label>
<input type="date" id="birthday" name="birthday"
min="1900-01-01" max="2024-12-31"
autocomplete="bday">
知っておくべき点は3つです。
valueは必ずYYYY-MM-DD形式:表示がどうであれ、送信される値とJavaScriptから読む値はこの形ですmin/maxもYYYY-MM-DDで書く:範囲外は選べなくなります- 未入力時は空文字:
valueAsDateはnullを返します
日付の読み取りは valueAsDate に注意
const input = document.getElementById('birthday');
input.value; // "2024-06-09"(文字列)
input.valueAsDate; // Date オブジェクト。ただし UTC 基準
valueAsDate はUTCの午前0時として解釈されます。 日本時間で扱うと日付が1日ずれることがあるため、日付だけを扱うなら文字列のまま処理する方が安全です。
// ずれる可能性がある
const d = input.valueAsDate;
console.log(d.getDate()); // 環境によって前日になる
// 文字列のまま扱う
const [y, m, day] = input.value.split('-').map(Number);
カレンダーを開きやすくする
既定では小さなアイコンを正確にクリックしないとカレンダーが開きません。showPicker() で欄のどこを押しても開くようにできます。
input.addEventListener('click', () => {
if (!('showPicker' in HTMLInputElement.prototype)) return;
try { input.showPicker(); } catch {}
});
type=”number”:使う場面を限定する
「数字を入れる欄」に無条件で使ってはいけません。 郵便番号・電話番号・クレジットカード番号には向きません。
- 先頭の
0が落ちる(0120→120) - ハイフンが入力できない
- マウスホイールで値が勝手に変わる
- 桁数の多い数値が指数表記になることがある
向くのは「増減させたい数量」だけです。その場合も min / max / step を必ず付けます。
<!-- 数量:これは type="number" が正しい -->
<label for="qty">数量</label>
<input type="number" id="qty" name="qty" value="1" min="1" max="99" step="1">
<!-- 郵便番号:type="text" にする -->
<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 の形式で入力してください">
ホイールでの誤操作は、フォーカス時にホイールを無効化して防げます。
document.querySelectorAll('input[type="number"]').forEach((el) => {
el.addEventListener('wheel', (e) => {
if (document.activeElement === el) e.preventDefault();
}, { passive: false });
});
{ passive: false } が必要です。付けないと preventDefault() が無視されます。
type=”file”:accept と capture
<!-- 画像を複数選ぶ -->
<input type="file" name="photos" accept="image/*" multiple>
<!-- スマホで背面カメラを直接起動 -->
<input type="file" name="doc" accept="image/*" capture="environment">
<!-- 拡張子で絞る -->
<input type="file" name="sheet" accept=".csv,.xlsx,text/csv">
accept は検証ではありません。 ユーザーはダイアログで「すべてのファイル」に切り替えられます。種類とサイズは必ずサーバー側で確認してください。
選択後にクライアント側で弾く場合はこう書きます。
const MAX = 5 * 1024 * 1024; // 5MB
fileInput.addEventListener('change', () => {
for (const file of fileInput.files) {
if (!file.type.startsWith('image/')) {
alert('画像ファイルを選んでください');
fileInput.value = ''; // 選択を解除する
return;
}
if (file.size > MAX) {
alert('5MB以下のファイルを選んでください');
fileInput.value = '';
return;
}
}
});
fileInput.value = '' で選択を解除できます。 これを忘れると、無効なファイルが選ばれたままの表示が残ります。
type=”color” と type=”range”
<label for="theme">テーマカラー</label>
<input type="color" id="theme" name="theme" value="#2a7fff">
<label for="volume">音量</label>
<input type="range" id="volume" name="volume"
min="0" max="100" step="5" value="50"
list="volume-marks">
<datalist id="volume-marks">
<option value="0"></option>
<option value="50"></option>
<option value="100"></option>
</datalist>
<output for="volume" id="volume-out">50</output>
type="range" に list を付けると目盛りが表示されます。また、range は現在値が見えないので、<output> を必ず添えてください。
const volume = document.getElementById('volume');
const out = document.getElementById('volume-out');
volume.addEventListener('input', () => {
out.textContent = volume.value;
});
input イベントと change イベントの違いに注意してください。input はつまみを動かしている間ずっと発火し、change は離したときだけ発火します。表示の更新は input、サーバーへの保存は change が適切です。
色や部品の見た目は accent-color で変えられます。appearance: none で作り直すより堅実です。
:root { accent-color: #2a7fff; }
textarea には pattern が効かない
公開当時のコードにあった誤りです。 pattern 属性は <input> だけのもので、<textarea> には存在しません。
<!-- 効かない -->
<textarea required pattern=".{10,}" title="10文字以上"></textarea>
<!-- 正しい:minlength を使う -->
<label for="feedback">フィードバック</label>
<textarea id="feedback" name="feedback"
required minlength="10" maxlength="1000"
placeholder="ご意見をお聞かせください"></textarea>
minlength の注意点:ユーザーが一度も入力していない状態では発動せず、required の方が先に効きます。また、改行は1文字として数えられます。
入力量に応じて高さを自動調整したい場合は、CSSの field-sizing が使えます。
textarea {
field-sizing: content;
min-height: 4lh;
max-height: 20lh;
width: 100%;
}
残り文字数を出すなら、maxlength と組み合わせます。
const ta = document.getElementById('feedback');
const counter = document.getElementById('counter');
ta.addEventListener('input', () => {
counter.textContent = `${ta.value.length} / ${ta.maxLength}`;
});
実装例:ユーザー登録フォーム
ここまでの内容をまとめた形です。
<form>
<label for="username">ユーザー名</label>
<input type="text" id="username" name="username"
required autocomplete="username" minlength="3">
<label for="email">メールアドレス</label>
<input type="email" id="email" name="email"
required autocomplete="email">
<label for="password">パスワード</label>
<input type="password" id="password" name="password"
required autocomplete="new-password" minlength="8">
<label for="birthdate">生年月日</label>
<input type="date" id="birthdate" name="birthdate"
required autocomplete="bday" max="2024-12-31">
<label for="phone">電話番号</label>
<input type="tel" id="phone" name="phone"
autocomplete="tel" inputmode="tel"
pattern="0\d{1,4}-?\d{1,4}-?\d{4}"
title="例: 090-1234-5678">
<button type="submit">登録</button>
</form>
ポイントを整理します。
- 新規登録は
autocomplete="new-password":current-passwordにするとパスワードマネージャーが誤動作します <input type="submit">ではなく<button type="submit">:ボタン内にアイコンや複数行を入れられます- 電話番号は
type="tel":形式が国ごとに違うため、ブラウザ側の検証はありません。patternで補います
エラー表示は :user-invalid を使ってください。:invalid だと開いた瞬間に全部赤くなります。
input:user-invalid {
border-color: #d33;
}
詳しくはHTML form要素の新機能と活用法にまとめました。
datalist は候補以外も入力できる
<label for="search">商品を検索</label>
<input type="search" id="search" list="products"
enterkeyhint="search" placeholder="商品名を入力">
<datalist id="products">
<option value="スマートフォン"></option>
<option value="ノートパソコン"></option>
<option value="タブレット"></option>
<option value="ヘッドフォン"></option>
</datalist>
<datalist> は入力の補助であって、選択肢の強制ではありません。 候補にない値もそのまま送信できます。選択肢を限定したいなら <select> を使ってください。
そのほかの制約は次の通りです。
- 候補リストの見た目はCSSで変更できません(ブラウザのUIです)
- 絞り込みが前方一致か部分一致かはブラウザ依存です
- 数十件を超えると使いづらくなります
入力欄の属性についてはHTML input要素の新機能と活用法で詳しく扱っています。
まとめ
patternは<input>専用。<textarea>にはminlengthtype="date"のvalueは常にYYYY-MM-DD。valueAsDateはUTC基準でずれるtype="number"は「増減させる数量」だけに使う。 郵便番号や電話番号には使わないtype="number"のホイール誤操作は{ passive: false }付きで無効化するacceptは検証ではない。サーバー側の確認が必須。value = ''で選択解除できるtype="range"には<output>を添える。表示更新はinput、保存はchange<datalist>は候補以外も入力できる。強制したいなら<select>- 新規登録のパスワードは
autocomplete="new-password"
type を1つ選び間違えるだけで、ユーザーが入力できない欄ができてしまいます。「その値は増減させるものか」「形式は国際的に一定か」の2点で判断すると、選択を誤りにくくなります。