やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

HTML文書をつくる21 入力フォーム(form, fieldset, legend, label, button, input, select, textarea, progress, meter, optgroup, option, datalist, output)

 サーバに値を送信するときなど。

要素一覧

要素 概要
<form> サーバに値を送信する
<fieldset> 入力フォームをグループ化する
<legend> <fieldset>のキャプション
<label> <input>のキャプション
<button> ボタン。クリックに反応する
<input> 各種入力フォーム。
<select> コンボボックス(プルダウン)。
<textarea> 複数行テキスト入力。
<progress> 進捗バー。
<meter> メーター。
<optgroup> 選択肢グループ化
<option> 選択肢
<datalist> オートコンプリートの候補
<output> 計算結果

<form>

 サーバに情報を送信する。

<form action="login.php" method="post">
    <input type="text" name="name" id="name" required>
    <input type="password" name="pass" id="pass" required>
    <input type="submit" value="送信">
</form>
属性 概要
action 送信された情報を処理するプログラムのURL
method HTTP通信方法(get, post)かdialog<dialog>要素中でsubmitするとデータを送信せずダイアログを閉じる)
enctype 送信されたデータのMIMEタイプ(method=postのとき)
enctype属性値 概要
application/x-www-form-urlencoded 既定値。
multipart/form-data <input>type属性値がfileのときに使用する
text/plain デバッグ

 詳細はform | MDNを参照。

<fieldset><legend><label>

<form>
  <fieldset>
    <legend>性別</legend>
    <input type="radio" name="sex" value="male">
    <label for="male"></label><br/>
    <input type="radio" name="sex" value="female">
    <label for="sasquatch"></label><br/>
  </fieldset>
</form>
性別

 <fieldset>はフォームをグループ化して囲みを表示する。<legend>はそのキャプション。<label><input>のキャプション。

 ふつう以下要素は親子関係だが、特定の属性値を一致させることで、包含させずとも親子関係であると定義できる。

<form>.id <fieldset>.form
<label>.for <input>.id

 たとえば上記コード例は以下のようにも書ける。

<form>
  <fieldset>
    <legend>性別</legend>
    <label><input type="radio" name="sex" value="male"></label><br/>
    <label><input type="radio" name="sex" value="female"></label><br/>
  </fieldset>
</form>
性別

 詳細はfieldset | MDNを参照。

<button>

index.html

<button>押してね♡</button>

main.js

document.querySelector('button').addEventListener('click', (e)=>{alert('いやん♡');});

 formmethod属性値にget,postを指定できる。これは<form>要素のmethod属性値と同じである。

 type属性値にsubmit, reset, buttonを指定できる。これは<input>要素のtype属性値と同じである。

 <button><input type="button">とほぼ同じである。ただ、<button>のほうがCSSで自在に表示をコントロールしやすい。コードの可読性も高い。

 詳細はbutton | MDN参照。

<input><datalist>

 さまざまな入力フォームに対応した可読性の低いフォーム。

<input type="text" name="username" value="初期値">
type属性値 表示
button
checkbox
color
date
datetime-local
email
file
hidden
image
month
number
password
radio
range
reset
search
submit
tel
text
time
url
week

 typeによってまったく異なるデータを扱うことがわかる。そのため属性の使い方も異なる。それらを正しく使わねば、まともに利用できない場合もある。

 詳細はinput | MDN参照。

checkbox, radio

 複数の中から選択する。

type属性値 概要
checkbox 任意の数だけ選択できる(しなくてもよい)
radio どれかひとつだけ選択できる(せねばならない)

 フォームに送信するときはname, value属性が必須である。とくに同じ質問に対する選択肢はname属性値を一致させる必要がある。わかりにくいが、これでグループ化している。私としては<fieldset>nameで一括してやってほしかった。

 checked属性があるときはその属性がある要素を初期状態でチェックする。

<form>
  <fieldset>
    <legend>性別</legend>
    <label><input type="radio" name="sex" value="male"></label><br/>
    <label><input type="radio" name="sex" value="female"></label><br/>
  </fieldset>
</form>
性別

<form>
  <fieldset>
    <legend>好物</legend>
    <label><input type="checkbox" name="favorite" value="apple">りんご</label><br/>
    <label><input type="checkbox" name="favorite" value="orange">みかん</label><br/>
  </fieldset>
</form>
好物

button, reset, submit, image

 ボタン。クリックに反応して何らかの処理をする。

type属性値 概要
button 特別な処理をしない
reset 入力フォームの値を初期化する
submit サーバにデータを送信する
image サーバにデータを送信するが、テキストでなく画像を表示する

 resetボタンは設置しないほうがよい。むしろそれを設置する意味がわからない。ただの嫌がらせ。

 サーバに送信するならsubmitを使う。そうでないならbuttonを使う。

<input type="button" value="押してね♡">
<input type="reset">
<input type="submit">
<input type="image" alt="画像ボタン">

date, time, datetime-local, month, week

 日時を入力する。

