HTML / CSS公開 約11分

HTML disabledとreadonlyの違い:送る値と操作を分ける

変更させない欄をdisabledにしたら、フォームのデータから番号が消えた場合に。readonlyとの違いを、入力できるか、Tabで止まるか、値が含まれるかの三点で比較します。nameの付け忘れも切り分けます。

この記事の目次
  1. 編集・移動・データの三つを別々に決める
  2. 含まれるキーが見える入力見本
  3. 番号が消えたら、無効状態とnameを順に調べる
  4. readonlyで固定できる要素と、固定できない値
  5. 値の一覧とTabの順序を一組で残す

受付番号を変更されないようにdisabledを付けたら、送信するデータに番号が入らなくなった。画面に値が見えることと、フォームのデータへ含まれることは別です。「入力できない見た目」にまとめず、操作とデータの扱いを選びます。

HTMLのform・label・inputが分かる人向けに、架空の観察会の入力見本を作ります。JavaScriptはフォームに含まれる値を表示するためだけに使います。実際の受付や送信は行わず、一枚のHTMLをブラウザーで開いて比較できます。

編集・移動・データの三つを別々に決める

disabledは、そのコントロールを無効にする属性です。対応する入力欄は通常の操作で編集できず、フォーカス対象にもならず、フォームの送信データにも含まれません。MDNのdisabledの説明にある、複数の効果を持つ指定です。

readonlyは、対応するテキスト欄などを読み取り専用にします。利用者による編集は止めますが、フォーカスを当てて値を読むことはでき、条件を満たせばフォームデータへも含まれます。単に薄い色に見せるCSSとは異なります。

text型の欄の状態 キー入力で編集 通常のTab移動 name付きの値を含める
通常 できる 止まる 含める
readonly できない 止まる 含める
disabled できない 飛ばす 含めない

表はこの教材のtext型の入力欄に対する比較です。非表示の要素や独自のtabindex、無効な親fieldsetは加えていません。複数の条件を同時に変えると、どの指定が値を除いたのか判断しにくくなります。

含まれるキーが見える入力見本

次をform-state.htmlへ保存します。受付番号はreadonly、まだ扱わない担当メモはdisabledにします。受付番号を本物の値に置き換える必要はありません。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>観察会の入力状態を比べる</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font-family: sans-serif; line-height: 1.8; color: #222; }
main { max-width: 560px; margin: auto; }
label, input { display: block; }
input { width: 100%; font: inherit; padding: 6px; margin-bottom: 16px; }
input[readonly] { background: #edf4ff; }
button { font: inherit; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
:focus-visible { outline: 3px solid #174ea6; outline-offset: 3px; }
</style>
</head>
<body><main>
<h1>観察会の入力見本</h1>
<p>架空の練習です。外部へ送信・保存しません。</p>
<form id="practice">
  <label for="note">参加メモ(編集可)</label>
  <input id="note" name="note" type="text" value="双眼鏡を持参">
  <label for="record">受付番号(読み取り専用)</label>
  <input id="record" name="record" type="text" value="R-021" readonly>
  <label for="staff">担当メモ(今回は対象外)</label>
  <input id="staff" name="staff" type="text" value="受付で確認" disabled>
  <button id="inspect" type="button" disabled>含まれる値を確認</button>
</form>
<p id="status" role="status">まだ確認していません。</p>
<pre id="result"></pre>
<noscript><p>値の一覧を作る操作にはJavaScriptが必要です。</p></noscript>
</main>
<script>
const form = document.querySelector('#practice');
const button = document.querySelector('#inspect');
const result = document.querySelector('#result');
const status = document.querySelector('#status');
form.addEventListener('submit', event => event.preventDefault());
button.addEventListener('click', () => {
  const entries = [...new FormData(form).entries()];
  result.textContent = entries.map(([key, value]) => `${key} = ${value}`).join('\n');
  status.textContent = `${entries.length}項目を表示しました。外部へは送信していません。`;
});
button.disabled = false;
</script>
</body>
</html>

最初に確認ボタンを押すと、noteとrecordの二行が出ます。staffは画面に見えても一覧にはありません。FormDataは、フォームからキーと値の組を作る仕組みです。FormDataのコンストラクターの説明にある条件に従い、nameを持つ有効な欄を対象にします。

このボタンはtype="button"なので送信を要求しません。submitも止め、Enterによる意図しないページ移動を避けています。通常の送信時の入力検証を実装した例ではありません。submitへ処理をそろえる記事と、何を観察するボタンかを区別します。

番号が消えたら、無効状態とnameを順に調べる

第一の失敗を再現するには、受付番号のreadonlyをdisabledへ変えて保存し、ページを開き直します。確認結果はnoteだけの一行になります。値を消していないのにrecordが消えるのは、無効な欄が対象から外れたためです。修正はreadonlyへ戻すこと。二行へ戻り、recordがR-021になるか再確認します。

第二の失敗は、readonlyを残したままname="record"だけを削除することです。この場合も結果は一行になります。idがrecordでも、データのキーを指定するnameの代わりにはなりません。修正ではnameを戻し、二行になることを確認します。

idはラベルのforやJavaScriptの要素取得に使い、nameはフォームデータのキーに使っています。見た目が正しく、ラベルを押すと欄へ移れることは、nameの存在を証明しません。関連付けの基本はlabelの記事で確認できます。

disabledは真偽属性なので、HTMLにdisabled="false"と書いても無効状態です。有効へ戻すなら属性を取り除きます。JavaScriptで切り替える場合はinput.disabled = falseのようにプロパティへ真偽値を渡すため、HTMLの文字列指定とは区別してください。

readonlyで固定できる要素と、固定できない値

readonlyの対応要素にはtextなどのinputとtextareaがあります。selectやcheckboxを操作不能にするためにreadonlyを付けても、同じ効果は得られません。固定済みの選択肢を見せるだけなら、通常の文章へ変え、送る値をどこで決めるかを別に設計する方法があります。

また、readonlyとdisabledの欄は制約検証の対象外です。受付番号にrequiredを足して、値が必ず入っていることを保証する設計にはしません。readonlyとrequiredの併記も避けます。アプリが埋める番号なら、その生成元で欠落を確かめる必要があります。

どちらの属性も、サーバー側で値を信用してよい根拠にはなりません。利用者の開発者ツールやスクリプトから値や属性は変えられます。本番の受付番号や料金などを確定する処理では、受信した値をサーバー側の情報と照合します。hiddenの欄を追加しても、この性質は変わりません。

一方、処理中だけ操作させたくないボタンならdisabledが候補です。今回の確認ボタンも、スクリプトの準備後に有効にしています。読み取り専用の入力と、今は利用できない操作を、同じ薄い色だけで表さず文言でも伝えます。

値の一覧とTabの順序を一組で残す

10〜15分を目安に完成例へ戻し、次の条件を試します。Tabの停止範囲はOSやブラウザーの設定にもよるため、使った環境も記録してください。

確認 期待する結果
参加メモを「雨具を持参」へ変更して確認 noteの値だけが変わり、recordはR-021
Tabで入力欄を進む 参加メモ、受付番号、確認ボタンの順。担当メモは飛ばす
受付番号へキー入力する 値は変わらず、フォーカスの枠は見える
担当メモのdisabledを外して開き直す 確認結果にstaffが加わり、三行になる

最後の変更は追加課題です。三行目が出ることを確認したらdisabledを戻します。入力状態を切り替えただけでは既に出した結果は自動更新しないため、毎回確認ボタンを押して現在の値を読み直します。

アクセシビリティの無料レッスンでフォームを点検する際は、見える値、操作できる場所、含まれるデータをそれぞれ記録できます。受付番号を編集できなくても、Tabで読めて結果にも残る。この三つを確認できれば、readonlyを選んだ理由を説明できます。

TRY A LESSON

記事の内容を、課題で確かめる

意味のあるHTMLを書く練習 →

HTMLの学習内容 →

記事についてのお問い合わせ

記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。

← 記事一覧に戻る