type属性値 概要 value,min,max
date 年月日 20220-01-01
time 時分 12:34
datetime-local 年月日時分 20220-01-01T12:34
month 年月 20220-01
week 年週 20220-W4
<input type="date">
<input type="time">
<input type="datetime-local">
<input type="month">
<input type="week">

 ほかの属性としてstep, readonly, listがあるが割愛する。

 気になるのはweekで、週のはじまりが月曜日であること。日曜日がはじまりであってほしい場合もあるはずだが、それについてはinput week | MDNで言及されていない。

text, password, email, search, tel, url

 テキスト入力系。

type属性値 概要
text 任意テキスト
password ●で伏せ字になる
search 検索キーワード入力用
email Eメールアドレス入力用
tel 電話番号
url URL
<input type="text" placeholder="任意テキスト">
<input type="password" placeholder="パスワード">
<input type="email" placeholder="📧">
<input type="search" placeholder="🔎" name="q">
<input type="tel" placeholder="📞">
<input type="url" placeholder="https://">

 placeholderで何も入力されていないときに表示するテキストを指定できる。

 list属性に<datalist>id属性値をセットすることで入力候補を指定できる。

 pattern属性値で正規表現により入力値を制限できる。

 required属性で必須にする。

number, range

 数値を入力する。

type属性値 概要
number 数値を入力する。スピナー。
range 数値を範囲入力する。スライダー。
属性値 概要
value 初期値
min 最小値
max 最大値
step 刻み幅
list 候補(<datalist>id属性値)
<input type="number">
<input type="range">

 numberはいわゆるスピナーである。スマホによるタッチ操作では小さすぎて扱いづらいと評判の残念化したUI。だからといってranger、いわゆるスライダーを使うと、正確な入力がしづらい。結局タッチ入力用UIライブラリを使ったほうがよい。

 <datalist>を使ってrangeにメモリを表示する。

<input type="range" list="tickmarks">
<datalist id="tickmarks">
<option value="0" label="0%"></option>
<option value="10"></option>
<option value="20"></option>
<option value="30"></option>
<option value="40"></option>
<option value="50" label="50%"></option>
<option value="60"></option>
<option value="70"></option>
<option value="80"></option>
<option value="90"></option>
<option value="100" label="100%"></option>
</datalist>

 目盛りとラベルの追加 | MDNによると、ブラウザによって実装状況がちがうらしい。Chrome 66ではlabelに対応しているが<datalist>CSSdisplaynoneのため非表示されるとか。そこで<datalist>style="display:inline;"属性を付与してみたのが以下。ラベルの表示位置がメチャクチャである。

<input type="range" list="tickmarks">
<datalist id="tickmarks" style="display:inline;">
<option value="0" label="0%" style="display:inline;"></option>
<option value="10" style="display:inline;"></option>
<option value="20" style="display:inline;"></option>
<option value="30" style="display:inline;"></option>
<option value="40" style="display:inline;"></option>
<option value="50" label="50%" style="display:inline;"></option>
<option value="60" style="display:inline;"></option>
<option value="70" style="display:inline;"></option>
<option value="80" style="display:inline;"></option>
<option value="90" style="display:inline;"></option>
<option value="100" label="100%" style="display:inline;"></option>
</datalist>

 こちらで色々と対策が書いてあるが、どれもCSSやHTMLをごちゃごちゃと書かねばならない。シンプルに解決できない。それならUIライブラリを探して使ったほうがよいと思う。

 あと、rangeは現在値も表示されない。また、垂直にするためにの標準的な方法がなく、ブラウザごとに異なる書き方をせねばならない。色々と未熟であるが、垂直にするには以下のCSSを書けばよい。

input[type="range"] {
  writing-mode: bt-lr; /* IE, Edge */
  -webkit-appearance: slider-vertical; /* WebKit系 */
}

 style属性に書くと以下。

<input type="range" list="tickmarks" style="writing-mode: bt-lr; -webkit-appearance: slider-vertical;">

color

 色を選ぶ。

type属性値 概要
color 色を選ぶ。カラーピッカー
<input type="color">

 value属性値で初期値をセットする。#RRGGBBである。R(赤),G(緑),B(青)はそれぞれ2桁の16進数表記で入力する。光の三原色であり加法混色により24bitカラーを作る。アルファチャンネルは非対応。

<input type="color" value="#FF0000">

 list属性で候補をセットする。

<input type="color" list="colorlist">
<datalist id="colorlist">
<option value="#FF0000" label="赤"></option>
<option value="#00FF00" label="緑"></option>
<option value="#0000FF" label="青"></option>
</datalist>

file

 ファイルを選ぶ。

type属性値 概要
file ファイルを選ぶ。ファイル選択ダイアログ。
<input type="file">

属性 概要
accept 選択できるファイル形式を定義する。値はMIMEタイプをカンマ区切りにすることで入力する。
multiple trueにするとファイルを複数選択できる。
capture 画像、音声、動画をキャプチャするデバイスを選択する。
files 選択されたファイルを列挙する。

 acceptでファイル形式を限定する。

<input type="file" accept="image/png, image/jpeg, image/png, image/gif">
<input type="file" accept="image/*">

 captureで新しいファイルをキャプチャするデバイスを選択する。

capture属性値 概要
user ユーザ側を向いているカメラやマイクを使う
environment 外側を向いているカメラやマイクを使う

 お察しのとおり、captureは主にスマホなどカメラが2つあるデバイスで動作する。PCでは通常のファイル選択ダイアログになる。

<input type="file" capture="user">
<input type="file" capture="environment">

 ふつう、カメラかマイクの選択だったり、画像か動画の選択だったりすべきではないか? よくわからない。

 multipleで複数ファイル選択できるようにする。

<input type="file" multiple>

 filesで選択したファイルをJavaScriptで取得する

<input id="fileItem" type="file" multiple>
var ui = document.getElementById('fileItem').files[0];
ui.addEventListener('change', () => {
  var file = document.getElementById('fileItem').files[0];
  alert(file.name);
});
ui.addEventListener('click', (e) => {e.target.value = '';});

 同じファイルを選んだらchangeイベントが発火しない罠がある。ブラウザによってはvalue属性値を空にすることで対策できる。

 Filesは配列として扱える。Fileは以下のプロパティをもつ。すべて読取専用。

プロパティ 概要
lastModified 最終更新日時のエポックタイムを返す
lastModifiedDate 最終更新日時をDate型で返す
name ファイル名を返す
webkitRelativePath URLの相対パスを返す
size バイト単位のファイルサイズを返す
type MIMEタイプを返す
メソッド 概要
slice 指定した範囲のバイナリを返す。
stream() ファイル内容を読めるようにする
text() ファイル内容をテキストにして返す
arrayBuffer() ファイル内容をバイナリ配列にして返す

hidden

 隠し値。サーバに送るときによく使われる。

type属性値 概要
hidden ファイルを選ぶ。ファイル選択ダイアログ。
<input type="hidden" name="hidden1" value="value1">

<select><optgroup><option>

 コンボボックス。

<select>
<option value="apple">りんご</option>
<option value="orange">みかん</option>
<option value="banana">バナナ</option>
</select>

 size属性値を項目数と同じにすればリストにできる。

<select size="3">
<option value="apple">りんご</option>
<option value="orange">みかん</option>
<option value="banana">バナナ</option>
</select>

 multiple属性で複数選択できる。(Ctrlキーを押したままクリックする。Shiftキーを押したまま終点要素をクリックする)

<select size="3" multiple>
<option value="apple">りんご</option>
<option value="orange">みかん</option>
<option value="banana">バナナ</option>
</select>

 マウスだけで複数選択できないのが残念なところ。

 選択肢をグループ化する。

<select size="8" multiple>
<optgroup label="果物">
<option value="apple">りんご</option>
<option value="orange">みかん</option>
<option value="banana">バナナ</option>
</optgroup>
<optgroup label="主食">
<option value="katu">カツ丼</option>
<option value="curry">カレー</option>
<option value="katu-curry">からあげ</option>
</optgroup>
</select>

 残念ながらグループ化はネストしても、インデント表示してくれない。

<select size="5" multiple>
<optgroup label="食べ物">
<optgroup label="果物">
<option value="apple">りんご</option>
<option value="orange">みかん</option>
<option value="banana">バナナ</option>
</optgroup>
</optgroup>
</select>

<textarea>

<textarea rows="5" cols="80">
ここに
初期値が
入ります。
</textarea>

 あまりにもザコすぎるテキストエディタ

 これなら外部ライブラリを使ったほうがずっといい。

<div id="editor" style="min-height: 100px"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ace.js"></script>
<script>
  const editor = ace.edit("editor",{
    theme: "ace/theme/monokai",
    mode: "ace/mode/javascript",
    minLines: 2
  });
</script>

<progress>

<progress max="100" value="70"></progress>

 進捗を表す。最小値は常に0でありセットできない。

<meter>

 数値の状態を表す。とくに閾値判定を視覚的にわかりやすくする。

  • シンプル
  • low/hight
  • optimum

シンプル

<meter name="fuel"
 min="0" max="100" value="50">
</meter>

 値が20

 値が50

 値が80

low/hight

<meter name="fuel"
       min="0" max="100"
       low="33" high="66"
       value="50">
</meter>

 値が20

 値が50

 値が80

 低すぎても高すぎてもダメなときに使う。

optimum

<meter name="fuel"
       min="0" max="100"
       low="33" high="66" optimum="80"
       value="50">
</meter>

 値が20

 値が50

 値が80

 多いほうが好ましいときに使う。

<output>

 ユーザ入力による計算結果である。

<form oninput="result.value=parseInt(a.value)+parseInt(b.value)">
  <input type="range" id="b" name="b" value="50" /> +
  <input type="number" id="a" name="a" value="10" /> =
  <output name="result" for="a b">60</output>
</form>
+ = 60

所感

 あまりにも多すぎる。これに加えて実際に使うときはJavaScriptが必須。CSSもいじりたい。でも外部ライブラリのほうがいいかも。かくして考慮すべき事項はインフレしつづける。UIは超大変。

対象環境

$ uname -a
Linux raspberrypi 5.10.63-v7l+ #1496 SMP Wed Dec 1 15:58:56 GMT 2021 armv7l GNU/Linux