From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../advanced_styling_for_html_forms/index.html | 556 ++++++ files/ja/learn/forms/form_validation/index.html | 840 ++++++++ .../example_1/index.html | 415 ++++ .../example_2/index.html | 212 ++ .../example_3/index.html | 246 +++ .../example_4/index.html | 296 +++ .../example_5/index.html | 289 +++ .../how_to_build_custom_form_widgets/index.html | 901 +++++++++ .../example/index.html | 164 ++ .../forms/how_to_structure_an_html_form/index.html | 329 ++++ files/ja/learn/forms/html5_input_types/index.html | 289 +++ .../forms/html_forms_in_legacy_browsers/index.html | 219 +++ files/ja/learn/forms/index.html | 96 + .../ja/learn/forms/other_form_controls/index.html | 333 ++++ .../index.html | 2017 ++++++++++++++++++++ .../sending_and_retrieving_form_data/index.html | 341 ++++ .../sending_forms_through_javascript/index.html | 396 ++++ files/ja/learn/forms/styling_html_forms/index.html | 398 ++++ .../learn/forms/the_native_form_widgets/index.html | 339 ++++ files/ja/learn/forms/ui_pseudo-classes/index.html | 616 ++++++ .../learn/forms/your_first_form/example/index.html | 113 ++ files/ja/learn/forms/your_first_form/index.html | 313 +++ 22 files changed, 9718 insertions(+) create mode 100644 files/ja/learn/forms/advanced_styling_for_html_forms/index.html create mode 100644 files/ja/learn/forms/form_validation/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/example_1/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/example_2/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/example_3/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/example_4/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/example_5/index.html create mode 100644 files/ja/learn/forms/how_to_build_custom_form_widgets/index.html create mode 100644 files/ja/learn/forms/how_to_structure_an_html_form/example/index.html create mode 100644 files/ja/learn/forms/how_to_structure_an_html_form/index.html create mode 100644 files/ja/learn/forms/html5_input_types/index.html create mode 100644 files/ja/learn/forms/html_forms_in_legacy_browsers/index.html create mode 100644 files/ja/learn/forms/index.html create mode 100644 files/ja/learn/forms/other_form_controls/index.html create mode 100644 files/ja/learn/forms/property_compatibility_table_for_form_controls/index.html create mode 100644 files/ja/learn/forms/sending_and_retrieving_form_data/index.html create mode 100644 files/ja/learn/forms/sending_forms_through_javascript/index.html create mode 100644 files/ja/learn/forms/styling_html_forms/index.html create mode 100644 files/ja/learn/forms/the_native_form_widgets/index.html create mode 100644 files/ja/learn/forms/ui_pseudo-classes/index.html create mode 100644 files/ja/learn/forms/your_first_form/example/index.html create mode 100644 files/ja/learn/forms/your_first_form/index.html (limited to 'files/ja/learn/forms') diff --git a/files/ja/learn/forms/advanced_styling_for_html_forms/index.html b/files/ja/learn/forms/advanced_styling_for_html_forms/index.html new file mode 100644 index 0000000000..1d04415369 --- /dev/null +++ b/files/ja/learn/forms/advanced_styling_for_html_forms/index.html @@ -0,0 +1,556 @@ +--- +title: HTML フォームへの高度なスタイル設定 +slug: Learn/Forms/Advanced_styling_for_HTML_forms +tags: + - Advanced + - CSS + - Forms + - HTML + - Web + - ガイド + - 例 +translation_of: Learn/Forms/Advanced_form_styling +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Forms/Styling_web_forms", "Learn/Forms/UI_pseudo-classes", "Learn/Forms")}}
+ +

本記事では、スタイル設定が難しい一部の(不良なものと劣悪なもの)フォームコントロールで CSS を使用する方法を見ていきます。前の記事で見たように、テキストフィールドやボタンでの CSS 使用はまったく問題がありません。ここからは、HTML フォームへのスタイル設定の闇の部分を見ていきます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーと、HTMLCSS の基本的な理解。
目的:フォームのどの部分をスタイル設定するのが難しいのか、またなぜなのかを理解する。そこをカスタマイズするのに何ができるかを学ぶ。
+ +

始める前に、2 種類の HTML フォームウィジェットについておさらいしましょう:

+ +

不良: スタイルの設定が難しく複雑なトリックが必要であり、時に CSS3 の高度な知識が必要である要素:

+ + + +

劣悪: いくつかの要素は CSS でスタイル設定できません。これらが含まれます:

+ + + +

見た目: OS レベルのスタイル制御

+ +

前の記事では歴史的に、ウェブフォームのスタイル設定は OS で行われており、それがこのコントロールの見た目のカスタマイズの問題の一部となっていました。

+ +

{{cssxref("appearance")}} プロパティは OS やシステムレベルでウェブフォームのスタイル設定を制御する方法としてい作成されました。不運なことに、そのプロパティの元々の実装の動作はブラウザー間で大きく異なっており、そんなに便利ではありませんでした。最近の実装ではもっと動作が一貫してきており、十分興味深く、それは Chromium-ベースのブラウザー (Chrome, Opera, Edge), Safari, Firefox のいずれも -webkit-  のプレフィックスつきバージョン (-webkit-appearance)をサポートしています。Firefox は、ウェブ開発者の大半が -webkit- プレフィックスバージョンをサポート使っていて互換性が良いため、このように決めました。

+ +

リファレンスページを見ると -webkit-appearance の多くの値がありますが、最も便利で、おそらく唯一使うのは none です。これはあらゆるコントロールがシステムレベルのスタイル設定するのをできるだけ防止して、CSS を用いた独自のスタイル設定をできるようにします。

+ +

例えば、次のコントロールを見てみます:

+ +
<form>
+  <p>
+    <label for="search">search: </label>
+    <input id="search" name="search" type="search">
+  </p>
+  <p>
+    <label for="text">text: </label>
+    <input id="text" name="text" type="text">
+  </p>
+  <p>
+    <label for="date">date: </label>
+    <input id="date" name="date" type="datetime-local">
+  </p>
+  <p>
+    <label for="radio">radio: </label>
+    <input id="radio" name="radio" type="radio">
+  </p>
+  <p>
+    <label for="checkbox">checkbox: </label>
+    <input id="checkbox" name="checkbox" type="checkbox">
+  </p>
+  <p><input type="submit" value="submit"></p>
+  <p><input type="button" value="button"></p>
+</form>
+ +

次の CSS を適用してシステムレベルのスタイル設定を削除します。

+ +
input {
+  -webkit-appearance: none;
+  appearance: none;
+}
+ +
+

: プレフィックスつきのプロパティを使っているときでも、両方の定義を常に入れておくのが良いです — プレフィックス付きとなしと。プレフィックスつきのものは通常は"作業中"を意味するため、将来のブラウザーベンダーはプレフィックスを落とすよう合意することもあるでしょう。上記のコードではそのような結末となった将来でも耐用できます。

+
+ +

下記の例ではあなたのシステムでどのように見えるかを示します — デフォルトでは左で、上記の CSS が適用されると右です (その他のシステムでテストしたい場合はここも探してください)。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/appearence-tester.html", '100%', 400)}}

+ +

たいていの場合、効果は枠線を除去し、CSS でのスタイル設定を少し簡単にしますが、それは本質できありません。いくつかの場合 — 検索やラジオボタン/チェックボックスでは、もっと便利です。これを見ていきましょう。

+ +

検索ボックスを変更する

+ +

<input type="search"> は基本的に単なるテキスト入力のため、なぜ appearance: none; が便利なのでしょうか? 答えは macOS での Chromium ベースのブラウザーでは、検索ボックスはスタイル設定の制限があります — 例えば heightfont-size を自在に調整できません。この理由は macOS以外の Chrome ブラウザーはもう WebKit レンダリングエンジンを使っていません 、これは既定では、いくつかのフォームコントロールで Aqua の見た目が有効です。Aqua が有効だと、いくつかのフォームコントロールは scalable となりません。

+ +

これは デフォルトの Aqua を無効にする appearance: none;で修正できます:

+ +
input[type="search"] {
+    -webkit-appearance: none;
+    appearance: none;
+}
+ +

下記の例では、2 つのスタイル設定された検索ボックスが見えます。右のものは appearance: none; が適用され、左はそうでありません。macOS Chrome で見ると左のものは正しいサイズでないように見えます。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/search-appearence.html", '100%', 200)}}

+ +

興味深いことに、検索フィールドで border/background をセットしてもこの問題を解決できます、なぜならそれも Aqua を無効化や "破壊する" からです。下記のスタイル設定された検索ボックスは appearance: none; が適用されていませんが、前の例と同じ macOS Chrome の問題に悩まされていません。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/styled-search.html", '100%', 200)}}

+ +
+

: 検索フィールドでは、Edge と Chrome で入力がフォーカスされないときに "x" の削除アイコンが消えるが、Safari では残ることに気づくこともあるでしょう。CSS で消すには、input[type="search"]::-webkit-search-cancel-button { display: none; }を使用できますが、フォーカス時のアイコンも取り除き、見た目が元に戻らないようです。

+
+ +

チェックボックスとラジオボタン

+ +

チェックボックスやラジオボタンのスタイリングは難しい場合があります。たとえば、チェックボックスやラジオボタンのサイズはデフォルトのデザインの変更が意図されておらず、ブラウザーで試してみると非常に異なる反応を示します。

+ +

例えば、シンプルなテストケースを考えてみます:

+ +
<span><input type="checkbox"></span>
+ +
span {
+    display: inline-block;
+    background: red;
+}
+
+input[type=checkbox] {
+    width : 100px;
+    height: 100px;
+}
+ +

さまざまなブラウザーでの処理方法は以下のとおりです:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ブラウザー描画結果
Firefox 71 (macOS)
Firefox 57 (Windows 10)
Chrome 77 (macOS), Safari 13, Opera
Chrome 63 (Windows 10)
Internet Explorer 11 (Windows 10)
Edge 16 (Windows 10)
+ +

ラジオボタン/チェックボックスで appearance: none を使う

+ +

これまで見てきたように、チェックボックスやラジオボタンのデフォルトの見た目を {{cssxref('appearance')}}:none; で削除できます。この HTML の例を見てみましょう:

+ +
<form>
+  <fieldset>
+    <legend>Fruit preferences</legend>
+
+    <p>
+      <label>
+        <input type="checkbox" name="fruit-1" value="cherry">
+        I like cherry
+      </label>
+    </p>
+    <p>
+      <label>
+        <input type="checkbox" name="fruit-2" value="banana" disabled>
+        I can't like banana
+      </label>
+    </p>
+    <p>
+      <label>
+        <input type="checkbox" name="fruit-3" value="strawberry">
+        I like strawberry
+      </label>
+    </p>
+  </fieldset>
+</form>
+ +

さて、カスタムチェックボックスデザインでこれらのスタイルを設定しましょう。元のチェックボックスを解除することから始めましょう:

+ +
input[type=checkbox] {
+  -webkit-appearance: none;
+  appearance: none;
+}
+ +

{{cssxref(":checked")}} と {{cssxref(":disabled")}} 擬似クラスを使用して、状態の変化に合わせてカスタムチェックボックスの外観を変更します:

+ +
input[type=checkbox] {
+  position: relative;
+  width: 1em;
+  height: 1em;
+  border: 1px solid gray;
+  /* Adjusts the position of the checkboxes on the text baseline */
+  vertical-align: -2px;
+  /* Set here so that Windows' High-Contrast Mode can override */
+  color: green;
+}
+
+input[type=checkbox]::before {
+  content: "✔";
+  position: absolute;
+  font-size: 1.2em;
+  right: 0;
+  top: -0.3em;
+  visibility: hidden;
+}
+
+input[type=checkbox]:checked::before {
+  /* Use `visibility` instead of `display` to avoid recalculating layout */
+  visibility: visible;
+}
+
+input[type=checkbox]:disabled {
+  border-color: black;
+  background: #ddd;
+  color: gray;
+}
+ +

こうした疑似クラスなどは、次の記事で見つけられますが、上記は次のことをしています:

+ + + +

実際の結果が表示されます。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/checkboxes-styled.html", '100%', 200)}}

+ +

もっと理解できるよう別の例もあります:

+ + + +

{{cssxref("appearance")}} や をサポートしていないブラウザーでこれらのチェックボックスを表示できます。カスタムデザインは失われますが、チェックボックスのままに見えて使用できます。

+ +
+

注記: Internet Explorer はどのバージョンの appearance もサポートしませんが、input[type=checkbox]::-ms-check にて IE のみチェックボックスをターゲットにできます。この手法は、-ms-check という名前にもかかわらず、ラジオボタンでも機能します。

+
+ +

"劣悪な"要素に何ができるか?

+ +

今度は"劣悪な"コントロールに注目しましょう — これは完全にスタイル設定するのが本当に難しいものです。簡単にいうと、これはドロップダウンボックス、colordatetime-local のような複合コントロールタイプ、フィードバック —  {{HTMLElement("progress")}} や {{HTMLElement("meter")}}のような指向性コントロールです。

+ +

問題は、要素はブラウザー同士でいろいろな既定の見た目があって、それにスタイル設定できても、内部のいくつかはスタイル設定できないことです。

+ +

別のルック&フィールに生きる準備がある場合、サイズ変更が一貫していて、background-colors のような単一スタイル設定、システムレベルのスタイル設定を除去できる見た目を使うシンプルなスタイル設定を持って逃げることもできます。

+ +

例を見てみましょう、たくさんの"劣悪な" フォーム機能をつぎつぎに表示しています:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/ugly-controls.html", '100%', 750)}}

+ +

この例では下記の CSS を適用しています:

+ +
body {
+  font-family: 'Josefin Sans', sans-serif;
+  margin: 20px auto;
+  max-width: 400px;
+}
+
+form > div {
+  margin-bottom: 20px;
+}
+
+select {
+  -webkit-appearance: none;
+  appearance: none;
+}
+
+.select-wrapper {
+  position: relative;
+}
+
+.select-wrapper::after {
+  content: "▼";
+  font-size: 1rem;
+  top: 6px;
+  right: 10px;
+  position: absolute;
+}
+
+button, label, input, select, progress, meter {
+  display: block;
+  font-family: inherit;
+  font-size: 100%;
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+  width: 100%;
+  padding: 5px;
+  height: 30px;
+}
+
+input[type="text"], input[type="datetime-local"], input[type="color"], select {
+  box-shadow: inset 1px 1px 3px #ccc;
+  border-radius: 5px;
+}
+
+label {
+  margin-bottom: 5px;
+}
+
+button {
+  width: 60%;
+  margin: 0 auto;
+}
+ +
+

注記: 多数のブラウザーで同時にこの例をテストしたい場合、live版をここで見つけてください (ソースコードも見てください)。

+ +

また JavaScript をページに追加してコントロール自身の下にあるファイルピッカー自身で選択されたファイルを一覧しているのを心に留めておいてください。これは <input type="file"> リファレンスページの例を簡単にしたバージョンです。

+
+ +

見ていくうちに、この見た目がモダンブラウザーのようになっていくのが上手になるでしょう。

+ +

すべてのコントロールとそのラベルに対してグローバルな CSS の正規化を適用し、サイズを同様にして、親のフォントを適用するなどを行っています。これは前の記事で述べたようなことです:

+ +
button, label, input, select, progress, meter {
+  display: block;
+  font-family: inherit;
+  font-size: 100%;
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+  width: 100%;
+  padding: 5px;
+  height: 30px;
+}
+ +

コントロールに統一した影と角丸も与えて、意味のあるようにします:

+ +
input[type="text"], input[type="datetime-local"], input[type="color"], select {
+  box-shadow: inset 1px 1px 3px #ccc;
+  border-radius: 5px;
+}
+ +

範囲や、プログレスバーや、メーターはコントロールエリアの周りに美しくないボックスができるだけなので、意味はありません。

+ +

これらのコントロールタイプそれぞれの仕様と、ハイライトの難しさをこれからお話ししましょう。

+ +

Select とデータリスト

+ +

モダンブラウザーでは、select とデータリストは一般的に、デフォルトからルック&フィールを大きく変えたくないようにスタイル設定する場合、悪くないものです。

+ +

これまで boxe を、とても統一されて一貫した見た目にしてきました。とにかくデータリストコントロールは <input type="text"> なので、問題にならないことがわかっています。

+ +

2 つのものが多少問題をはらんでいます。まず最初に、select がドロップダウンであることを示す "矢印" アイコンは、ブラウザーによって異なります。また select ボックスのサイズを増やしたり、変更したりすると、醜くなりがちです。これを修正するため、例では最初に旧友の appearance: none を使ってアイコンを除去しています:

+ +
select {
+  -webkit-appearance: none;
+  appearance: none;
+}
+ +

次に生成されたコンテンツを使って独自のアイコンを作成します。コントロールの周りに特別なラッパーを置いていて、その理由は::before/::after<select> 要素では動作しなしためです (これは生成されたコンテンツが要素がフォーマットするボックスに対し相対的に配置されますが、フォーム入力は置換された要素 — 表示がブラウザーによって生成されて順番に配置されるもの — として動作するので、1 つもないためです):

+ +
<div class="select-wrapper"><select id="select" name="select">
+  <option>Banana</option>
+  <option>Cherry</option>
+  <option>Lemon</option>
+</select></div>
+ +

次に生成されたコンテンツを使って小さな下向き矢印を作り、正しい場所にポジショニングします:

+ +
.select-wrapper {
+  position: relative;
+}
+
+.select-wrapper::after {
+  content: "▼";
+  font-size: 1rem;
+  top: 6px;
+  right: 10px;
+  position: absolute;
+}
+ +

2 つ目のもう少し有名な問題は、<select> ボックスをクリックして開いたときに出てくる、オプションを含んだボックスを制御できないことです。オプションが親でセットされたフォントを継承していないのに気づくでしょう。また余白スペースや色のようなものも一貫してセットできません。例えば、Firefox では<option> 要素に colorbackground-color を適用できますが、Chrome ではそうなりません。どちらも余白スペース (例 padding)を適用できません。同じことはデータリスト用に出てくるオートコンプリートのリストにも当てはまります。

+ +

本当にオプションをスタイル設定する必要がある場合、カスタムコントロールを生成するライブラリを使用するか、独自のカスタムコントロールを作成するかのどちらかが必要で、あるいは multiple属性を使う select の場合、これはページ上に出てくるすべてのオプションを作成し、この特殊な問題を避けることができます:

+ +
<select id="select" name="select" multiple>
+  ...
+</select>
+ +

もちろんこれでも進めているデザインにはフィットしないこともありますが、注目に値します!

+ +

日付入力タイプ

+ +

日付/時間の入力タイプ (datetime-local, time, week, month) にはすべて同じ重大な関連した問題があります。実際のボックスはテキスト入力と同じくスタイル設定が容易であり、デモ内で得たものの見た目は良いです。

+ +

しかし、コントロールの内部パーツ (例 日付をピックアップするのに使うポップアップカレンダー、値を増減するスピナー) はまったくスタイル設定できず、appearence: none;を使ってスタイルを除去できません。スタイル設定に完全なコントロールが必要な場合、カスタムコントロールを生成するライブラリを使うか、自分で作らねばなりません。

+ +
+

: ここでも<input type="number"> は触れる価値があります — これにも値を増減するスピナーがあるので、同じ問題に悩まさされます。しかし、number タイプを使った場合にデータは簡単に集められて、単に text 入力を (あるいはモバイルブラウザーで数字キーパッドを表示するのに tel を) 代わりに使うのも簡単です

+
+ +

Range 入力タイプ

+ +

<input type="range"> はスタイル設定が煩わしいです。下記のようなものを使ってデフォルトのスライダートラックを完全に削除してカスタムスタイル (ここでは、薄い赤のトラック)に置き換えます:

+ +
input[type="range"] {
+  appearance: none;
+  -webkit-appearance: none;
+  background: red;
+  height: 2px;
+  padding: 0;
+  outline: 1px solid transparent;
+}
+ +

しかし、range コントロールのドラッグハンドルをカスタマイズするのはとても難しいです — range のスタイル設定を完全完全にコントロールするには、その中に複数の非標準な、ブラウザー固有の疑似要素も含んだ、複雑な CSS コードが必要です。Styling Cross-Browser Compatible Range Inputs with CSS で必要なものを細かく書くための CSS トリックを確認します。

+ +

Color 入力タイプ

+ +

color 入力タイプはそこまでひどくないです。サポートされたブラウザーでは、単に小さな枠のある単色のブロックを提供します。

+ +

枠を削除して、色のブロックだけにするには、次のようにします:

+ +
input[type="color"] {
+  border: 0;
+  padding: 0;
+}
+ +

しかし、カスタムソリューションは大きく異るようにする唯一の方法です。

+ +

ファイル入力タイプ

+ +

ファイル入力タイプは通常は OK です — 例で見てきたように、ページの残りの部分に問題なくフィットする何かを作るのはとても簡単です — コントロールの一部分でもある出力行は、入力にそう指示している場合は親のフォントを継承して、カスタムリストに名前やサイズをお好みでスタイル設定できます; 結局は作っています。

+ +

ファイルピッカーの唯一の問題は、ファイルピッカーを開くボタンは完全にスタイル設定できないことです— サイズや色設定は不可能で、別のフォントすら指定できません。

+ +

この回避法は、フォームコントロールに関連するラベルがある場合、ラベルをクリックするとコントロールがアクティブになるという事実を利用します。つまり実際のフォーム入力を次のように隠します:

+ +
input[type="file"] {
+  height: 0;
+  padding: 0;
+  opacity: 0;
+}
+ +

次にボタンのように動作するラベルをスタイル設定し、そのラベルが押された時にファイルピッカーが期待通り開くようにします:

+ +
label[for="file"] {
+  box-shadow: 1px 1px 3px #ccc;
+  background: linear-gradient(to bottom, #eee, #ccc);
+  border: 1px solid rgb(169, 169, 169);
+  border-radius: 5px;
+  text-align: center;
+  line-height: 1.5;
+}
+
+label[for="file"]:hover {
+  background: linear-gradient(to bottom, #fff, #ddd);
+}
+
+label[for="file"]:active {
+  box-shadow: inset 1px 1px 3px #ccc;
+}
+ +

上記の CSS スタイル設定の結果は、下記のライブ実行の例で見ることができます (styled-file-picker.html のライブと、ソースコードも見てください)。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/styled-file-picker.html", '100%', 200)}}

+ +

メーターとプログレスバー

+ +

<meter><progress> は多くの中で最悪かもしれません。前の例で見たように、希望する幅にだいたい正確に設定できました。しかしそれを超えると、どんな方法でもスタイル設定が本当に難しいです。高さの設定をお互いに、ブラウザー間で一貫して処理できず、背景は色付けできるものの、前面のバーはできず、appearance: none を設定すると良くならず、より悪くなります。

+ +

スタイル設定を制御したい場合は、この機能のカスタムソリューションを作ったり、progressbar.js のようなサードパーティのソリューションを使うのは簡単です。

+ +

よりよいフォームへの道: 役に立つライブラリとポリフィル

+ +

すでにいくつか見てきたように、"劣悪な"コントロールを完全にコントロールしたい場合は、JavaScript に頼るしかありません。カスタムフォームウィジェットをビルドする方法の記事では、独自のフォームウィジェットを作成する方法を見ていきますが、そこには役に立つ便利なライブラリがいくつかあります。

+ + + +

次のライブラリはフォームだけではありませんが、HTML フォームを処理するための非常に興味深い機能を備えています:

+ + + +

CSS と JavaScript には副作用があることに注意してください。したがって、それらのライブラリのいずれかを使用することを選択した場合は、スクリプトが失敗した場合に備えて、堅牢なフォールバック HTML を用意する必要があります。スクリプトが失敗する理由はたくさんあります。特にモバイル環境では、これらのケースを可能な限り最善に処理するようにウェブサイトやアプリケーションを設計する必要があります。

+ +

スキルをテストしましょう!

+ +

この記事の最後に到達しましたが、重要な情報を覚えていますか?次に進む前に、この情報を保持しているか検証するテストがあります — Test your skills: Advanced styling を見てください。心に留めておくこととして、この評価シリーズでの質問は、次の記事の知識も前提していてるので、試す前にその記事に取り組むとよいかもしれません。

+ +

まとめ

+ +

HTML フォームで CSS を使用するのはまだ困難ですが、しばしばそれらを回避する方法があります。クリーンでユニバーサルな解決方法はありませんが、最新のブラウザーでは新しい可能性があります。今のところ、最良の解決策は、HTML フォームウィジェットに適用されたときに異なるブラウザーが CSS をサポートする方法の詳細を学ぶことです。

+ +

このガイドの次の記事では、モダンブラウザーでさまざまな状態のフォームをスタイル設定できるさまざまな UI 疑似クラスを見ていきます。

+ +

{{PreviousMenuNext("Learn/Forms/Styling_web_forms", "Learn/Forms/UI_pseudo-classes", "Learn/Forms")}} 

+ +

このモジュール

+ + + +

Advanced Topics

+ + diff --git a/files/ja/learn/forms/form_validation/index.html b/files/ja/learn/forms/form_validation/index.html new file mode 100644 index 0000000000..ea6887ba21 --- /dev/null +++ b/files/ja/learn/forms/form_validation/index.html @@ -0,0 +1,840 @@ +--- +title: クライアント側のフォームデータ検証 +slug: Learn/Forms/Form_validation +tags: + - HTML + - Intermediate + - JavaScript + - Web + - ウェブ + - ガイド + - フォーム + - フォーム検証 + - 中級者向け + - 例 +translation_of: Learn/Forms/Form_validation +--- +
+
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Forms/UI_pseudo-classes", "Learn/Forms/Sending_and_retrieving_form_data", "Learn/HTML/Forms")}}
+
+ +

データをサーバーへ送信する前に、必須のフォームコントロールが記入され、すべてのフォームコントロールが正しい書式で記入されていることを保証することが重要です。このクライアント側フォーム検証validationは、送信されるデータが様々なフォームコントロールで指定されている要件を満たしていることを保証します。この記事では、クライアント側フォーム検証の基本概念と例を紹介します。

+ + + + + + + + + + + + +
前提知識:コンピューターリテラシー、適度な HTMLCSSJavaScript の理解。
目的:フォーム検証とは何か、なぜ重要なのか、どのように実装するのかを理解すること。
+ +

クライアント側検証は最初のチェックであり、ユーザーの使い勝手を良くするために重要な機能ですクライアント側で不当なデータを捕捉することで、ユーザーはすぐに修正できます。無効なデータがサーバーに送られるて拒否されると、サーバーへ往復してユーザーにデータ修正するよう連絡することによってかなり時間を浪費します。

+ +

しかし、クライアント側の検証はセキュリティ対策とは考えられません!アプリは常にサーバー側でもクライアント側と同様に送信されたデータのセキュリティをチェックします。なぜならクライアント側の検証は容易に回避することができて、悪意のユーザーは簡単に、サーバーへ不正なデータを送信できます。何が起こり得るかは ウェブサイトセキュリティを見てください。サーバー側検証はこのガイドの範囲を超えますが、覚えておいてください。

+ +

フォーム検証とは何か

+ +

有名なサイトの登録フォームに行き、データを求められている書式で入力しなかいと、フィードバックがあることに気づくでしょう。次のようなメッセージが表示されます。

+ + + +

これはフォーム検証と呼ばれます。データを入力したとき、ブラウザー、またはウェブアプリケーションは、そのデータが正しい書式であり、アプリケーションに設定された制約に合っているかをチェックします。ブラウザーで行われる検証はクライアント側検証と、サーバー側で行われるものはサーバー側検証と呼ばれます。この章では、クライアント側検証に集中します。

+ +

情報が正しい書式であれば、アプリケーションはデータのサーバーへの送信を許可し、(ふつうは) データベースに保存されます。正しい書式でなければ、何を修正する必要があるかを説明するメッセージを表示し、ユーザーに再入力させます。

+ +

私たちはできるだけ簡単にフォームを埋めてもらいたいわけですが、なぜフォームを検証する必要があるのでしょうか?理由は主に三つあります。

+ + + +

様々な種類のフォーム検証

+ +

ウェブで見かけるフォーム検証には二つの種類があります。

+ + + +

内蔵フォーム検証の利用

+ +

HTML5 のフォーム制御の機能の一つに JavaScript に頼らない多くのユーザーデータの検証があります。これはフォーム要素の validation属性を使って行います。これまで多くを見てきましたが、まとめ直すと:

+ + + +

入力データをこの指定されたルールに基いて検証します。検証にパスすれば妥当で検証にパスしなければ妥当ではないと考えます。

+ +

要素が妥当な場合は、次のようになります。

+ + + +

要素が不正なときは、次のようになります。

+ + + +
+

注: サーバーへの送信を中断するエラーがいくつかあります。次のものが含まれます: {{domxref('validityState.badInput', 'badInput')}}、{{domxref('validityState.patternMismatch','patternMismatch')}}、{{domxref('validityState.rangeOverflow','rangeOverflow')}} または{{domxref('validityState.rangeUnderflow','underFlow')}}、{{domxref('validityState.stepMismatch','stepMismatch')}}、フォームの制御の {{domxref('validityState.tooLong','tooLong')}} または{{domxref('validityState.tooShort','tooShort')}}、{{domxref('validityState.typeMismatch','typeMismatch')}}、と 必要とされた値の{{domxref('validityState.valueMissing','valueMissing')}}、また {{domxref('validityState.customError','customError')}}も含まれる。

+
+ +

入力要素の制約の検証

+ +

この節では、これまで述べてきたいくつかの属性をテストします。

+ +

簡単な最初のファイル

+ +

簡単な例から始めましょう。― 好きな果物を banana(バナナ)か cherry(サクランボ)から選べる入力欄があるとします。単純なテキストの {{HTMLElement("input")}} とそれに合わせたラベル、送信の {{htmlelement("button")}} から成ります。ソースコードは GitHub の fruit-start.html で、ライブサンプルは次の通りです。

+ +
<form>
+  <label for="choose">banana と cherry のどちらが好き?</label>
+  <input id="choose" name="i_like">
+  <button>Submit</button>
+</form>
+ +
input:invalid {
+  border: 2px dashed red;
+}
+
+input:valid {
+  border: 2px solid black;
+}
+ +

{{EmbedLiveSample("Validation_constraints_on_input_elements", "100%", 80)}}

+ +

始めるにあたって、ハードディスク内の新しいディレクトリーに fruit-start.html のコピーを作成してください。

+ +

required 属性

+ +

required 属性は、使うのがもっとも簡単な HTML5 の検証機能です。入力欄を必須にしたい場合は、この属性を使用して要素をマークすることができます。この属性が設定されていて、要素が {{cssxref(':required')}} にマッチすると、UI疑似クラスとフォームは送信されず、入力が空の場合のエラーメッセージが表示されるでしょう。空のままでは、この入力は不正とみなされ、{{cssxref(':invalid')}} 疑似クラスにマッチします。

+ +

以下のように、required 属性を入力欄に追加しましょう。

+ +
<form>
+  <label for="choose">banana と cherry のどちらが好き? (要入力)</label>
+  <input id="choose" name="i_like" required>
+  <button>Submit</button>
+</form>
+ +

このサンプルファイルの中に含まれている CSS も書いておきましょう。

+ +
input:invalid {
+  border: 2px dashed red;
+}
+
+input:invalid:required {
+  background-image: linear-gradient(to right, pink, lightgreen);
+}
+
+input:valid {
+  border: 2px solid black;
+}
+ +

この CSS によって、入力が妥当でない場合には、入力を赤の破線で境界線を描きますが、入力が妥当な場合には、黒の直線で境界線を描きます。要求された値があり、値が妥当でないときは背景にグラディエーションを追加します。つぎの例の動作を確認しましょう。

+ +

{{EmbedLiveSample("The_required_attribute", "100%", 80)}}

+ +
+

: この例は GitHub の fruit-validation.html で見ることができます (ソースコードも見てください)。

+
+ +

値のないままフォームを送信してみましょう。妥当ではない入力がどのようにフォーカスされるか注意しましょう。デフォルトのエラーメッセージ("Please fill out this field") が表示され、フォームの送信を阻止します。

+ +

required 属性をサポートしている要素にこの属性がある場合、その要素に値があるかないかによって、要素が {{cssxref(':required')}} 疑似クラスに一致するかどうかが決まります。もし {{HTMLElement("input")}} に値がなければ、input は{{cssxref(':invalid')}} 疑似クラスに一致します。

+ +
+

注意: よりよいユーザーエクスペリエンスのために、フォームのフィールドが必要なときにはユーザーに通知しましょう。これはユーザーエクスペリエンスだけに良いというわけではなく、WCAG アクセシビリティ ガイドラインで求められています。また、あなたが本当に必要とする場合にのみ必須にしましょう。例えばあなたは誰かの性別や肩書などの情報は本当に必要でしょうか?

+
+ +

正規表現での検証

+ +

もう一つとてもよく使われる機能は pattern 属性で、値として正規表現を取ります。正規表現 (regex) はテキスト文字列の中の文字の組み合わせに一致させるために使うことができ、フォームの検証には理想的です (JavaScript と同様に様々な利用ができます) 。

+ +

正規表現はかなり複雑です。このモジュールでは正規表現のすべてを説明する意図はありません。いくつかの例を挙げますのでどのように動作するか基本的なアイディアを把握してください。

+ + + +

正規表現には多くの組合せがあるので例はここまでとする。完全な一覧や多くの例は、正規表現ドキュメントを参照してください。

+ +

使用例を実装しましょう。HTML を更新して pattern 属性を追加しましょう:

+ +
<form>
+  <label for="choose">banana と cherry のどちらが好き?</label>
+  <input id="choose" name="i_like" required pattern="[Bb]anana|[Cc]herry">
+  <button>Submit</button>
+</form>
+ +
input:invalid {
+  border: 2px dashed red;
+}
+
+input:valid {
+  border: 2px solid black;
+}
+ +

これは下記の更新があります。次のものを使ってみてください:

+ +

{{EmbedLiveSample("Validating_against_a_regular_expression", "100%", 80)}}

+ +
+

: GitHub の fruit-pattern.html でライブサンプルを見ることができます(ソースコードも見てください)

+
+ +

この例では、{{HTMLElement("input")}} 要素は "banana"、"Banana"、"cherry" または "Cherry" という 4 つの文字列値のうち 1 つを受け付けます。正規表現は大文字小文字を区別しますが、中括弧にはさまれた"Aa"のパターンを使って小文字と同様に先頭が大文字のバージョンをサポートします。

+ +

この時点で、pattern 属性の中の値を以前に見たいくつかの例と同じ値に変更してみて、入力欄が有効になるように入力する値がどのように影響するかを確認してください。自分で考えた値も書いてみて、どのようになるか確認しましょう。果物に関する値を可能にすれば、例が分かりやすくなります。

+ +

もし {{HTMLElement("input")}} の空ではない値が正規表現パターンに一致しなかった場合、この input は {{cssxref(':invalid')}} 疑似クラスに一致します。

+ +
+

メモ: {{HTMLElement("input")}} 要素の型によっては、検証のために pattern 属性が必要ないことがあります。例えば email 型を指定すると、入力された文字列を、妥当な形式のメールアドレスまたは、 multiple 属性がある場合はカンマで区切られたメールアドレスのリストであることを確認する正規表現で検証します。

+
+ +
+

メモ: {{HTMLElement("textarea")}} 要素は pattern 属性に対応していません。

+
+ +

入力欄の長さの制約

+ +

あなたが、{{HTMLElement("input")}} または {{HTMLElement("textarea")}} によって作成してすべてのテキストフィールドで文字数を制限したいときには minlength 属性と maxlength 属性が使用できます。フィールドが値をもっており、その文字数が minlength の値より少ないか、文字数が maxlength の値より大きい場合は、フィールドは不正です。

+ +

ブラウザーはよくテキストフィールドに期待している以上に入力させないことがあります。単に maxlength を使うよりも良いユーザーエクスペリエンスは、入力文字数のフィードバックを提供してサイズ以下でコンテンツを編集できるようにもしておくことです。例えば、Twitter の文字入力の制限があります。これは JavaScript と maxlength を使った解決策の組合せで実現できます。

+ +

入力欄の値に制約を加える

+ +

数値のフィールド (例えば <input type="number">) の場合、min 属性と max 属性によって入力に制限を加えられます。もしそのフィールドの値がこの範囲を超える場合、そのフィールドは妥当ではありません。

+ +

他の例を見てみましょう。fruit-start.html ファイルの新しいコピーを作成してください。

+ +

では、<body> 要素の中身を削除して、以下のように置き換えてください。

+ +
<form>
+  <div>
+    <label for="choose">banana と cherry のどちらが好き?</label>
+    <input type="text" id="choose" name="i_like" required minlength="6" maxlength="6">
+  </div>
+  <div>
+    <label for="number">どのくらい要ります?</label>
+    <input type="number" id="number" name="amount" value="1" min="1" max="10">
+  </div>
+  <div>
+    <button>Submit</button>
+  </div>
+</form>
+ + + +
input:invalid {
+  border: 2px dashed red;
+}
+
+input:valid {
+  border: 2px solid black;
+}
+
+div {
+  margin-bottom: 10px;
+}
+ +

例をライブで実行してみましょう。

+ +

{{EmbedLiveSample("Constraining_the_length_of_your_entries", "100%", 100)}}

+ +
+

: GitHub の fruit-length.html でライブサンプルを見ることができます(ソースコードも見てください)

+
+ +
+

: <input type="number"> (及び rangedate のような他の型)は step 属性を取ることもでき、入力コントロール(数値の増加・減少ボタンなど)を使用するときに上げ下げすることができる値の刻みを設定することができます。上の例では step 属性を入れていませんので、既定値の 1 となります。つまり 3.2 のような浮動小数でも、不正になります。

+
+ +

サンプル全体

+ +

HTML の内蔵検証機能の使い方を示す例の全体を示します。まずは HTML から:

+ +
<form>
+  <p>
+    <fieldset>
+      <legend>Do you have a driver's license?<abbr title="This field is mandatory" aria-label="required">*</abbr></legend>
+      <!-- While only one radio button in a same-named group can be selected at a time,
+           and therefore only one radio button in a same-named group having the "required"
+           attribute suffices in making a selection a requirement -->
+      <input type="radio" required name="driver" id="r1" value="yes"><label for="r1">Yes</label>
+      <input type="radio" required name="driver" id="r2" value="no"><label for="r2">No</label>
+    </fieldset>
+  </p>
+  <p>
+    <label for="n1">How old are you?</label>
+    <!-- pattern 属性は number 型の入力欄を実装していないものの、pattern
+         属性には対応しているブラウザー向けの代替策として動作できます。
+         なお、pattern 属性に対応しているブラウザーでは、number 型の入力欄
+         で使用すると暗黙に失敗します。
+         ここでは代替策としての使い方のみです。-->
+    <input type="number" min="12" max="120" step="1" id="n1" name="age"
+           pattern="\d+">
+  </p>
+  <p>
+    <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory" aria-label="required">*</abbr></label>
+    <input type="text" id="t1" name="fruit" list="l1" required
+           pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
+    <datalist id="l1">
+      <option>Banana</option>
+      <option>Cherry</option>
+      <option>Apple</option>
+      <option>Strawberry</option>
+      <option>Lemon</option>
+      <option>Orange</option>
+    </datalist>
+  </p>
+  <p>
+    <label for="t2">What's your e-mail address?</label>
+    <input type="email" id="t2" name="email">
+  </p>
+  <p>
+    <label for="t3">Leave a short message</label>
+    <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
+  </p>
+  <p>
+    <button>Submit</button>
+  </p>
+</form>
+ +

この HTML をスタイル設定する CSS は:

+ +
form {
+  font: 1em sans-serif;
+  max-width: 320px;
+}
+
+p > label {
+  display: block;
+}
+
+input[type="text"],
+input[type="email"],
+input[type="number"],
+textarea,
+fieldset {
+  width : 100%;
+  border: 1px solid #333;
+  box-sizing: border-box;
+}
+
+input:invalid {
+  box-shadow: 0 0 5px 1px red;
+}
+
+input:focus:invalid {
+  box-shadow: none;
+}
+ +

これで次のようにレンダリングされます。

+ +

{{EmbedLiveSample("Full_example", "100%", 420)}}

+ +

入力値と、それをサポートする入力タイプの制約に使える属性の完全なリストは、検証関連の属性を見てください。

+ +
+

: GitHub の fruit-length.html でライブサンプルを見ることができます(ソースコードも見てください)

+
+ +

JavaScript を使用したフォーム検証

+ +

内蔵のエラーメッセージの見かけを制御したい場合や、HTML5 のフォーム検証に対応していないブラウザーに対処したい場合は、JavaScript を使用する必要があります。この節では、このようにするさまざまな方法を見ていきます。

+ +

HTML5 の制約検証 API

+ +

多くのブラウザーが 制約検証API  に対応しています。この API は各フォーム要素で使用できる一連のメソッドやプロパティで構成されています。

+ + + +

制約検証 API には、上記の要素で利用できる、次のプロパティがあります。

+ + + +

制約検証 API には、上記の要素で利用できる、次のメソッドがあります。

+ + + +

独自のエラーメッセージを実装する

+ +

上記の HTML5 の検証制約の例で見てきたように、ユーザーが不正なフォームを送信しようとするたびにブラウザーはエラーメッセージを表示します。このメッセージを表示する方法は、ブラウザーにより異なります。

+ +

これらの自動のメッセージには、2 つの欠点があります。

+ + + +

Example of an error message with Firefox in French on an English page

+ +

これらのメッセージの外見やテキストを変更するには、制約検証 API の最も一般的なユースケースです。この使用法を例で詳しく見てみましょう。

+ +

いくつかの簡単な HTML で開始します (空の HTML ファイルにこれを入力します; もしよければ、fruit-start.html を基礎としてもいいでしょう):

+ +
<form>
+  <label for="mail">私にメールアドレスを教えてください:</label>
+  <input type="email" id="mail" name="mail">
+  <button>Submit</button>
+</form>
+ +

このページに次の JavaScript を追加します:

+ +
const email = document.getElementById("mail");
+
+email.addEventListener("input", function (event) {
+  if (email.validity.typeMismatch) {
+    email.setCustomValidity("I am expecting an e-mail address!");
+  } else {
+    email.setCustomValidity("");
+  }
+});
+ +

ここでメールアドレス入力への参照を保管して、入力値が変更されるたびに制約コードが走るためのイベントリスナーを追加します。

+ +

制約コードの中で、メールアドレス入力の validity.typeMismatch プロパティが trueかどうか、つまり値がメールアドレスの形式のパターンにマッチしていないかを確認します。その場合、カスタムメッセージとともに setCustomValidity() を呼び出して、フォームを送信するときに、送信が失敗してカスタムエラーメッセージが表示されます。

+ +

validity.typeMismatchfalseの場合、空文字で setCustomValidity() メソッドを呼び出します。これは入力が妥当となり、フォームが送信されます。

+ +

次のもので試すことができます:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/form-validation/custom-error-message.html", '100%', 80)}}

+ +
+

: この例は GitHub の custom-error-message.html で見ることができます (ソースコードも見てください)。

+
+ +

制約検証 API の使用例

+ +

これまでほんとうに簡単な例を見てきましたので、少し複雑な独自の検証を作成するために API を使用する方法を見ていきましょう。

+ +

始めに、HTML です。また、次のものに沿ってみてください:

+ +
<form novalidate>
+  <p>
+    <label for="mail">
+      <span>メールアドレスを入力してください。</span>
+      <input type="email" id="mail" name="mail">
+      <span class="error" aria-live="polite"></span>
+    </label>
+  </p>
+  <button>Submit</button>
+</form>
+ +

この簡単なフォームでは、ブラウザーの自動検証を無効にするために novalidate 属性を使用しています。これで、検証を制御するためにスクリプトを使用できます。ただし、これは制約検証 API の対応や CSS の疑似クラス {{cssxref(":valid")}}, {{cssxref(":invalid")}}, {{cssxref(":in-range")}}, {{cssxref(":out-of-range")}} の適用を無効にするわけではありません。つまり、データを送信する前にブラウザーが自動的なフォームの妥当性確認を行わないとしても、あなた自身で確認を行って、フォームの状態に応じたスタイル設定ができます。

+ +

検証する入力は <input type="email">で、これは required(入力必須)で、8文字の minlength があります。これをわれわれのコードで確認して、それぞれカスタムエラーメッセージを表示させてみましょう。

+ +

<span>要素の中にエラーメッセージを表示させようとしています。 <span>にセットされた aria-live 属性は、スクリーンリーダーのような支援技術を使用している人々を含む皆に、独自のエラーメッセージを提示するようにします。

+ +
+

: ここでの要点は、フォームに novalidate 属性をつけると、フォームがエラーメッセージのバブルを表示するのを停止して、その代わりDOM内にカスタムエラーメッセージを選択した方法で表示させられることです。

+
+ +

この CSS はフォームの見栄えを少し良くして、入力データが無効なときの見た目のフィードバックを提供します。

+ +
/* これはサンプルの見栄えをよくするスタイルです */
+body {
+  font: 1em sans-serif;
+  width: 200px;
+  padding: 0;
+  margin : 0;
+}
+
+p * {
+  display: block;
+}
+
+input[type=email]{
+  -webkit-appearance: none;
+  appearance: none;
+
+  width: 100%;
+  border: 1px solid #333;
+  margin: 0;
+
+  font-family: inherit;
+  font-size: 90%;
+
+  box-sizing: border-box;
+}
+
+/* これは不正なフィールド向けのスタイルです */
+input:invalid{
+  border-color: #900;
+  background-color: #FDD;
+}
+
+input:focus:invalid {
+  outline: none;
+}
+
+/* これはエラーメッセージ向けのスタイルです */
+.error {
+  width  : 100%;
+  padding: 0;
+
+  font-size: 80%;
+  color: white;
+  background-color: #900;
+  border-radius: 0 0 5px 5px;
+
+  box-sizing: border-box;
+}
+
+.error.active {
+  padding: 0.3em;
+}
+ +

以下の JavaScript コードは独自のエラー検証を制御します。

+ +
// DOM ノードの選択法はたくさんあります。ここではフォーム自体、メールアドレス
+// 入力ボックス、そしてエラーメッセージを配置する span 要素を取得しています。
+const form  = document.getElementsByTagName('form')[0];
+
+const email = document.getElementById('mail');
+const error = document.querySelector('#mail + span.error');
+
+email.addEventListener("input", function (event) {
+  // ユーザーが何かを入力するたびに、メールアドレスのフィールドが妥当かを
+  // 確認します。
+  if (email.validity.valid) {
+    // エラーメッセージを表示している場合に、フィールドが妥当になれば
+    // エラーメッセージを取り除きます。
+    error.innerHTML = ""; // メッセージの内容物をリセットします
+    error.className = "error"; // メッセージの表示状態をリセットします
+  } else {
+    // If there is still an error, show the correct error
+    showError();
+  }
+});
+
+form.addEventListener("submit", function (event) {
+  // ユーザーがデータを送信しようとするたびに、メールアドレスの
+  // フィールドが妥当かをチェックします。
+  if (!email.validity.valid) {
+
+    // フィールドが妥当ではない場合、独自のエラーメッセージを
+    // 表示します。
+    showError();
+    // また、イベントをキャンセルしてフォームの送信を止めます。
+    event.preventDefault();
+  }
+});
+
+function showError() {
+  if(email.validity.valueMissing) {
+    // If the field is empty
+    // display the following error message.
+    emailError.textContent = 'You need to enter an e-mail address.';
+  } else if(email.validity.typeMismatch) {
+    // If the field doesn't contain an email address
+    // display the following error message.
+    emailError.textContent = 'Entered value needs to be an e-mail address.';
+  } else if(email.validity.tooShort) {
+    // If the data is too short
+    // display the following error message.
+    emailError.textContent = `Email should be at least ${ email.minLength } characters; you entered ${ email.value.length }.`;
+  }
+
+  // Set the styling appropriately
+  emailError.className = 'error active';
+}
+ +

コメントがよく説明していますが、簡単にいうと:

+ + + +

こちらが実際の結果です。

+ +

{{EmbedGHLiveSample("learning-area/html/forms/form-validation/detailed-custom-validation.html", '100%', 150)}}

+ +
+

: GitHub の detailed-custom-validation.html に例があります(ソースコード見てください)

+
+ +

制約検証 API はフォーム検証を制御するための強力なツールであり、HTML および CSS のみで検証を行うよりもはるかにユーザーインターフェイスをコントロールできます。

+ +
+

: さらなる情報は、制約検証ガイド制約検証 API リファレンスを見てください。

+
+ +

組み込み API を使用しないフォーム検証

+ +

古いブラウザーやカスタムコントロールにおいて、制約検証 API を使用できない (または使用したくない)ことがあるでしょう。このような場合でも、フォームを検証するために JavaScript が使用できます。フォームを検証には、実際のデータの検証よりもユーザーインターフェイスの疑問が多くなります。

+ +

フォームを検証するために、あなたはいくつかの疑問を考えなければなりません。

+ +
+
どのような検証を実施するべきか
+
どのようにデータを検証するかを決めなければなりません。文字列演算、型変換、正規表現など。これはあなた次第です。フォームのデータは常にテキストであり、スクリプトには常に文字列として渡されることを忘れないようにしてください。
+
フォームが妥当でない場合に何をするべきか
+
これは明らかにユーザーインターフェイスの問題です。フォームがどのように動作するかを決めなければなりません。どのような場合でもフォームのデータを送信しますか?エラー状態の入力欄を強調しますか?エラーメッセージを表示しますか?
+
ユーザーが不正なデータを修正することをどのように支援できるか
+
ユーザーの不満を軽減するためには、ユーザーに入力内容の修正を案内するために、できるだけ多くの役立つ情報を提供することがとても重要です。明確なエラーメッセージはもちろん、ユーザーが何を求められているか理解できるように前向きの提案をするべきです。フォーム検証のユーザーインターフェイスの要件について深く知りたいのであれば、ぜひ読むべきである有用な記事があります(英語)。 + +
+
+ +

制約検証 API を使用しない例

+ +

これまでのことを説明するため、古いブラウザーでも動作するように前出のサンプルを作り直してみましょう。

+ +

ご覧の通り、HTML はほとんど同じであり、HTML5 の validation機能を取り除いただけです

+ +
<form>
+  <p>
+    <label for="mail">
+        <span>メールアドレスを入力してください。</span>
+        <input type="text" class="mail" id="mail" name="mail">
+        <span class="error" aria-live="polite"></span>
+    </label>
+  <p>
+  <!-- 一部の古いブラウザーでは button 要素で、type 属性に明示的に
+       submit を設定する必要があります。-->
+  <button type="submit">Submit</button>
+</form>
+ +

同様に、CSS も大きく変更する必要はありません。{{cssxref(":invalid")}} 疑似クラスから実クラスへの変更と、Internet Explorer 6 で動作しない属性セレクターの使用を避けただけです。

+ +
/* これはサンプルの見栄えをよくするスタイルです */
+body {
+  font: 1em sans-serif;
+  padding: 0;
+  margin : 0;
+}
+
+form {
+  max-width: 200px;
+}
+
+p * {
+  display: block;
+}
+
+input.mail {
+  -webkit-appearance: none;
+
+  width: 100%;
+  border: 1px solid #333;
+  margin: 0;
+
+  font-family: inherit;
+  font-size: 90%;
+
+  box-sizing: border-box;
+}
+
+/* これは不正なフィールド向けのスタイルです */
+input.invalid{
+  border-color: #900;
+  background-color: #FDD;
+}
+
+input:focus.invalid {
+  outline: none;
+}
+
+/* これはエラーメッセージ向けのスタイルです */
+.error {
+  width  : 100%;
+  padding: 0;
+
+  font-size: 80%;
+  color: white;
+  background-color: #900;
+  border-radius: 0 0 5px 5px;
+  box-sizing: border-box;
+}
+
+.error.active {
+  padding: 0.3em;
+}
+ +

JavaScript コードでは大きな変更があり、多くの面倒な作業が必要です。

+ +
// 古いブラウザーで DOM ノードを選択する方法は少ない
+const form  = document.getElementsByTagName('form')[0];
+const email = document.getElementById('mail');
+
+// 以下は DOM 内で次の兄弟要素にたどり着くための技です。
+// これは容易に無限ループになることがあるため、危険です。
+// 新しいブラウザーでは、element.nextElementSibling を使用しましょう。
+let error = email;
+while ((error = error.nextSibling).nodeType != 1);
+
+// HTML5 仕様書より
+const emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
+
+// 多くの古いブラウザーは addEventListener メソッドをサポートしていません。
+// 以下はこれを扱うためのシンプルな方法です。なお唯一の方法ではありません。
+function addEvent(element, event, callback) {
+  let previousEventCallBack = element["on"+event];
+  element["on"+event] = function (e) {
+    const output = callback(e);
+
+    // `false` を返すコールバックはコールバックチェーンを止めて、
+    // イベントコールバックの実行を中断します。
+    if (output === false) return false;
+
+    if (typeof previousEventCallBack === 'function') {
+      output = previousEventCallBack(e);
+      if(output === false) return false;
+    }
+  }
+};
+
+// ここから検証制約の再構築ができます。
+// CSS の疑似クラスに頼ることはできないため、メールアドレスフィールドで
+// valid/invalid クラスを明示的に設定しなければなりません。
+addEvent(window, "load", function () {
+  // ここで、フィールドが空かを確認しています (フィールドは必須入力ではありません)
+  // 空でなければ、内容部が適切な電子メールアドレスかを確認します。
+  const test = email.value.length === 0 || emailRegExp.test(email.value);
+
+  email.className = test ? "valid" : "invalid";
+});
+
+// ユーザーがフィールドに入力したときに、何をするかを定義します。
+addEvent(email, "input", function () {
+  const test = email.value.length === 0 || emailRegExp.test(email.value);
+  if (test) {
+    email.className = "valid";
+    error.innerHTML = "";
+    error.className = "error";
+  } else {
+    email.className = "invalid";
+  }
+});
+
+// ユーザーがデータを送信しようとしたときに何をするかを定義します。
+addEvent(form, "submit", function () {
+  const test = email.value.length === 0 || emailRegExp.test(email.value);
+
+  if (!test) {
+    email.className = "invalid";
+    error.innerHTML = "I expect an e-mail, darling!";
+    error.className = "error active";
+
+    // 一部の古いブラウザーは event.reventDefault() メソッドをサポートしていません。
+    return false;
+  } else {
+    email.className = "valid";
+    error.innerHTML = "";
+    error.className = "error";
+  }
+});
+ +

結果は以下のようになります。

+ +

{{EmbedLiveSample("An_example_that_doesnt_use_the_constraint_validation_API", "100%", 130)}}

+ +

ご覧の通り、自分でで検証システムを構築するのは大変なことではありません。難しいのはクロスプラットフォームで、かつ作成するであろうあらゆるフォームで使用できる汎用的なものにすることです。フォーム検証を行うために利用できる、Validate.js のような多くのライブラリがあります。

+ +

まとめ

+ +

クライアント側のフォーム検証は、カスタムスタイル設定やエラーメッセージには複雑な JavaScript を必要としませんが、ユーザーについては注意深く考えることが必要です。ユーザーが正しいデータを入力できるよう支援することを、常に忘れないでください。最後に、以下のことを必ず行ってください。

+ + + +

フォームが正しく埋められたことをチェックしたら、送信することができます。次のデータ送信でカバーします。

+ +

{{PreviousMenuNext("Learn/Forms/UI_pseudo-classes", "Learn/Forms/Sending_and_retrieving_form_data", "Learn/HTML/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/example_1/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_1/index.html new file mode 100644 index 0000000000..1515dc573f --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_1/index.html @@ -0,0 +1,415 @@ +--- +title: 例 1 +slug: Learn/Forms/How_to_build_custom_form_widgets/Example_1 +translation_of: Learn/Forms/How_to_build_custom_form_controls/Example_1 +--- +

これは、カスタムフォームウィジェットの作成方法を説明する最初のコード例です。

+ +

Basic state

+ +

HTML

+ +
<div class="select">
+  <span class="value">Cherry</span>
+  <ul class="optList hidden">
+    <li class="option">Cherry</li>
+    <li class="option">Lemon</li>
+    <li class="option">Banana</li>
+    <li class="option">Strawberry</li>
+    <li class="option">Apple</li>
+  </ul>
+</div>
+ +

CSS

+ +
/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+
+ +

Result for basic state

+ +
{{ EmbedLiveSample('Basic_state', 120, 130) }}
+ +

Active state

+ +

HTML

+ +
<div class="select active">
+  <span class="value">Cherry</span>
+  <ul class="optList hidden">
+    <li class="option">Cherry</li>
+    <li class="option">Lemon</li>
+    <li class="option">Banana</li>
+    <li class="option">Strawberry</li>
+    <li class="option">Apple</li>
+  </ul>
+</div>
+ +

CSS

+ +
/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

Result for active state

+ +
{{ EmbedLiveSample('Active_state', 120, 130) }}
+ +

Open state

+ +

HTML

+ +
<div class="select active">
+  <span class="value">Cherry</span>
+  <ul class="optList">
+    <li class="option highlight">Cherry</li>
+    <li class="option">Lemon</li>
+    <li class="option">Banana</li>
+    <li class="option">Strawberry</li>
+    <li class="option">Apple</li>
+  </ul>
+</div>
+ +

CSS

+ +
/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0, 0, 0, .45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFF;
+}
+ +

Result for open state

+ +
{{ EmbedLiveSample('Open_state', 120, 130) }}
diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/example_2/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_2/index.html new file mode 100644 index 0000000000..7a547909ce --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_2/index.html @@ -0,0 +1,212 @@ +--- +title: 例 2 +slug: Learn/Forms/How_to_build_custom_form_widgets/Example_2 +tags: + - Forms + - HTML +translation_of: Learn/Forms/How_to_build_custom_form_controls/Example_2 +--- +

これは、カスタムフォームウィジェットの作成方法を説明する2番目の例です。

+ +

JS

+ +

HTML コンテンツ

+ +
<form class="no-widget">
+  <select name="myFruit">
+      <option>Cherry</option>
+      <option>Lemon</option>
+      <option>Banana</option>
+      <option>Strawberry</option>
+      <option>Apple</option>
+  </select>
+
+  <div class="select">
+    <span class="value">Cherry</span>
+    <ul class="optList hidden">
+      <li class="option">Cherry</li>
+      <li class="option">Lemon</li>
+      <li class="option">Banana</li>
+      <li class="option">Strawberry</li>
+      <li class="option">Apple</li>
+    </ul>
+  </div>
+<form>
+
+ +

CSS コンテンツ

+ +
.widget select,
+.no-widget .select {
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+
+/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

JavaScript コンテンツ

+ +
window.addEventListener("load", function () {
+  var form = document.querySelector('form');
+
+  form.classList.remove("no-widget");
+  form.classList.add("widget");
+});
+ +

JS の結果

+ +

{{ EmbedLiveSample('JS', 120, 130) }}

+ +

No JS

+ +

HTML コンテンツ

+ +
<form class="no-widget">
+  <select name="myFruit">
+      <option>Cherry</option>
+      <option>Lemon</option>
+      <option>Banana</option>
+      <option>Strawberry</option>
+      <option>Apple</option>
+  </select>
+
+  <div class="select">
+    <span class="value">Cherry</span>
+    <ul class="optList hidden">
+      <li class="option">Cherry</li>
+      <li class="option">Lemon</li>
+      <li class="option">Banana</li>
+      <li class="option">Strawberry</li>
+      <li class="option">Apple</li>
+    </ul>
+  </div>
+<form>
+ +

CSS コンテンツ

+ +
.widget select,
+.no-widget .select {
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+ +

No JS の結果

+ +

{{ EmbedLiveSample('No_JS', 120, 130) }}

diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/example_3/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_3/index.html new file mode 100644 index 0000000000..ac3763cb80 --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_3/index.html @@ -0,0 +1,246 @@ +--- +title: 例 3 +slug: Learn/Forms/How_to_build_custom_form_widgets/Example_3 +tags: + - Forms + - HTML +translation_of: Learn/Forms/How_to_build_custom_form_controls/Example_3 +--- +

これは、カスタムフォームウィジェットの作成方法を説明する3番目の例です。

+ +

状態を変更する

+ +

HTML コンテンツ

+ +
<form class="no-widget">
+  <select name="myFruit" tabindex="-1">
+      <option>Cherry</option>
+      <option>Lemon</option>
+      <option>Banana</option>
+      <option>Strawberry</option>
+      <option>Apple</option>
+  </select>
+
+  <div class="select" tabindex="0">
+    <span class="value">Cherry</span>
+    <ul class="optList hidden">
+      <li class="option">Cherry</li>
+      <li class="option">Lemon</li>
+      <li class="option">Banana</li>
+      <li class="option">Strawberry</li>
+      <li class="option">Apple</li>
+    </ul>
+  </div>
+</form>
+ +

CSS コンテンツ

+ +
.widget select,
+.no-widget .select {
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+
+/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

JavaScript コンテンツ

+ +
// ------- //
+// HELPERS //
+// ------- //
+
+NodeList.prototype.forEach = function (callback) {
+  Array.prototype.forEach.call(this, callback);
+}
+
+// -------------------- //
+// Function definitions //
+// -------------------- //
+
+function deactivateSelect(select) {
+  if (!select.classList.contains('active')) return;
+
+  var optList = select.querySelector('.optList');
+
+  optList.classList.add('hidden');
+  select.classList.remove('active');
+}
+
+function activeSelect(select, selectList) {
+  if (select.classList.contains('active')) return;
+
+  selectList.forEach(deactivateSelect);
+  select.classList.add('active');
+};
+
+function toggleOptList(select, show) {
+  var optList = select.querySelector('.optList');
+
+  optList.classList.toggle('hidden');
+}
+
+function highlightOption(select, option) {
+  var optionList = select.querySelectorAll('.option');
+
+  optionList.forEach(function (other) {
+    other.classList.remove('highlight');
+  });
+
+  option.classList.add('highlight');
+};
+
+// ------------- //
+// Event binding //
+// ------------- //
+
+window.addEventListener("load", function () {
+  var form = document.querySelector('form');
+
+  form.classList.remove("no-widget");
+  form.classList.add("widget");
+});
+
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  selectList.forEach(function (select) {
+    var optionList = select.querySelectorAll('.option');
+
+    optionList.forEach(function (option) {
+      option.addEventListener('mouseover', function () {
+        highlightOption(select, option);
+      });
+    });
+
+    select.addEventListener('click', function (event) {
+      toggleOptList(select);
+    },  false);
+
+    select.addEventListener('focus', function (event) {
+      activeSelect(select, selectList);
+    });
+
+    select.addEventListener('blur', function (event) {
+      deactivateSelect(select);
+    });
+  });
+});
+ +

結果

+ +

{{ EmbedLiveSample('Change_states') }}

diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/example_4/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_4/index.html new file mode 100644 index 0000000000..51fa53c27a --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_4/index.html @@ -0,0 +1,296 @@ +--- +title: 例 4 +slug: Learn/Forms/How_to_build_custom_form_widgets/Example_4 +tags: + - Advanced + - Example + - Forms + - Guide + - HTML + - Web +translation_of: Learn/Forms/How_to_build_custom_form_controls/Example_4 +--- +

これは、カスタムフォームウィジェットの作成方法を説明する4番目の例です。

+ +

状態を変更する

+ +

HTML コンテンツ

+ +
<form class="no-widget">
+  <select name="myFruit">
+    <option>Cherry</option>
+    <option>Lemon</option>
+    <option>Banana</option>
+    <option>Strawberry</option>
+    <option>Apple</option>
+  </select>
+
+  <div class="select">
+    <span class="value">Cherry</span>
+    <ul class="optList hidden">
+      <li class="option">Cherry</li>
+      <li class="option">Lemon</li>
+      <li class="option">Banana</li>
+      <li class="option">Strawberry</li>
+      <li class="option">Apple</li>
+    </ul>
+  </div>
+</form>
+ +

CSS コンテンツ

+ +
.widget select,
+.no-widget .select {
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+
+/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

JavaScript コンテンツ

+ +
// ------- //
+// HELPERS //
+// ------- //
+
+NodeList.prototype.forEach = function (callback) {
+  Array.prototype.forEach.call(this, callback);
+}
+
+// -------------------- //
+// Function definitions //
+// -------------------- //
+
+function deactivateSelect(select) {
+  if (!select.classList.contains('active')) return;
+
+  var optList = select.querySelector('.optList');
+
+  optList.classList.add('hidden');
+  select.classList.remove('active');
+}
+
+function activeSelect(select, selectList) {
+  if (select.classList.contains('active')) return;
+
+  selectList.forEach(deactivateSelect);
+  select.classList.add('active');
+};
+
+function toggleOptList(select, show) {
+  var optList = select.querySelector('.optList');
+
+  optList.classList.toggle('hidden');
+}
+
+function highlightOption(select, option) {
+  var optionList = select.querySelectorAll('.option');
+
+  optionList.forEach(function (other) {
+    other.classList.remove('highlight');
+  });
+
+  option.classList.add('highlight');
+};
+
+function updateValue(select, index) {
+  var nativeWidget = select.previousElementSibling;
+  var value = select.querySelector('.value');
+  var optionList = select.querySelectorAll('.option');
+
+  nativeWidget.selectedIndex = index;
+  value.innerHTML = optionList[index].innerHTML;
+  highlightOption(select, optionList[index]);
+};
+
+function getIndex(select) {
+  var nativeWidget = select.previousElementSibling;
+
+  return nativeWidget.selectedIndex;
+};
+
+// ------------- //
+// Event binding //
+// ------------- //
+
+window.addEventListener("load", function () {
+  var form = document.querySelector('form');
+
+  form.classList.remove("no-widget");
+  form.classList.add("widget");
+});
+
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  selectList.forEach(function (select) {
+    var optionList = select.querySelectorAll('.option');
+
+    optionList.forEach(function (option) {
+      option.addEventListener('mouseover', function () {
+        highlightOption(select, option);
+      });
+    });
+
+    select.addEventListener('click', function (event) {
+      toggleOptList(select);
+    });
+
+    select.addEventListener('focus', function (event) {
+      activeSelect(select, selectList);
+    });
+
+    select.addEventListener('blur', function (event) {
+      deactivateSelect(select);
+    });
+  });
+});
+
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  selectList.forEach(function (select) {
+    var optionList = select.querySelectorAll('.option'),
+        selectedIndex = getIndex(select);
+
+    select.tabIndex = 0;
+    select.previousElementSibling.tabIndex = -1;
+
+    updateValue(select, selectedIndex);
+
+    optionList.forEach(function (option, index) {
+      option.addEventListener('click', function (event) {
+        updateValue(select, index);
+      });
+    });
+
+    select.addEventListener('keyup', function (event) {
+      var length = optionList.length,
+          index  = getIndex(select);
+
+      if (event.keyCode === 40 && index < length - 1) { index++; }
+      if (event.keyCode === 38 && index > 0) { index--; }
+
+      updateValue(select, index);
+    });
+  });
+});
+ +

結果

+ +

{{ EmbedLiveSample('Change_states') }}

diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/example_5/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_5/index.html new file mode 100644 index 0000000000..4bad8016bb --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/example_5/index.html @@ -0,0 +1,289 @@ +--- +title: 例 5 +slug: Learn/Forms/How_to_build_custom_form_widgets/Example_5 +tags: + - Forms + - HTML +translation_of: Learn/Forms/How_to_build_custom_form_controls/Example_5 +--- +

これが、カスタムフォームウィジェットの作成方法を説明する最後の例です。

+ +

状態を変更する

+ +

HTML コンテンツ

+ +
<form class="no-widget">
+  <select name="myFruit">
+    <option>Cherry</option>
+    <option>Lemon</option>
+    <option>Banana</option>
+    <option>Strawberry</option>
+    <option>Apple</option>
+  </select>
+
+  <div class="select" role="listbox">
+    <span class="value">Cherry</span>
+    <ul class="optList hidden" role="presentation">
+      <li class="option" role="option" aria-selected="true">Cherry</li>
+      <li class="option" role="option">Lemon</li>
+      <li class="option" role="option">Banana</li>
+      <li class="option" role="option">Strawberry</li>
+      <li class="option" role="option">Apple</li>
+    </ul>
+  </div>
+</form>
+ +

CSS コンテンツ

+ +
.widget select,
+.no-widget .select {
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+
+/* --------------- */
+/* Required Styles */
+/* --------------- */
+
+.select {
+  position: relative;
+  display : inline-block;
+}
+
+.select.active,
+.select:focus {
+  box-shadow: 0 0 3px 1px #227755;
+  outline: none;
+}
+
+.select .optList {
+  position: absolute;
+  top     : 100%;
+  left    : 0;
+}
+
+.select .optList.hidden {
+  max-height: 0;
+  visibility: hidden;
+}
+
+/* ------------ */
+/* Fancy Styles */
+/* ------------ */
+
+.select {
+  font-size   : 0.625em; /* 10px */
+  font-family : Verdana, Arial, sans-serif;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  padding : 0.1em 2.5em 0.2em 0.5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : 0.2em solid #000; /* 2px */
+  border-radius : 0.4em; /* 4px */
+
+  box-shadow : 0 0.1em 0.2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  background : #F0F0F0;
+  background : -webkit-linear-gradient(90deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+
+  white-space   : nowrap;
+  text-overflow : ellipsis;
+  vertical-align: top;
+}
+
+.select:after {
+  content : "▼";
+  position: absolute;
+  z-index : 1;
+  height  : 100%;
+  width   : 2em; /* 20px */
+  top     : 0;
+  right   : 0;
+
+  padding-top : .1em;
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  text-align : center;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+}
+
+.select .optList {
+  z-index : 2;
+
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  background: #f0f0f0;
+  border: .2em solid #000;
+  border-top-width : .1em;
+  border-radius: 0 0 .4em .4em;
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4);
+
+  -moz-box-sizing : border-box;
+  box-sizing : border-box;
+
+  min-width : 100%;
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.select .option {
+  padding: .2em .3em;
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

JavaScript コンテンツ

+ +
// ------- //
+// HELPERS //
+// ------- //
+
+NodeList.prototype.forEach = function (callback) {
+  Array.prototype.forEach.call(this, callback);
+}
+
+// -------------------- //
+// Function definitions //
+// -------------------- //
+
+function deactivateSelect(select) {
+  if (!select.classList.contains('active')) return;
+
+  var optList = select.querySelector('.optList');
+
+  optList.classList.add('hidden');
+  select.classList.remove('active');
+}
+
+function activeSelect(select, selectList) {
+  if (select.classList.contains('active')) return;
+
+  selectList.forEach(deactivateSelect);
+  select.classList.add('active');
+};
+
+function toggleOptList(select, show) {
+  var optList = select.querySelector('.optList');
+
+  optList.classList.toggle('hidden');
+}
+
+function highlightOption(select, option) {
+  var optionList = select.querySelectorAll('.option');
+
+  optionList.forEach(function (other) {
+    other.classList.remove('highlight');
+  });
+
+  option.classList.add('highlight');
+};
+
+function updateValue(select, index) {
+  var nativeWidget = select.previousElementSibling;
+  var value = select.querySelector('.value');
+  var optionList = select.querySelectorAll('.option');
+
+  optionList.forEach(function (other) {
+    other.setAttribute('aria-selected', 'false');
+  });
+
+  optionList[index].setAttribute('aria-selected', 'true');
+
+  nativeWidget.selectedIndex = index;
+  value.innerHTML = optionList[index].innerHTML;
+  highlightOption(select, optionList[index]);
+};
+
+function getIndex(select) {
+  var nativeWidget = select.previousElementSibling;
+
+  return nativeWidget.selectedIndex;
+};
+
+// ------------- //
+// Event binding //
+// ------------- //
+
+window.addEventListener("load", function () {
+  var form = document.querySelector('form');
+
+  form.classList.remove("no-widget");
+  form.classList.add("widget");
+});
+
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  selectList.forEach(function (select) {
+    var optionList = select.querySelectorAll('.option'),
+        selectedIndex = getIndex(select);
+
+    select.tabIndex = 0;
+    select.previousElementSibling.tabIndex = -1;
+
+    updateValue(select, selectedIndex);
+
+    optionList.forEach(function (option, index) {
+      option.addEventListener('mouseover', function () {
+        highlightOption(select, option);
+      });
+
+      option.addEventListener('click', function (event) {
+        updateValue(select, index);
+      });
+    });
+
+    select.addEventListener('click', function (event) {
+      toggleOptList(select);
+    });
+
+    select.addEventListener('focus', function (event) {
+      activeSelect(select, selectList);
+    });
+
+    select.addEventListener('blur', function (event) {
+      deactivateSelect(select);
+    });
+
+    select.addEventListener('keyup', function (event) {
+      var length = optionList.length,
+          index  = getIndex(select);
+
+      if (event.keyCode === 40 && index < length - 1) { index++; }
+      if (event.keyCode === 38 && index > 0) { index--; }
+
+      updateValue(select, index);
+    });
+  });
+});
+
+ +

結果

+ +

{{ EmbedLiveSample('Change_states') }}

diff --git a/files/ja/learn/forms/how_to_build_custom_form_widgets/index.html b/files/ja/learn/forms/how_to_build_custom_form_widgets/index.html new file mode 100644 index 0000000000..7bbd20b511 --- /dev/null +++ b/files/ja/learn/forms/how_to_build_custom_form_widgets/index.html @@ -0,0 +1,901 @@ +--- +title: カスタムフォームコントロールの作成方法 +slug: Learn/Forms/How_to_build_custom_form_widgets +tags: + - Advanced + - Example + - Forms + - Guide + - HTML + - Web +translation_of: Learn/Forms/How_to_build_custom_form_controls +--- +
{{LearnSidebar}}
+ +

HTML フォームで使用可能なコントロールだけでは十分でない場合が多くあります。例えば、{{HTMLElement("select")}} 要素のようなコントロールに高度なスタイル設定を行いたい場合や、独自の動作を提供したい場合は、あなた独自のコントロールを作成するしかありません。

+ +

本記事では、そのようなコントロールの作り方を見ていきます。その目的のため、次の例に取り組みます: {{HTMLElement("select")}} 要素の再構築です。このほかに独自コントロール作成をどういう方法でするか、いつするか、またそれが意味をなすのかや、コントロール作成が必須なときに何に気をつけるべきかを扱います。

+ +
+

注記: ここではコントロールの構築に注目しており、汎用かつ再利用可能なコードの作成法は見ていきません。それには JavaScript の重要なコードや未知のコンテキストでの DOM 操作が組み合わされており、本記事の対象から外れます。

+
+ +

デザイン、構造、セマンティクス

+ +

カスタムコントロールを作成する前に、何をしたいかをはっきりと理解することから始めるべきです。これはあなたの貴重な時間を節約するでしょう。特に、コントロールの全状態を明確に定義することが重要です。これを行うには、状態や動作がよく知られている既存のコントロールからスタートするとよいでしょう。この結果、簡単に可能な限りの模倣を行えます。

+ +

本記事の例では、{{HTMLElement("select")}} 要素を再構築します。以下が、私たちが実現したい成果です:

+ +

The three states of a select box

+ +

このスクリーンショットでは、コントロールの主要な状態 3 つを示しています: 通常状態 (左)、アクティブ状態 (中央)、そして開いた状態 (右) です。

+ +

動作の点では、ネイティブな HTML用要素を再作成します。このため、ネイティブ HTML要素と同様な動作や意味を持たせるべきです。独自のコントロールもネイティブコントロールと同様に、キーボードだけでなくマウスでも使用できるように、またスクリーンリーダーに理解できるようにしたいと考えます。コントロールがどのように各状態に達するかを定義することから始めましょう:

+ +
+
コントロールは以下のときに通常状態になります:
+
+
    +
  • ページを読み込む
  • +
  • コントロールはアクティブであったが、ユーザーがコントロール以外のどこかをクリックした
  • +
  • コントロールはアクティブであったが、キーボードを使用して別のコントロールにフォーカスを移した (例 Tab キー)
  • +
+
+
コントロールは以下のときにアクティブ状態になります:
+
+
    +
  • ユーザーがコントロール上でクリックする
  • +
  • ユーザーが Tab キーを押下して、コントロールがフォーカスを得る
  • +
  • コントロールが開いた状態で、ユーザーがコントロールをクリックする
  • +
+
+
コントロールは以下のときに開いた状態になります:
+
+
    +
  • コントロールが開いた状態ではないときに、ユーザーがコントロールをクリックした
  • +
+
+
+ +

状態をどのように変えるかを理解したら、コントロールの値をどのように変えるかの定義が重要になります:

+ +
+
以下のときに値が変わります:
+
+
    +
  • コントロールが開いた状態であるときに、ユーザーが選択肢をクリックする
  • +
  • コントロールがアクティブ状態であるときに、ユーザーが上下矢印キーを押下する
  • +
+
+
+ +
+
以下のときには値は変わりません:
+
+
    +
  • 最初のオプションが選択済みのときに、ユーザーが上矢印をクリックする
  • +
  • 最後のオプションが選択済みのときに、ユーザーが下矢印をクリックする
  • +
+
+
+ +

最後に、コントロールの選択肢がどのように動作するかを定義しましょう:

+ + + +

この例の用途としては、ここまでです。しかし注意深い読者の方は、いくつかの動作が欠けていることに気づくでしょう。例えば、コントロールが開いた状態であるときにユーザーが Tab キーを押すと何が起きると考えますか? その答えは... 何も起きません。正しい動作は明らかでしょうが、実際は私たちの仕様で定義されていないため、とても見逃されやすいのです。これは、コントロールの動作を設計する人と実装する人が異なるチーム環境で特に当てはまります。

+ +

別のおもしろい例です: コントロールが開いた状態であるときに上下矢印キーを押すと何が起きるのでしょうか? こちらはやや難しくなります。アクティブ状態と開いた状態をまったく別のものと考えるなら、その答えはやはり "何も起きません" です。これは、開いた状態でのキーボードの作用を定義していないためです。一方、アクティブ状態と開いた状態が少し重なると考えるなら、値は替わるかもしれませんがそれに対応して選択肢が強調されることはないでしょう。繰り返しになりますが、これはコントロールが開いた状態の選択肢に対するキーボードの作用を定義していないためです (コントロールが開いた状態で何が起きるかだけを定義しており、その後がないためです)。

+ +

もう少し突っ込んで考えてみます: エスケープキーはどうでしょう? Esc キーを押すと開いた select が閉じます。ネイティブの{{htmlelement('select')}}と同じ機能を提供する場合、キーボードやマウスやスクリーンリーダーへのタッチ、その他あらゆる入力デバイスまで、全てのユーザーにとっての select の動作と全く同じようにふるまうべきです。

+ +

この例では欠けている仕様が明らかですので対処するでしょうが、めずらしい新たなコントロールでは真の問題になり得ます。標準要素では、{{htmlelement('select')}} もその 1 つですが、仕様の作成者は膨大な時間をかけて全てのユースケースの全ての入力デバイスの全ての操作を指定します。新コントロールの作成は簡単ではなく、特にそれが作成されたことのないものの場合は、どのような動作が正しいかについて、わずかなアイデアですら誰も持っていないため簡単ではないです。少なくとも select はこれまでやってきたため、どうふるまうかはわかっています!

+ +

一般的に、新しい操作を設計するのは、標準を作成するに十分なリーチを持った、とても大きな産業プレイヤーだけの選択肢です。例えば、Apple は 2001年に iPod にスクロールホイールを導入しました。完全に新しい操作方法のデバイスを導入するのに成功するマーケットシェアがありましたが、たいていのデバイス会社はそうはいきません。

+ +

新しいユーザーインタラクションを発明しないのがベストです。インタラクションを追加する場合、設計段階で時間を使うのが重要です。動作の定義が貧弱であったり定義もれがあったりした場合、いったんユーザーが使い始めると動作を再定義するのが非常に困難になると思われますので、設計段階に時間をかけることは賢明です。もし疑っているのでしたら、他の人に意見を聞きましょう。また予算を持っているのでしたら、ユーザーテストの実施をためらってはいけません。このプロセスは、UX デザインと呼ばれます。この点について詳しく学びたいのでしたら、以下の役に立つリソースをご覧になるとよいでしょう:

+ + + +
+

注記: さらにほとんどのシステムでは、使用できる選択肢すべてを見るために {{HTMLElement("select")}} 要素を開く手段があります (これは {{HTMLElement("select")}} 要素をマウスでクリックするのと同じです)。これは Windows では Alt + Down  キー で実現できますが、この例では実装しません。しかし、仕組みはすでに click イベント向けに実装されていますので、行うのは簡単です。

+
+ +

HTML の構造とセマンティクスの定義

+ +

コントロールの基本的な機能が決まりましたので、構築を始めるときが来ました。最初のステップはコントロールの HTML 構造の定義と、基本的なセマンティクスの付与です。こちらが、{{HTMLElement("select")}} 要素の再構築に必要な HTML です:

+ +
<!-- これはコントロールの中心的なコンテナです。
+     tabindex 属性は、ユーザーがコントロールにフォーカスを当てられるようにするものです。
+     これを JavaScript で設定する方がよいことは、後で見ていきます。-->
+<div class="select" tabindex="0">
+
+  <!-- このコンテナは、コントロールの現在の値を表示するために使用します。-->
+  <span class="value">Cherry</span>
+
+  <!-- このコンテナは、コントロールで使用できるすべての選択肢を包含します。
+       これはリストですから、ul 要素を使用するとよいでしょう。-->
+  <ul class="optList">
+    <!-- 各々の選択肢は表示される値だけを包含しており、フォームのデータで送信される
+         実際の値を処理する方法は後で見ていきます。-->
+    <li class="option">Cherry</li>
+    <li class="option">Lemon</li>
+    <li class="option">Banana</li>
+    <li class="option">Strawberry</li>
+    <li class="option">Apple</li>
+  </ul>
+
+</div>
+ +

クラス名の使い方に注目してください。これらは基盤となる実際の HTML とは関係なく、フォームに関するそれぞれの部分を示します。これは CSS や JavaScript を強固な HTML の構造と結びつけないようにするために重要であり、そのためにコントロールを扱うコードを壊すことなく、後から実装を変更することができます。例えば {{HTMLElement("optgroup")}} 要素と同等の機能を実装したい場合などです。

+ +

クラス名は、しかしながら、意味のある値ではありません。現在の状態では、スクリーンリーダーのユーザーのみがリストを"見る"ことができます。ARIA セマンティクスを少し追加します。

+ +

CSS でルックアンドフィールを作成する

+ +

構造ができましたので、コントロールのデザインを始められます。カスタムコントロールを作成する上でのポイントは、望むとおりにコントロールへスタイルを設定できることです。そのために、CSS を 2 つの部分に分けます: ひとつはコントロールが {{HTMLElement("select")}} 要素のように動作するために欠かせない CSS ルールであり、もうひとつは希望する見た目にするための好みのスタイルで構成されます。

+ +

必須のスタイル

+ +

必須のスタイルは、コントロールの 3 つの状態を扱うために欠かせないものです。

+ +
.select {
+  /* 選択肢のリスト向けのポジショニングコンテキストを作成します;
+     adding this to .select{{cssxref(':focus-within')}} will be a better option when fully supported
+  */
+  position: relative;
+
+  /* コントロールをテキストフローの一部かつまとまった大きさにします */
+  display : inline-block;
+}
+ +

アクティブ状態であるコントロールのルックアンドフィールを定義するため、追加で active クラスが必要です。このコントロールはフォーカスを得ることができますので、同様に動作させるためにカスタムスタイルを {{cssxref(":focus")}} 疑似クラスにも適用します。

+ +
.select.active,
+.select:focus {
+  outline: none;
+
+  /* box-shadow プロパティは必須ではありませんが、これをデフォルト値として使用するのは
+     アクティブ状態を見えるようにするために重要です。自由に書き換えてください。*/
+  box-shadow: 0 0 3px 1px #227755;
+}
+ +

次に、選択肢のリストを扱いましょう:

+ +
/* .select セレクタは、私たちが定義するクラスがコントロールの内部にあることを
+   確実にするためのシンタックスシュガーです。*/
+.select .optList {
+  /* 選択肢のリストが値の下部かつ HTML フローの外側に表示される
+     ようにします。*/
+  position : absolute;
+  top      : 100%;
+  left     : 0;
+}
+ +

選択肢のリストが隠れている状態を扱うための追加クラスも必要です。これはアクティブ状態と開いた状態で完全には一致しない相違点を扱うために必要です。

+ +
.select .optList.hidden {
+  /* これはアクセシブルな方法でリストを隠すための簡単な方法です。
+     アクセシビリティについては最後に説明します。*/
+  max-height: 0;
+  visibility: hidden;
+}
+ +
+

: 選択肢のリストに高さと幅を与えないように transform: scale(1, 0) も使えます。

+
+ +

美化

+ +

基本的な機能性を適切に置きましたので、戯れを始められます。以下は何ができるかの例であり、本記事の冒頭で示したスクリーンショットに一致するものです。とはいえ、自由に実験して何ができるかを見てみるとよいでしょう。

+ +
.select {
+  /* アクセシビリティのため、すべてのサイズは em 単位の値で表します
+     (ユーザーがテキストのみのモードでブラウザーのズーム機能を使用したときに、
+     コントロールをリサイズ可能にします)。算出結果は、ほとんどのブラウザーで
+     デフォルト値である 1em == 16px を想定します。
+     px から em への変換がわからない場合は http://riddle.pl/emcalc/ を試してください */
+  font-size   : 0.625em; /* この値 (10px) は、本コンテキストにおける新たなフォントサイズの em 単位値です。*/
+  font-family : Verdana, Arial, sans-serif;
+
+  box-sizing : border-box;
+
+  /* 後で追加する下向き矢印のためのスペースが必要です */
+  padding : .1em 2.5em .2em .5em; /* 1px 25px 2px 5px */
+  width   : 10em; /* 100px */
+
+  border        : .2em solid #000; /* 2px */
+  border-radius : .4em; /* 4px */
+  box-shadow    : 0 .1em .2em rgba(0,0,0,.45); /* 0 1px 2px */
+
+  /* 最初の宣言は、線形グラデーションをサポートしないブラウザー向けのものです。*/
+  background : #F0F0F0;
+  background : linear-gradient(0deg, #E3E3E3, #fcfcfc 50%, #f0f0f0);
+}
+
+.select .value {
+  /* 値がコントロールの幅より大きくなる可能性があるため、コントロールの幅を
+     変更しないようにすることが必要です。内容物がオーバーフローした場合は、
+     省略記号をつけるとよいでしょう。*/
+  display  : inline-block;
+  width    : 100%;
+  overflow : hidden;
+  white-space : nowrap;
+  text-overflow: ellipsis;
+  vertical-align: top;
+}
+ +

下向き矢印をデザインするための追加要素は不要です。代わりに {{cssxref(":after")}} 疑似要素を使用します。ただし、select クラスでシンプルな背景画像を使用することによる実装も可能です。

+ +
.select:after {
+  content : "▼"; /* Unicode 文字 U+25BC を使用します。http://www.utf8-chartable.de をご覧ください */
+  position: absolute;
+  z-index : 1; /* これは、矢印が選択肢のリストに重ならないようにするために重要です */
+  top     : 0;
+  right   : 0;
+
+  box-sizing : border-box;
+
+  height  : 100%;
+  width   : 2em;
+  padding-top : .1em;
+
+  border-left  : .2em solid #000;
+  border-radius: 0 .1em .1em 0;
+
+  background-color : #000;
+  color : #FFF;
+  text-align : center;
+}
+ +

次に、選択肢のリストにスタイルを設定しましょう:

+ +
.select .optList {
+  z-index : 2; /* 選択肢のリストが下向き矢印より上になるよう、明示的に示します。*/
+
+  /* ul 要素のデフォルトスタイルを初期化します。*/
+  list-style: none;
+  margin : 0;
+  padding: 0;
+
+  box-sizing : border-box;
+
+  /* 値の幅がコントロールの幅より小さい場合でも、選択肢のリストの幅が
+     コントロール自体と同じになるようにします。*/
+  min-width : 100%;
+
+  /* リストが長すぎる場合に、内容物が垂直方向にはみ出します (自動的に
+     垂直スクロールバーを表示します) が、水平方向にはみ出しません
+     (幅を指定しないため、リストは自身の幅へ自動的に調整されます。
+     それができない場合は、内容物が切り詰められます) 。*/
+  max-height: 10em; /* 100px */
+  overflow-y: auto;
+  overflow-x: hidden;
+
+  border: .2em solid #000; /* 2px */
+  border-top-width : .1em; /* 1px */
+  border-radius: 0 0 .4em .4em; /* 0 0 4px 4px */
+
+  box-shadow: 0 .2em .4em rgba(0,0,0,.4); /* 0 2px 4px */
+  background: #f0f0f0;
+}
+ +

選択肢向けに、ユーザーが選択しようとしている (あるいは選択した) 値を示せるようにするための highlight クラスを追加しなければなりません。

+ +
.select .option {
+  padding: .2em .3em; /* 2px 3px */
+}
+
+.select .highlight {
+  background: #000;
+  color: #FFFFFF;
+}
+ +

これで、3 つの状態の結果は以下のようになります:

+ + + + + + + + + + + + + + + + + + + +
通常状態アクティブ状態開いた状態
{{EmbedLiveSample("Basic_state",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_1")}}{{EmbedLiveSample("Active_state",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_1")}}{{EmbedLiveSample("Open_state",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_1")}}
ソースコードを確認する
+ +

JavaScript でコントロールに命を吹き込む

+ +

デザインや構造の準備ができましたので、コントロールが実際に動作するようにするための JavaScript コードを記述できます。

+ +
+

警告: 以下は教育目的のコードであり、そのままで使用するべきではありません。ご覧のとおり、さまざまな箇所に将来性のないものや古いブラウザーで動作しないものがあります。また、本番のコードでは最適化すべき冗長な箇所もあります。

+
+ +

なぜ動作しないのか?

+ +

始める前に、重要事項を覚えておくことが大切です: ブラウザー内の JavaScript は信頼できる技術ではありません。カスタムコントロールを作成するとき、すべてをつなぎ合わせるために必要であることから JavaScript に頼らなければならないでしょう。ところが、JavaScript をブラウザーで実行できない場合がいくつもあります:

+ + + +

このようなリスクがあるため、JavaScript が動作しない場合に何が起きるかを注意深く考えることが重要です。この問題について詳しく扱うのは、スクリプトをどれだけ汎用および再利用可能にしたいかと密接に関わりますので本記事の範囲を外れますが、本サンプルでは基本的な部分について考えていきます。

+ +

本記事の例では、JavaScript コードが実行されない場合に標準の {{HTMLElement("select")}} 要素にフォールバックします。われわれのコントロールと {{HTMLElement("select")}}を導入し、いずれが表示されるかは body 要素のり class に依存します。読み込みに成功したとき、body 要素の class がコントロールの関数をなす script により更新されます。

+ +

これを実現するには、2 つのことが必要です。

+ +

第一に、カスタムコントロールを使用する前に通常の {{HTMLElement("select")}} 要素を追加することが必要です。実際は、これは残りのフォームデータと共にカスタムコントロールのデータを送信できるようにするために必要です。詳しくは後述します。

+ +
<body class="no-widget">
+  <form>
+    <select name="myFruit">
+      <option>Cherry</option>
+      <option>Lemon</option>
+      <option>Banana</option>
+      <option>Strawberry</option>
+      <option>Apple</option>
+    </select>
+
+    <div class="select">
+      <span class="value">Cherry</span>
+      <ul class="optList hidden">
+        <li class="option">Cherry</li>
+        <li class="option">Lemon</li>
+        <li class="option">Banana</li>
+        <li class="option">Strawberry</li>
+        <li class="option">Apple</li>
+      </ul>
+    </div>
+  </form>
+
+</body>
+ +

第二に、不要な要素 (すなわち、スクリプトを実行する場合における "本物の" {{HTMLElement("select")}} 要素や、実行しない場合におけるカスタムコントロール) を隠せるようにするための新たなクラスが 2 つ必要です。デフォルトでは、HTML コードでカスタムコントロールを隠すことに注意してください。

+ +
.widget select,
+.no-widget .select {
+  /* この CSS セレクタの基本的な意味は:
+     - body のクラスを "widget" に設定して、本物の {{HTMLElement("select")}} 要素を隠す
+     - または body のクラスを変更せずに "no-widget" のままにしておくことで、
+       クラスが "select" である要素が隠される */
+  position : absolute;
+  left     : -5000em;
+  height   : 0;
+  overflow : hidden;
+}
+ +

この CSS は要素の 1 つを見えなくしますが、スクリーンリーダーからは利用できます。

+ +

ここで、スクリプトを実行するか否かを判断するための JavaScript スイッチが必要になります。このスイッチはとても簡単です: ページを読み込むときにスクリプトを実行したら、no-widget クラスを削除して widget クラスを追加します。これにより {{HTMLElement("select")}} 要素やカスタムコントロールの可視性を切り替えます。

+ +
window.addEventListener("load", function () {
+  document.body.classList.remove("no-widget");
+  document.body.classList.add("widget");
+});
+ + + + + + + + + + + + + + + + + +
JS なしJS あり
{{EmbedLiveSample("No_JS",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_2")}}{{EmbedLiveSample("JS",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_2")}}
ソースコードを確認する
+ +
+

注記: コードを本当に汎用かつ再利用可能にしたい場合はクラスを切り替えるのではなく、単に {{HTMLElement("select")}} 要素を隠すためのコントロールのクラスを追加して、ページ内にあるすべての {{HTMLElement("select")}} 要素の後ろにカスタムコントロールを表す DOM ツリーを動的に追加する方がはるかによいでしょう。

+
+ +

作業をより簡単に

+ +

作成しようとしているコードでは、必要な作業すべてのために標準の JavaScript と DOM API を使用するでしょう。ここで使用するつもりである機能は以下のとおりです:

+ +
    +
  1. {{domxref("element.classList","classList")}}
  2. +
  3. {{domxref("EventTarget.addEventListener","addEventListener")}}
  4. +
  5. forEach
  6. +
  7. {{domxref("element.querySelector","querySelector")}} および {{domxref("element.querySelectorAll","querySelectorAll")}}
  8. +
+ +

これら特定機能を利用できるかに加えて、作業を始める前に残されている問題があります。{{domxref("element.querySelectorAll","querySelectorAll()")}} 関数が返すオブジェクトは Array ではなく {{domxref("NodeList")}} です。これは、Array オブジェクトは forEach 関数をサポートしているが {{domxref("NodeList")}} はサポートしていないために重要な問題です。{{domxref("NodeList")}} は Array ととても似ており、また forEach はとても便利であることから、作業を楽にするため以下のように {{domxref("NodeList")}} で forEach をサポートさせることができます:

+ +
NodeList.prototype.forEach = function (callback) {
+  Array.prototype.forEach.call(this, callback);
+}
+ +

古いブラウザーをサポートする必要がある場合、そのブラウザーがこうした機能をサポートしているか確かめてください。そうでない場合、リスト内を繰り返すか、ライブラリーや pollyfill を使う必要があります。

+ +

イベントコールバックを作成する

+ +

基盤が整いましたので、ユーザーがコントロールと対話するたびに使用されるすべての関数を定義し始めることができます。

+ +
// この関数は、カスタムコントロールを非アクティブにしたいときに使用します。
+// 引数は 1 つあります。
+// select : 非アクティブにする `select` クラスの DOM ノード
+function deactivateSelect(select) {
+
+  // コントロールがアクティブではないときは何もしません。
+  if (!select.classList.contains('active')) return;
+
+  // カスタムコントロールの選択肢のリストを取得することが必要です。
+  var optList = select.querySelector('.optList');
+
+  // 選択肢のリストを閉じます。
+  optList.classList.add('hidden');
+
+  // そして、カスタムコントロール自身を非アクティブにします。
+  select.classList.remove('active');
+}
+
+// この関数は、ユーザーがコントロールをアクティブ/非アクティブにしたがっているときに使用します。
+// 引数は 2 つあります:
+// select : アクティブにする `select` クラスの DOM ノード
+// selectList : `select` クラスであるすべての DOM ノードのリスト
+function activeSelect(select, selectList) {
+
+  // コントロールがすでにアクティブであるときは何もしません。
+  if (select.classList.contains('active')) return;
+
+  // すべてのカスタムコントロールを非アクティブにすることが必要です。
+  // deactivateSelect 関数は forEach コールバック関数の要件を
+  // すべて満たしていますので、仲介する無名関数を使用せずに
+  // 直接使用しています。
+  selectList.forEach(deactivateSelect);
+
+  // そして、指定されたコントロールをアクティブ状態にします。
+  select.classList.add('active');
+}
+
+// この関数は、ユーザーが選択肢のリストを開く/閉じることを求めたときに使用します。
+// 引数は 1 つあります:
+// select : 表示を切り替えるリストの DOM ノード
+function toggleOptList(select) {
+
+  // リストはコントロールから確保します。
+  var optList = select.querySelector('.optList');
+
+  // リストのクラスを表示/非表示に切り替えます。
+  optList.classList.toggle('hidden');
+}
+
+// この関数は、選択肢を強調したいときに使用します。
+// 引数は 2 つあります:
+// select : 強調する選択肢を包含する `select` クラスの DOM ノード
+// option : 強調する `option` クラスの DOM ノード
+function highlightOption(select, option) {
+
+  // カスタムコントロールで使用可能なすべての選択肢のリストを取得します。
+  var optionList = select.querySelectorAll('.option');
+
+  // すべての選択肢から強調効果を取り除きます。
+  optionList.forEach(function (other) {
+    other.classList.remove('highlight');
+  });
+
+  // 適切な選択肢を強調します。
+  option.classList.add('highlight');
+};
+ +

以上が、カスタムコントロールのさまざまな状態を制御するために必要なもののすべてです。

+ +

次に、これらの関数と適切なイベントを関連づけます:

+ +
// ドキュメントが読み込まれたときのイベントの関連づけを制御します。
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  // 各々のコントロールは初期化が必要です。
+  selectList.forEach(function (select) {
+
+    // すべての `option` も同様です。
+    var optionList = select.querySelectorAll('.option');
+
+    // ユーザーが選択肢にマウスポインタを乗せるたびに、その選択肢を強調します。
+    optionList.forEach(function (option) {
+      option.addEventListener('mouseover', function () {
+        // 注記: 変数 `select` および `option` は、関数呼び出しのスコープ内でのみ
+        // 使用可能なクロージャです。
+        highlightOption(select, option);
+      });
+    });
+
+    // ユーザーが独自の select 要素でクリックするたびに
+    select.addEventListener('click', function (event) {
+      // 注記: 変数 `select` は、関数呼び出しのスコープ内でのみ
+      // 使用可能なクロージャです。
+
+      // 選択肢のリストの可視性を切り替えます。
+      toggleOptList(select);
+    });
+
+    // コントロールが再びフォーカスを得た場合
+    // ユーザーがコントロールをクリックしたり、コントロールへアクセスするために
+    // Tab キーを使用するたびに、コントロールはフォーカスを得ます。
+    select.addEventListener('focus', function (event) {
+      // 注記: 変数 `select` および `selectList` は、関数呼び出しのスコープ内でのみ
+      // 使用可能なクロージャです。
+
+      // コントロールをアクティブにします。
+      activeSelect(select, selectList);
+    });
+
+    // コントロールがフォーカスを失った場合
+    select.addEventListener('blur', function (event) {
+      // 注記: 変数 `select` は、関数呼び出しのスコープ内でのみ
+      // 使用可能なクロージャです。
+
+      // コントロールを非アクティブにします。
+      deactivateSelect(select);
+    });
+
+    // ユーザーが`esc`を押した場合にフォーカスを外す
+    select.addEventListener('keyup', function (event) {
+
+      // deactive on keyup of `esc`
+      if (event.keyCode === 27) {
+         deactivateSelect(select);
+      }
+    });
+});
+ +

この時点でコントロールは設計どおりに状態が変わりますが、コントロール値はまだ更新されません。次の章でこれを扱います。

+ + + + + + + + + + + + + + + +
Live example
{{EmbedLiveSample("Change_states",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_3")}}
ソースコードを確認する
+ +

コントロールの値を制御する

+ +

コントロールが動作するようになりましたので、ユーザーの入力に従って値を更新して、フォームデータと共にその値を送信できるようにするコードを追加しなければなりません。

+ +

これを行うもっとも簡単な方法は、覆い隠したネイティブコントロールを使用することです。そのようなコントロールはブラウザーが提供するすべての組み込みのコントロールと共に値の経過を保持しており、フォームを送信するときは通常どおりに値を送信します。これらすべてを行えるようにするために、車輪の再発明を行うのは無駄です。

+ +

先ほど見たように、アクセシビリティの理由からフォールバック手段としてすでにネイティブの select コントロールを使用しています。単純に、その値をカスタムコントロールの値と同期することができます:

+ +
// この関数は、表示される値を更新してネイティブコントロールの値と同期します。
+// 引数は 2 つあります:
+// select : 更新する値を持つ `select` クラスの DOM ノード
+// index  : 選択される値のインデックス
+function updateValue(select, index) {
+  // 指定されたカスタムコントロール向けのネイティブコントロールを取得することが必要です。
+  // この例では、ネイティブコントロールはカスタムコントロールの兄弟です。
+  var nativeWidget = select.previousElementSibling;
+
+  // カスタムコントロールの値のプレースホルダーの取得も必要です。
+  var value = select.querySelector('.value');
+
+  // そして、選択肢の全リストが必要です。
+  var optionList = select.querySelectorAll('.option');
+
+  // 選択した値のインデックスを、selectedIndex に設定します。
+  nativeWidget.selectedIndex = index;
+
+  // 上記に応じて、値のプレースホルダーも更新します。
+  value.innerHTML = optionList[index].innerHTML;
+
+  // そして、カスタムコントロールで対応する選択肢を強調します。
+  highlightOption(select, optionList[index]);
+};
+
+// この関数は、ネイティブコントロールで現在選択されているインデックスを返します。
+// 引数は 1 つあります:
+// select : ネイティブコントロールに関係する `select` クラスの DOM ノード
+function getIndex(select) {
+  // 指定されたカスタムコントロール向けのネイティブコントロールにアクセスすることが必要です。
+  // この例では、ネイティブコントロールはカスタムコントロールの兄弟です。
+  var nativeWidget = select.previousElementSibling;
+
+  return nativeWidget.selectedIndex;
+};
+ +

これら 2 つの関数で、ネイティブコントロールとカスタムコントロールを関連づけることができます:

+ +
// ドキュメントが読み込まれたときのイベントの関連づけを制御します。
+window.addEventListener('load', function () {
+  var selectList = document.querySelectorAll('.select');
+
+  // 各々のコントロールは初期化が必要です。
+  selectList.forEach(function (select) {
+    var optionList = select.querySelectorAll('.option'),
+        selectedIndex = getIndex(select);
+
+    // カスタムコントロールがフォーカスを得られるようにします。
+    select.tabIndex = 0;
+
+    // ネイティブコントロールがフォーカスを得ないようにします。
+    select.previousElementSibling.tabIndex = -1;
+
+    // デフォルトで選択されている値が正しく表示されるようにします。
+    updateValue(select, selectedIndex);
+
+    // ユーザーが選択肢をクリックするのに応じて値を更新します。
+    optionList.forEach(function (option, index) {
+      option.addEventListener('click', function (event) {
+        updateValue(select, index);
+      });
+    });
+
+    // フォーカスがあるコントロールでユーザーがキーボードを使用するのに応じて、値を更新します。
+    select.addEventListener('keyup', function (event) {
+      var length = optionList.length,
+          index  = getIndex(select);
+
+      // ユーザーが下矢印キーを押すと、次の選択肢にジャンプします。
+      if (event.keyCode === 40 && index < length - 1) { index++; }
+
+      // ユーザーが上矢印キーを押すと、前の選択肢にジャンプします。
+      if (event.keyCode === 38 && index > 0) { index--; }
+
+      updateValue(select, index);
+    });
+  });
+});
+ +

上記のコードで、tabIndex プロパティを使用していることは注目に値します。このプロパティは、ネイティブコントロールにフォーカスが当たらないようにすることと、ユーザーがキーボードやマウスを使用するとカスタムコントロールがフォーカスを得るようにするために必要です。

+ +

これで完了です! 結果は以下のとおりです:

+ + + + + + + + + + + + + + + +
Live example
{{EmbedLiveSample("Change_states",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_4")}}
ソースコードを確認する
+ +

ちょっと待ってください、本当に終わったのでしょうか?

+ +

アクセシブルにする

+ +

フル機能のセレクトボックスとはかけ離れていますが動作するものはできましたし、よく動作しています。しかし、私たちが行ってきたことは DOM の操作にすぎません。これには実際のセマンティクスがなく、またセレクトボックスのように見えていてもブラウザーの視点からはそうではないため、支援技術はそれがセレクトボックスであるとは理解できません。つまり、このきれいなセレクトボックスはアクセシブルではありません!

+ +

幸いなことに解決策があり、それは ARIA と呼ばれます。ARIA は "Accessible Rich Internet Application" を表し、その W3C 仕様 は私たちがここで行っていることに特化して設計されています: ウェブアプリケーションやカスタムコントロールをアクセシブルにします。これは基本的には、私たちが作り出した要素がネイティブコントロールとして通るかのように、役割や状態や特性をより説明できるようにするために HTML を拡張する属性のセットです。これらの属性の使用はとても簡単ですので、行ってみましょう。

+ +

role 属性

+ +

ARIA で使用される主要な属性が、role 属性です。role 属性は、要素を何に使用するかを定義する値を受け入れます。それぞれのロールは、自身の要件や動作を定義します。本記事の例では、ロール listbox を使用します。これは "composite role" であり、このロールの要素は子要素を持ち、またそれぞれの子要素も特定のロールを持ちます (この例では、ロール option の子要素が少なくとも 1 つ)。

+ +

また、ARIA は標準の HTML マークアップにデフォルトで適用されるロールを定義することも特筆に値します。例えば、{{HTMLElement("table")}} 要素はロール grid に、{{HTMLElement("ul")}} 要素はロール list にマッチします。{{HTMLElement("ul")}} 要素を使用しているため、私たちのコントロールのロール listbox が、{{HTMLElement("ul")}} 要素のロール list を置き換えるようにしなければなりません。そのために、ロール presentation を使用します。このロールは要素に特別な意味はないことを示せるようにするためのものであり、単に情報を与えるために使用されます。これを {{HTMLElement("ul")}} 要素に適用します。

+ +

ロール listbox をサポートするため、HTML を以下のように更新することが必要です:

+ +
<!-- 最初の要素に role="listbox" 属性を追加します -->
+<div class="select" role="listbox">
+  <span class="value">Cherry</span>
+  <!-- ul 要素に role="presentation" を追加します -->
+  <ul class="optList" role="presentation">
+    <!-- すべての li 要素に role="option" 属性を追加します -->
+    <li role="option" class="option">Cherry</li>
+    <li role="option" class="option">Lemon</li>
+    <li role="option" class="option">Banana</li>
+    <li role="option" class="option">Strawberry</li>
+    <li role="option" class="option">Apple</li>
+  </ul>
+</div>
+ +
+

注記: role 属性と class 属性の両方を含める方法は、CSS 属性セレクタに対応しない古いブラウザーをサポートしたい場合にのみ必要です。

+
+ +

aria-selected 属性

+ +

role を使用するだけでは不十分です。ARIA は、状態や特性を表す多くの属性も提供します。これらをより多くまた適切に使用すると、コントロールが支援技術にもっと良く理解されるようになります。ここでは、使用する属性を 1 つに絞ります: aria-selected です。

+ +

aria-selected 属性は、どの選択肢が現在選択されているかを示すために使用します。これにより、支援技術はユーザーに現在何が選択されているかを伝えることができます。ここではユーザーが選択肢を選択するたびに、選択された選択肢を示すためにこの属性を JavaScript で動的に使用します。このために、updateValue() 関数の変更が必要です:

+ +
function updateValue(select, index) {
+  var nativeWidget = select.previousElementSibling;
+  var value = select.querySelector('.value');
+  var optionList = select.querySelectorAll('.option');
+
+  // すべての選択肢が選択されていないようにします。
+  optionList.forEach(function (other) {
+    other.setAttribute('aria-selected', 'false');
+  });
+
+  // 指定された選択肢が選択されているようにします。
+  optionList[index].setAttribute('aria-selected', 'true');
+
+  nativeWidget.selectedIndex = index;
+  value.innerHTML = optionList[index].innerHTML;
+  highlightOption(select, optionList[index]);
+};
+ +

スクリーンリーダーにオフスクリーンselectに焦点をあてて他のスタイルを無視するようにした法が簡単に見えますが、これはアクセシブルな解決策ではありません。スクリーンリーダーは盲目の人だけのものではありません。低視力や、完全な視力の人もこれを使います。このため、スクリーンリーダーをオフスクリーン要素だけに焦点をあてるようにはできません。

+ +

以下がこれらの変更を施した最終結果です (NVDAVoiceOver などの支援技術でコントロールを使用してみても、よい感触を得られるでしょう):

+ + + + + + + + + + + + + + + +
Live example
{{EmbedLiveSample("Change_states",120,130, "", "HTML/Forms/How_to_build_custom_form_controls/Example_5")}}
ソースコードを確認する
+ +

もっと先に進むには、この例でのコードは汎用性や再利用性に改善が必要です。これは課題として挑戦できます。この2つのヒントを挙げると:すべての関数で最初の引数は同じで、つまりこれらの関数は同じコンテキストが必要です。そのコンテキストを共有するオブジェクトを作るのが賢明です。

+ +

他の方法: ラジオボタンを使う

+ +

上の例では、非動的なHTML、CSS、JavaScript を使って {{htmlelement('select')}} 要素を再発明しました。このselectは限られた選択肢から1つが選択され、それは {{htmlelement('input/radio', 'radio')}} ボタンのグループと同様な機能です。

+ +

このため、代わりにラジオボタンを使って再発明できます。このオプションを見てみましょう。

+ +

We can start with

+ +

完全に意味のある、アクセシブルで、順序のない、関連する{{htmlelement('label')}}つきの{{htmlelement('input/radio','radio')}} ボタンのリストから始めます、グループ全体を適切な意味のある{{htmlelement('fieldset')}} と{{htmlelement('legend')}} のペアにラベルづけします。.

+ +
 <fieldset>
+  <legend>Pick a fruit</legend>
+    <ul class="styledSelect">
+      <li><input type="radio" name="fruit" value="Cherry" id="fruitCherry" checked><label for="fruitCherry">Cherry</label></li>
+      <li><input type="radio" name="fruit" value="Lemon" id="fruitLemon"><label for="fruitLemon">Lemon</label></li>
+      <li><input type="radio" name="fruit" value="Banana" id="fruitBanana"><label for="fruitBanana"">Banana</label></li>
+      <li><input type="radio" name="fruit" value="Strawberry" id="fruitStrawberry"><label for="fruitStrawberry">Strawberry</label></li>
+      <li><input type="radio" name="fruit" value="Apple" id="fruitApple"><label for="fruitApple">Apple</label></li>
+    </ul>
+  </fieldset>
+ +

(legend/fieldsetではなく)ラジオボタンリストに少しスタイルづけをして、前の例と同じ見た目にし、完了したことがわかるようにします:

+ +
.styledSelect {
+  display: inline-block;
+  padding: 0;
+}
+.styledSelect li {
+  list-style-type: none;
+  padding: 0;
+  display: flex;
+}
+.styledSelect [type=radio] {
+  position: absolute;
+  left: -100vw;
+  top: -100vh;
+}
+.styledSelect label {
+  margin: 0;
+  line-height: 2;
+  padding: 0 0 0 4px;
+}
+.styledSelect:not(:focus-within) input:not(:checked) + label {
+  height: 0;
+  outline: none;
+  overflow: hidden;
+}
+.styledSelect:not(:focus-within) input:checked + label {
+  border: .2em solid #000;
+  border-radius: .4em;
+  box-shadow: 0 .1em .2em rgba(0,0,0,.45);
+}
+.styledSelect:not(:focus-within) input:checked + label::after {
+  content : "▼";
+  background: black;
+  float: right;
+  color: white;
+  padding: 0 4px;
+  margin: 0 -4px 0 4px;
+}
+.styledSelect:focus-within {
+  border: .2em solid #000;
+  border-radius: .4em;
+  box-shadow: 0 .1em .2em rgba(0,0,0,.45);
+}
+.styledSelect:focus-within input:checked + label {
+  background-color: #333;
+  color: #fff;
+  width: 100%;
+}
+ +

JavaScriptなしで少しの CSSにて、ラジオボタンのリストをスタイルづけしてチェック済み項目のみを表示することができます。フォーカスが<fieldset>内の <ul> に来ると、リストは開いて、上下左右の矢印が前後の項目を選択するのに使えます。次で試してください:

+ +

{{EmbedLiveSample("An_alternative_approach_Using_radio_buttons",200,240)}}

+ +

これはある程度、JavaScriptなしで動作します。JavaScript が失敗しても動作する、われわれのカスタムコントロールど同じものを作ってきました。よい解決策でしょう?これはキーボードでは動作しますが、マウスクリックではそうなりません。ネイティブな意味づけのない要素を作るフレームワークに依存する代わりに、ウェブ標準をカスタムコントロールの基礎として使った方が意味があります。しかし、われわれのコントロールは <select> が自ずと持つ機能と同じものを備えていません。

+ +

いい面として、このコントロールはスクリーンリーダーにとって完全にアクセシブルでキーボードで完全に操作できます。しかし、このコントロールは {{htmlelement('select')}} 要素の置き換えではありません。異なる機能や足りない機能があります。例えば、4つの矢印は選択肢を操作できますが、最後のボタンで下を押すと最初のボタンに移動します。<select> のように上端、下端で止まりません。

+ +

この足りない機能の追加は、読者の課題としておきます。

+ +

まとめ

+ +

独自のフォームコントロールの作成方法を見てきましたが、ご覧いただいたようにこれは容易なことではありません。独自のカスタムコントロールを作る前に、HTMLに要求を十分に満たす代替要素がないかを検討してください。本当にカスタムコントロールを作成する必要がある場合、サードパーティのライブラリに頼るほうが簡単かつよいことも少なくありません。独自作成する場合、既存の要素を編集するか、準備されたコントロールを実装するフレームワークを使うようにして、実用的でアクセシブルなフォームコントロールの作成は見た目より複雑であることを忘れないでください。

+ +

自分でコーディングする前に検討するとよいライブラリをいくつか紹介します:

+ + + +

ラジオボタン、独自JavaScript 、またはサードパーティライブラリで代替コントロールを作る場合、アクセシブルかつ機能への耐性を高めましょう。すなわち Web 標準の実装状況がまちまちである、多様なブラウザーで良好に動作できるようにすることが必要です。楽しんでください!

+ +

このモジュール

+ +

学習コース

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/how_to_structure_an_html_form/example/index.html b/files/ja/learn/forms/how_to_structure_an_html_form/example/index.html new file mode 100644 index 0000000000..2c97485087 --- /dev/null +++ b/files/ja/learn/forms/how_to_structure_an_html_form/example/index.html @@ -0,0 +1,164 @@ +--- +title: '例: お支払いフォーム' +slug: Learn/Forms/How_to_structure_an_HTML_form/Example +translation_of: Learn/Forms/How_to_structure_a_web_form/Example +--- +

これは記事 HTML フォームの構築方法の基本的なお支払いフォームの例です。

+ +

お支払いフォーム

+ +

HTML コンテンツ

+ +
<form>
+        <h1>Payment form</h1>
+        <p>Required fields are followed by <strong><abbr title="required">*</abbr></strong>.</p>
+        <section>
+            <h2>Contact information</h2>
+            <fieldset>
+              <legend>Title</legend>
+              <ul>
+                  <li>
+                    <label for="title_1">
+                      <input type="radio" id="title_1" name="title" value="M." >
+                      Mister
+                    </label>
+                  </li>
+                  <li>
+                    <label for="title_2">
+                      <input type="radio" id="title_2" name="title" value="Ms.">
+                      Miss
+                    </label>
+                  </li>
+              </ul>
+            </fieldset>
+            <p>
+              <label for="name">
+                <span>Name: </span>
+                <strong><abbr title="required">*</abbr></strong>
+              </label>
+              <input type="text" id="name" name="username">
+            </p>
+            <p>
+              <label for="mail">
+                <span>E-mail: </span>
+                <strong><abbr title="required">*</abbr></strong>
+              </label>
+              <input type="email" id="mail" name="usermail">
+            </p>
+            <p>
+              <label for="password">
+                <span>Password: </span>
+                <strong><abbr title="required">*</abbr></strong>
+              </label>
+              <input type="password" id="pwd" name="password">
+            </p>
+        </section>
+        <section>
+            <h2>Payment information</h2>
+            <p>
+              <label for="card">
+                <span>Card type:</span>
+              </label>
+              <select id="card" name="usercard">
+                <option value="visa">Visa</option>
+                <option value="mc">Mastercard</option>
+                <option value="amex">American Express</option>
+              </select>
+            </p>
+            <p>
+              <label for="number">
+                <span>Card number:</span>
+                <strong><abbr title="required">*</abbr></strong>
+              </label>
+                <input type="number" id="number" name="cardnumber">
+            </p>
+            <p>
+              <label for="date">
+                <span>Expiration date:</span>
+                <strong><abbr title="required">*</abbr></strong>
+                <em>formatted as mm/yy</em>
+              </label>
+              <input type="date" id="date" name="expiration">
+            </p>
+        </section>
+        <section>
+            <p> <button type="submit">Validate the payment</button> </p>
+        </section>
+    </form>
+ +

CSS コンテンツ

+ +
      h1 {
+          margin-top: 0;
+      }
+
+      ul {
+          margin: 0;
+          padding: 0;
+          list-style: none;
+      }
+
+      form {
+          margin: 0 auto;
+          width: 400px;
+          padding: 1em;
+          border: 1px solid #CCC;
+          border-radius: 1em;
+      }
+
+      div+div {
+          margin-top: 1em;
+      }
+
+      label span {
+          display: inline-block;
+          width: 120px;
+          text-align: right;
+      }
+
+      input, textarea {
+          font: 1em sans-serif;
+          width: 250px;
+          box-sizing: border-box;
+          border: 1px solid #999;
+      }
+
+      input[type=checkbox], input[type=radio] {
+          width: auto;
+          border: none;
+      }
+
+      input:focus, textarea:focus {
+          border-color: #000;
+      }
+
+      textarea {
+          vertical-align: top;
+          height: 5em;
+          resize: vertical;
+      }
+
+      fieldset {
+          width: 250px;
+          box-sizing: border-box;
+          margin-left: 136px;
+          border: 1px solid #999;
+      }
+
+      button {
+          margin: 20px 0 0 124px;
+      }
+
+      label {
+        position: relative;
+      }
+
+      label em {
+        position: absolute;
+        right: 5px;
+        top: 20px;
+      }
+ +

結果

+ +

{{ EmbedLiveSample('A_payment_form', '100%', 620) }}

diff --git a/files/ja/learn/forms/how_to_structure_an_html_form/index.html b/files/ja/learn/forms/how_to_structure_an_html_form/index.html new file mode 100644 index 0000000000..ca3865643f --- /dev/null +++ b/files/ja/learn/forms/how_to_structure_an_html_form/index.html @@ -0,0 +1,329 @@ +--- +title: フォームの構築方法 +slug: Learn/Forms/How_to_structure_an_HTML_form +tags: + - CodingScripting + - HTML + - Web + - ガイド + - フォーム + - 例 + - 初心者 + - 学習 + - 構造 +translation_of: Learn/Forms/How_to_structure_a_web_form +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Forms/Your_first_form", "Learn/Forms/Basic_native_form_controls", "Learn/Forms")}}
+ +

基本から外れて、ここでは色々なフォームのパーツを構造化し、意味をつけるのに使われる要素を詳しく見ていきます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーと、HTML の基本的な理解
目的:HTML フォームを構造化して意味を与えて使いやすくアクセシブルにする方法を理解すること。
+ +

フォームは柔軟性があるため、HTML で最も複雑な構造の 1 つとなっています。専用のフォーム要素と属性を使用して、あらゆる種類の基本フォームを作成できます。HTML フォームを構築するときに正しい構造を使用すると、フォームが使用可能でアクセスしやすいことを確実にするのに役立ちます。

+ +

<form> 要素

+ +

{{HTMLElement("form")}} 要素はフォームを正式に定義するとともに、自身の属性でフォームの動作を定義します。HTML フォームを作成しようとするたびに、この要素から始めて、すべてのコンテンツをその中に入れなければなりません。多くの支援技術やブラウザープラグインは {{HTMLElement("form")}} 要素を検出でき、またフォームを使いやすくするための特別なフックを実装できます。

+ +

前の記事ですでにこれを見ています。

+ +
警告: フォームの中にフォームを入れ子にすることは厳格に禁じられています。これは予期せぬ動作を発生するおそれがあるので、悪い方針です。
+ +

{{HTMLElement("form")}} 要素の外部でもフォームウィジェットを使用できますが、そのフォームウィジェットは form 属性を用いて関連付けなければ、どのフォームに対しても何も行わないことに注意してください。これは、実際には {{ HTMLElement("form") }} に包含されていない要素であっても明示的にフォームへ紐付けることを可能にします。

+ +

  次に、フォームに含まれる構造的な要素を見ていきましょう。

+ +

<fieldset> および <legend> 要素

+ +

{{HTMLElement("fieldset")}} 要素は、スタイルや意味付けのために、同じ目的を持つウィジェットのグループの作成に便利です。{{HTMLElement("fieldset")}} 要素は、<fieldset> タグのすぐ下に {{HTMLElement("legend")}} 要素を入れてラベルを付与できます。{{HTMLElement("legend")}} 要素は、{{HTMLElement("fieldset")}} 要素の目的を正式に説明します。

+ +

多くの支援技術は {{HTMLElement("legend")}} 要素を、対応する {{HTMLElement("fieldset")}} 要素内にある各ウィジェットのラベルの一部であるかのように扱うでしょう。例えば JawsNVDA といったスクリーンリーダーは、各ウィジェットのラベルを読み上げる前に legend の内容を読み上げます。

+ +

以下に小さなサンプルを挙げます:

+ +
<form>
+  <fieldset>
+    <legend>Fruit juice size</legend>
+    <p>
+      <input type="radio" name="size" id="size_1" value="small">
+      <label for="size_1">Small</label>
+    </p>
+    <p>
+      <input type="radio" name="size" id="size_2" value="medium">
+      <label for="size_2">Medium</label>
+    </p>
+    <p>
+      <input type="radio" name="size" id="size_3" value="large">
+      <label for="size_3">Large</label>
+    </p>
+  </fieldset>
+</form>
+ +
+

: この例は fieldset-legend.html で見ることができます(ライブ版も見てください)。

+
+ +

この例では、スクリーンリーダーは最初のウィジェットを "Fruit juice size small"、2 番目を "Fruit juice size medium"、3 番目を "Fruit juice size large" と読み上げるでしょう。

+ +

このサンプルでのユースケースは、もっとも重要なことのひとつです。ラジオボタンのセットを置くたびに、それらを {{HTMLElement("fieldset")}} 要素内へ入れ子にするようにしましょう。ユースケースは他にもあり、通常は {{HTMLElement("fieldset")}} 要素でフォームを明確に区分するために使用できます。理想的には長いフォームは複数ページに分けるべきですが、フォームが長くなっても1つのページに収めないといけない場合、別々の関連セクションを別々の fieldset に入れることは使いやすさを改善します。

+ +

支援技術への影響力により、{{HTMLElement("fieldset")}} 要素はアクセシブルなフォームを作成するために重要な要素のひとつです。しかし、それを誤用しないようにするのはあなたの責務です。できれば、フォームを作成するたびにスクリーンリーダーがどのように解釈するか聞いてみましょう。変に聞こえるのであれば、フォームの構造を改善するためのよいヒントになります。

+ +

<label> 要素

+ +

これまでの記事で見てきたように、{{HTMLElement("label")}} 要素は、HTML フォームウィジェットのラベルを定義する正式な方法です。これは、アクセシブルなフォームを作成したい場合にもっとも重要な要素です — 適切に実装された時は、スクリーンリーダーはフォーム要素のラベルと関連する指示を一緒に読み上げます。前の記事で見てきたこの例を見てみます:

+ +
<label for="name">Name:</label> <input type="text" id="name" name="user_name">
+ +

<label><input> とがそれぞれ forid 属性により正し関連付けられると (label の for 属性は対応するウィジェットの id 属性を参照します)、スクリーンリーダーは "Name, edit text"のように読み上げます。

+ +

フォームコントロールとラベルを関連付けるもう 1 つの方法は、フォームコントロールを <label> の中でネストすることで、暗黙的に関連付けることです。

+ +
<label for="name">
+  Name: <input type="text" id="name" name="user_name">
+</label>
+ +

この場合でも for 属性を設定することがベストプラクティスと考えられています。これは、ラベルとウィジェットの暗黙的な関係を理解できない支援技術があるためです。

+ +

ラベルがなかったり、フォームコントロールが明示的/暗黙にラベルに関連付けられていない場合、スクリーンリーダーは全く役立たない "Edit text blank" のような読み上げを行います。

+ +

ラベルもクリック可能です!

+ +

ラベルをセットアップするもう 1 つの利点は、ユーザーがラベルをクリックするとウィジェットをアクティブにすることが、あらゆるブラウザーで可能になります。これは例えば、テキスト入力で、入力と同様にラベルをクリックしてフォーカスさせることができますし、ラジオボタンやチェックボックスで特に有用です — このコントロールのヒットエリアはとても小さく、できるだけ大きくしておくのは便利です。

+ +

例えば、次の例で "I like cherry" の テキストをクリックすると選択された taste_cherry チェックボックスの状態が切り替わります:

+ +
<form>
+  <p>
+    <input type="checkbox" id="taste_1" name="taste_cherry" value="cherry">
+    <label for="taste_1">I like cherry</label>
+  </p>
+  <p>
+    <input type="checkbox" id="taste_2" name="taste_banana" value="banana">
+    <label for="taste_2">I like banana</label>
+  </p>
+</form>
+ +
+

: この例は checkbox-label.html で見ることができます(ライブ版も見てください)。

+
+ +

複数のラベル

+ +

厳密に言うと、1 つのウィジェット内に複数のラベルを入れることができますが、複数のラベルを持つウィジェットの扱いに問題がある支援技術があるかもしれません。複数のラベルがある場合、アクセシブルなフォームを作成するには1つの {{htmlelement("label")}} 要素内にウィジェットを入れ子にするとよいでしょう。

+ +

以下のサンプルについて考えてみましょう:

+ +
<p>Required fields are followed by <abbr title="required">*</abbr>.</p>
+
+<!-- 2 つの例をご覧ください: -->
+<div>
+  <label for="username">Name:</label>
+  <input id="username" type="text" name="username">
+  <label for="username"><abbr title="required" aria-label="required">*</abbr></label>
+</div>
+
+<!-- 前出の例よりは良いです: -->
+<div>
+  <label for="username">
+    <span>Name: </span>
+    <input id="username" type="text" name="username">
+    <abbr title="required" aria-label="required">*</abbr>
+  </label>
+</div>
+
+<!-- これが最も良いでしょう: -->
+<div>
+  <label for="username">Name: <abbr title="required" aria-label="required">*</abbr></label>
+  <input id="username" type="text" name="username">
+</div>
+ +

{{EmbedLiveSample("Multiple_labels", 120, 120)}}

+ +

このサンプルでは、最初の段落で入力必須の要素の規則を定義しています。ユーザーが入力必須の要素を見つける前にスクリーンリーダーのような支援技術が注意事項を表示したり読み上げたりするためには、規則をはじめに置かなければなりません。これがユーザーにアスタリスクの意味を知らせても、それに依存することはできません。スクリーンリーダーはアスタリスクが出てくると "スター" と読み上げます。視力のあるユーザーがマウスを持ってくると、title 属性によって"必須"と表示されます。タイトルはスクリーンリーダーの設定により読み上げられるので、常にスクリーンリーダーに読み上げられる aria-label 属性を入れておくのがより信頼性が高いでしょう。

+ +

上記の違いをふまえると、以降を効率よく見ていけるでしょう:

+ + + +
+

: スクリーンリーダーによっては、少し異なる結果になる場合もあります。これは VoiceOver (と同様に動作する NVDA)でテストしています。あなたの体験を聞きたいです。

+
+ +
+

: この例は GitHub の required-labels.html で見ることができます(ライブ版も見てください)。2 や 3 のコメントを外したバージョンの例を実行しないでください — 複数の label と複数の同じ input ID があると、スクリーンリーダーは確実に混乱します!

+
+ +

フォームで使用される一般的な HTML 構造

+ +

ウェブフォーム特有の構造の前提として、フォームは単に HTML であると覚えておくとよいでしょう。つまり、ウェブフォームを組み立てるために HTML のすべての力を利用できるのです。

+ +

サンプルでわかるように、ラベルとそのウィジェットを {{HTMLElement("ul")}} や {{HTMLElement("ol")}} リストの中の {{HTMLElement("li")}} 要素で包み込むのが一般的な慣習です。HTML リストにあるように、{{HTMLElement("p")}} 要素と {{HTMLElement("div")}} 要素も良く使われます。リストは複数のチェックボックスやラジオボタンを構造化するのに最もよく使われます。

+ +

{{HTMLElement("fieldset")}} 要素に加えて、複雑なフォームの構築に HTML の見出し (例{{htmlelement("h1")}}, {{htmlelement("h2")}}) やセクション (例 {{htmlelement("section")}}) を使うことも一般的です。

+ +

とりわけ、コーディングスタイルがどうあるのが心地よく、どれがアクセシブルで使いやすいフォームとなるのかを見つけるのはあなた次第です。別の機能セクションは別の {{htmlelement("section")}} 要素と、ラジオボタンを含む {{htmlelement("fieldset")}} にそれぞれ分けておくべきです。

+ +

アクティブラーニング: フォーム構造を構築する

+ +

これらのアイデアを実践し、もう少し複雑なフォーム構造、つまり支払いフォームを作成しましょう。このフォームはあなたがまだ理解していないかもしれないウィジェットタイプをいくつも含みますが、今はそのことを心配しないでください。次の記事 (ネイティブフォームウィジェット) でそれらがどのように機能するのかがわかります。今のところ、以下の説明に沿って説明を注意深く読み、フォームを構成するためにどのラッパー要素を使用しているか、そしてその理由を理解することから始めてください。

+ +
    +
  1. あらかじめ、空のテンプレートファイルお支払いフォームの CSS のローカルコピーをコンピューターの新しいディレクトリーに作成します。
  2. +
  3. まず最初に、HTML {{htmlelement("head")}} 内に次の行を追加して CSS を HTML に適用します。 +
    <link href="payment-form.css" rel="stylesheet">
    +
  4. +
  5. 次に、外側の {{htmlelement("form")}} 要素を追加してフォームを作成します: +
    <form>
    +
    +</form>
    +
  6. +
  7. <form> タグ内に、必須フィールドにマークを付ける方法をユーザーに通知するための見出しと段落を追加します: +
    <h1>Payment form</h1>
    +<p>Required fields are followed by <strong><abbr title="required">*</abbr></strong>.</p>
    +
  8. +
  9. 次に、前のエントリーの下に、より大きなコードセクションをフォームに追加します。ここでは、連絡先情報フィールドを個別の {{htmlelement("section")}} 要素内にラップしていることがわかります。さらに、2 つのラジオボタンのセットがあり、それぞれ独自のリスト ({{htmlelement("li")}}) 要素の中に入れています。最後に、2 つの標準テキスト {{htmlelement("input")}} とそれに関連する {{htmlelement("label")}} 要素があり、それぞれ {{htmlelement("p")}} の内側に含まれていて、パスワードを入力するためのパスワード入力があります。フォームにこのコードを追加してください: +
    <section>
    +    <h2>Contact information</h2>
    +    <fieldset>
    +      <legend>Title</legend>
    +      <ul>
    +          <li>
    +            <label for="title_1">
    +              <input type="radio" id="title_1" name="title" value="K" >
    +              King
    +            </label>
    +          </li>
    +          <li>
    +            <label for="title_2">
    +              <input type="radio" id="title_2" name="title" value="Q">
    +              Queen
    +            </label>
    +          </li>
    +          <li>
    +            <label for="title_3">
    +              <input type="radio" id="title_3" name="title" value="J">
    +              Joker
    +            </label>
    +          </li>
    +      </ul>
    +    </fieldset>
    +    <p>
    +      <label for="name">
    +        <span>Name: </span>
    +        <strong><abbr title="required">*</abbr></strong>
    +      </label>
    +      <input type="text" id="name" name="username">
    +    </p>
    +    <p>
    +      <label for="mail">
    +        <span>E-mail: </span>
    +        <strong><abbr title="required">*</abbr></strong>
    +      </label>
    +      <input type="email" id="mail" name="usermail">
    +    </p>
    +    <p>
    +      <label for="pwd">
    +        <span>Password: </span>
    +        <strong><abbr title="required">*</abbr></strong>
    +      </label>
    +      <input type="password" id="pwd" name="password">
    +    </p>
    +</section>
    +
  10. +
  11. それでは、フォームの 2 番目の <section> — 支払い情報に目を向けます。ここには 3 つの異なるウィジェットとそのラベルがあり、それぞれ <p> の中に含まれています。1 つ目は、クレジットカードの種類を選択するためのドロップダウンメニュー ({{htmlelement("select")}}) です。2 番目は、クレジットカード番号を入力するための tel 型の <input> 要素です。number 型を使うこともできますが、そのスピナーUIは望ましくありません。最後のものは、カードの有効期限を入力するための date 型の <input> 要素です。これは、サポートしているブラウザーでは日付選択ウィジェットが表示され、サポートしていないブラウザーでは通常のテキスト入力に戻ります。新しい入力タイプは HTML5 入力タイプで再度紹介されます。
    +
    + 前のセクションの下に次のように入力してください。 +
    <section>
    +    <h2>Payment information</h2>
    +    <p>
    +      <label for="card">
    +        <span>Card type:</span>
    +      </label>
    +      <select id="card" name="usercard">
    +        <option value="visa">Visa</option>
    +        <option value="mc">Mastercard</option>
    +        <option value="amex">American Express</option>
    +      </select>
    +    </p>
    +    <p>
    +      <label for="number">
    +        <span>Card number:</span>
    +        <strong><abbr title="required">*</abbr></strong>
    +      </label>
    +      <input type="tel" id="number" name="cardnumber">
    +    </p>
    +    <p>
    +      <label for="date">
    +        <span>Expiration date:</span>
    +        <strong><abbr title="required">*</abbr></strong>
    +        <em>formatted as mm/dd/yyyy</em>
    +      </label>
    +      <input type="date" id="date" name="expiration">
    +    </p>
    +</section>
    +
  12. +
  13. 最後に追加するセクションはもっと単純で、フォームデータを送信するための submit タイプの {{htmlelement("button")}} のみを含みます。これをフォームの一番下に追加してください: +
    <p> <button type="submit">Validate the payment</button> </p>
    +
  14. +
+ +

完成したフォームは以下のように動作しています (GitHub でも確認できます。payment-form.html ソースを参照してライブ実行してください)。

+ +

{{EmbedLiveSample("A_payment_form","100%",620, "", "Learn/HTML/Forms/How_to_structure_an_HTML_form/Example")}}

+ +

あなたのスキルをテストしてみましょう!

+ +

この記事はここまでですが、最も重要な情報を覚えていますか? 先に進む前に、この情報を保持しているかどうかを確認するためのテストがあります — Test your skills: Form structure を参照してください。

+ +

まとめ

+ +

ウェブフォームを適切に構築するためのあらゆる知識を得ることができました。これからここで出てきた機能を見ていき、次の記事では、ユーザーから情報を集めるのに使いたくなるすべての種類のフォームウィジェットの詳細な実装について詳しく見ていきます。

+ +

関連情報

+ + + +

{{PreviousMenuNext("Learn/HTML/Forms/Your_first_HTML_form", "Learn/HTML/Forms/The_native_form_widgets", "Learn/HTML/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/html5_input_types/index.html b/files/ja/learn/forms/html5_input_types/index.html new file mode 100644 index 0000000000..c825e48653 --- /dev/null +++ b/files/ja/learn/forms/html5_input_types/index.html @@ -0,0 +1,289 @@ +--- +title: HTML5 入力タイプ +slug: Learn/Forms/HTML5_input_types +tags: + - Beginner + - Controls + - Example + - Forms + - Guide + - HTML + - Web + - Widgets +translation_of: Learn/Forms/HTML5_input_types +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Forms/Basic_native_form_controls", "Learn/Forms/Other_form_controls", "Learn/Forms")}}
+ +

前の記事では {{htmlelement("input")}} 要素を見てきて、HTML の初期から利用できる type 属性の値をカバーしてきました。ここでは最近のフォームコントロールの機能、特定のデータを集めるのに HTML5 で追加された、新しい入力タイプを詳しく見ていきましょう。

+ + + + + + + + + + + + +
前提条件:コンピューターリテラシーの基本と、HTML の理解
目的:ネイティブフォームコントロールで利用できる新しい入力タイプの理解と、HTML を用いた実装方法。
+ +
+

: この記事のたいていの昨日はブラウザーを超えて広くサポートされています。例外は注記しておきます。ブラウザーサポートについての詳細は、HTML フォーム要素のリファレンス、特に <input> タイプリファレンスを見てください。

+
+ +

HTML フォームコントロールの見た目はデザイナーの仕様により全く異なるため、ウェブ開発者はときどき独自のフォームコントロールを作成します。これは上級のチュートリアル — カスタムフォームウィジェットの作成方法 — で扱います。

+ +

メールアドレスフィールド

+ +

このフィールドタイプは {{htmlattrxref("type","input")}}属性の email の値でセットされます:

+ +
<input type="email" id="email" name="email">
+ +

この {{htmlattrxref("type","input")}} が使われたとき、ユーザーは有効なメールアドレスをフィールドに入力することが必須です。その他のコンテンツでは、ブラウザーによってフォーム送信時にエラーが表示されます。この動作は下記のスクリーンショットで見ることができます。

+ +

An invalid email input showing the message "Please enter an email address."

+ +

email 入力タイプと multiple 属性を組み合わせて、複数のメールアドレスが同じ入力に (カンマ区切りで)入力させることもできます:

+ +
<input type="email" id="email" name="email" multiple>
+ +

いくつかの端末 — 特にスマートフォンのような動的キーボードつきのタッチデバイス — では、@ キーを含むメールアドレス入力に適した別の仮想キーパッドが現れることもあります。Android 版 Firefox のキーボードの例として下記のスクリーンショットを見てください:

+ +

firefox for android email keyboard, with ampersand displayed 既定では.

+ +
+

: 基本的なテキスト入力タイプの例は basic input examples にあります(ソースコード も見てください)。

+
+ +

これは新しい入力タイプを使う良い理由であり、こうした端末のユーザーのユーザー体験を向上させます。

+ +

クライアント側の検証

+ +

上で見てきたように、他の新しい input タイプと同様に、email は組み込みのクライアント側のエラー検証 — データがサーバーに送られる前にブラウザーで実行されるもの — があります。これはユーザーが正確に入力するのに役立ち、時間を短縮できます — データが正しくないことをサーバーとのラウンドトリップを待つことなくすぐに知ることは便利です。

+ +

しかしこれは完全なセキュリティ対策と考えるべきではありません! アプリは送信データのセキュリティ確認をクライアント側と同様にサーバー側でも行うべきで、なぜならクライアント側の検証は簡単にオフにできるため悪意のあるユーザーは簡単にサーバーに不正なデータを送信できるためです。起こりうることについては Website security を読んで下さい。サーバー側の検証を実装するのはこのモジュールの範囲を超えていますが、記憶しておくべきです。

+ +

既定の制約では a@b は有効なメールアドレスです。これは既定では email 入力タイプはイントラネットのメールアドレスを許可しているためです。異なる検証動作を実装するには、pattern 属性を用いたり、エラーメッセージをカスタムできます。この機能の使い方は後の クライアント側検証の記事で話します。

+ +
+

: 入力データがメールアドレスでない場合、{{cssxref(':invalid')}} 疑似クラスがマッチして、{{domxref('validityState.typeMismatch')}} プロパティは true を返します。

+
+ +

検索フィールド

+ +

検索フィールドは、ページやアプリの検索ボックス作成に使われるものです。このフィールドタイプは {{htmlattrxref("type","input")}}属性の search の値でセットされます:

+ +
<input type="search" id="search" name="search">
+ +

text フィールドと search フィールドの主な違いは、ブラウザーの見た目のスタイル設定方法です。よく、search フィールドは角丸で描画されます。また時折、値がクリックされたときに値をクリアする "Ⓧ"も表示されます。その他、ダイナミックキーボードのある端末では、キーボードの enter キーが "検索"、あるいは虫眼鏡アイコンで表示されることもあります。

+ +

下記のスクリーンショットでは macOS での Firefox 71, Safari 13, Chrome 79 と Windoes10 での Edge 18 と Chrome 79 での文字が入った検索フィールドを表示しています。注意として、クリアのアイコンはフィールドに値が入った場合のみ表示され、Safari を除いてフィールドにフォーカスが当たった場合のみ表示されます。

+ +

Screenshots of search fields on several platforms.

+ +

もう 1 つの注目すべき機能として、search フィールドの値は同一サイトの複数ページにまたがって、自動保存してオートコンプリートすることができます。これはたいていのモダンブラウザーでは自動的に起こる傾向があります

+ +

電話番号フィールド

+ +

{{htmlattrxref("type","input")}} 属性に tel の値を使って作られる電話番号の入力専用フィールド:

+ +
<input type="tel" id="tel" name="tel">
+ +

ダイナミックキーボードつきのタッチデバイスにアクセルしたとき、たいていの端末では type="tel" が出てくると数字のキーパッドを表示します、つまりこのタイプは数字キーパッドが使えるときに使えて、電話番号に使う必要があるだけではありません。

+ +

下記は Android 用 Firefox キーボードの例です:

+ +

firefox for android email keyboard, with ampersand displayed 既定では.

+ +

世界中にはいろいろな電話番号のフォーマットがあるため、このフィールドはユーザーが入力した値に制約を一切つけません (つまり、文字が入っていることなどがありえます)。

+ +

前に触れた通り、pattern 属性が制約を強化するのに使われて、これはクライアント側検証で学びます。

+ +

URL フィールド

+ +

{{htmlattrxref("type","input")}} 属性に url の値を使うことで作成できる URL 入力の専用フィールド:

+ +
<input type="url" id="url" name="url">
+ +

これはフィールドに特別な検証制約を追加します。ブラウザーは (http:のような) プロトコルがない場合や、URL の形式が良くない場合にエラーを報告します。ダイナミックキーボードのある端末ではよく、コロン、ピリオド、スラッシュの複数やすべてをデフォルトキーとして表示します。

+ +

例は下記のものを見てください (Android 用 Firefox の例):

+ +

firefox for android email keyboard, with ampersand displayed 既定では.

+ +
注: URL が正しい形式であっても、それが必ずしも実際存在する場所を参照しているとは限りません!
+ +

数値フィールド

+ +

{{HTMLElement("input")}} {{htmlattrxref("type","input")}} に number を指定して作成される数値入力用のコントロール。このコントロールはテキストフィールドのような見た目であるが、浮動小数値のみを許可し、通常は数値を増減するためのスピナー形式のボタンがあります。ダイナミックキーボードつきの端末では、一般的に数値キーボードが表示されます。

+ +

下記のスクリーンショット (Android 用 Firefox より) に例があります:

+ +

firefox for android email keyboard, with ampersand displayed 既定では.

+ +

number の入力タイプでは、{{htmlattrxref("min","input")}} と {{htmlattrxref("max","input")}} 属性をセットすることで最小値と最大値の制約をつけることができます。

+ +

スピナーボタンを押すことで増減させる増分を接とするのに step 属性を使うこともできます。既定では、number 入力タイプは数値が整数値の場合のみ検証します。浮動小数を許可するには、step="any"を指定します。省略された場合、step 値は既定で 1 となり、つまりすべての数値が有効です。

+ +

いくつか例を見てみましょう。次の最初の例では値が 110 の間に制限され、増減ボタンで値が 2 ずつ変更される数値コントロールを作成しています。

+ +
<input type="number" name="age" id="age" min="1" max="10" step="2">
+ +

2 つ目の例では値が 01 の間に制限され、増減ボタンで値が 0.01 ずつ変更される数値コントロールを作成しています。

+ +
<input type="number" name="change" id="pennies" min="0" max="1" step="0.01">
+ +

number 入力タイプは有効な値の範囲に制限されているときに意味を持ちます。例えば、ある人の年齢や身長です。範囲が大きすぎて増分が意味をなさない場合 (例えば USA の郵便番号は、00001 から 99999 の範囲です)、tel タイプがより良い選択となることもあります。これは数値のスピナーUI機能に対して、数値キーパッドを提供します。

+ +
+

: number 入力は Internet Explorer の 10 より前のバージョンではサポートされません。

+
+ +

スライダーコントロール

+ +

数字を選ぶもう1つの方法はスライダーです。家造りのようなサイトで資産の価格をフィルターするのによく見ることでしょう。これを示す例をライブで見てみましょう:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/range-example/index.html", '100%', 200)}}

+ +

使用からすると、スライダーはテキストフィールドより不正確です。このため、正確な値が必ずしも重要でない数値の選択に使われます。

+ +

スライダーは {{HTMLElement("input")}} に {{htmlattrxref("type","input")}} 属性を range にセットして作成します。スライダーはマウスやタッチや、キーパッドの矢印で移動できます。

+ +

スライダーを適切に設定するのは重要です。そのためには、それぞれ最小、最大、増分値を設定する min, max, step 属性を設定するのを強くお奨めします。

+ +

上の例の背後にあるコードを見て、どのように実現されるかを見てみましょう。まずは基本となる HTML:

+ +
<label for="price">Choose a maximum house price: </label>
+<input type="range" name="price" id="price" min="50000" max="500000" step="100" value="250000">
+<output class="price-output" for="price"></output>
+ +

この例では、値が 50000500000 までの範囲で、100 ずつ増減するスライダーを作成しています。デフォルトの値は value 属性を使って 250000 としています。

+ +

スライダーについての問題は、現在の値がいくつなのかのフィードバックが一切ないことです。これは、現在の値を入れている {{htmlelement("output")}} 要素を導入する理由です (この要素は次の記事でも見ていきます)。入力値や、あらゆる要素内の計算結果を表示できますが、<output> は特別です — <label>のように — for 属性を取って要素や出力値の元となる要素と関連付けることができます。

+ +

実際に現在の値を表示して、変更時に更新するには、JavaScript を使う必要がありますが、比較的に簡単です:

+ +
const price = document.querySelector('#price');
+const output = document.querySelector('.price-output');
+
+output.textContent = price.value;
+
+price.addEventListener('input', function() {
+  output.textContent = price.value;
+});
+ +

ここでは range 入力と output を 2 つの変数に保存しています。次に outputtextContent を入力の value ですぐにセットします。最後に、イベントリスナーをセットして、range スライダーが移動するといつも、outputtextContent を新しい値に更新します。

+ +
+

: range 入力は Internet Explorer 10以前のバージョンではサポートされません。

+
+ +

日付と時刻ピッカー

+ +

日付と時刻の値を集めるのはウェブ開発者にとってずっと悪夢でした。すばらしいユーザー体験のためには、カレンダー選択UIを与えるのが重要であり、それによってユーザーはネイティブのカレンダーアプリへのコンテキストスイッチや、パースするのが難しい違うフォーマットで入力する可能性を避けて日付選択ができます。前の千年紀の最後の分は、次のようにいろいろな表現ができます。例えば: 1999/12/31, 23:59 or 12/31/99T11:59PM.

+ +

HTML 日付コントロールは、カレンダーウィジェットを提供して統一されたデータを作成し、この種類のデータを扱うことができます。

+ +

日付と時刻コントロールは{{HTMLElement("input")}} 要素と、日付か時間かその両方かを集めたいのかにあわせて適当な {{htmlattrxref("type","input")}} 属性を使って作成できます。非サポートブラウザーで{{htmlelement("select")}} 要素にフォールバックするライブの例は次の通り:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/datetime-local-picker-fallback/index.html", '100%', 200)}}

+ +

利用できるいろいろなタイプを簡単に見ていきましょう。注意としては、このタイプの使用法はとても複雑で、特にブラウザーサポートを使う考えた場合そうです (下記を見てください); 完全な詳細情報は、各タイプのリファレンスページと、そこにある詳細な例を見てください。

+ +

datetime-local

+ +

<input type="datetime-local"> は特別なタイムゾーン情報のない日付と時刻を表示して選択するウィジェットを作成します。

+ +
<input type="datetime-local" name="datetime" id="datetime">
+ +

month

+ +

<input type="month"> は年と月を表示して選択するウィジェットを作成します。

+ +
<input type="month" name="month" id="month">
+ +

time

+ +

<input type="time"> は時刻の値を表示して選択するウィジェットを作成します。時刻は 12時間のフォーマットで表示されますが、戻り値は 24時間フォーマットです。

+ +
<input type="time" name="time" id="time">
+ +

week

+ +

<input type="week"> は本年の週の番号を表示して選択するウィジェットを作成します。

+ +

週は月曜始まりで日曜日で終わります。それに加えて、その年の最初の週はその年の最初の木曜日を含みます—これはその年の最初の日を含まないことも、前年の最後の数日を含むこともあります。

+ +
<input type="week" name="week" id="week">
+ +

date/time 値の制限

+ +

日付をと時刻のコントロールはすべて、minmax 属性で制約をつけることができて、さらに step 属性 (この値は入力タイプによって変わります)で追加の制約も可能です。

+ +
<label for="myDate">When are you available this summer?</label>
+<input type="date" name="myDate" min="2013-06-01" max="2013-08-31" step="7" id="myDate">
+ +

date/time 入力のブラウザーサポート

+ +

日付と時刻のウィジェットに最良のブラウザーサポートがないことに注意するべきです。現状では、Chrome, Edge, Opera ではよくサポートしていますが、Internet Explorerではサポートがなく、Safari にはモバイルのサポートがあり(しかしデスクトップサポートはなし)、Firefox では timedate のみサポートします。

+ +

上でリンクしているリファレンスページでは非サポートブラウザーでフォールバックするようプログラミングするヒントを提供しています; もう1つのオプションは日付選択を提供する JavaScript ライブラリの利用です。たいていのモダンなフレームワークには機能を利用できるコンポーネントがあり、またスタンドアローンのライブラリもあります (ヒントとなる Top date picker JavaScript plugins and libraries を見てください)。

+ +

色選択コントロール

+ +

色も扱いが難しいです。色の表現がたくさんあります: RGB 値 (10進数や16進数), HSL 値、キーワードなど。

+ +

color コントロールは {{HTMLElement("input")}} 要素を、 {{htmlattrxref("type","input")}} 属性に colorをつけて作成できます:

+ +
<input type="color" name="color" id="color">
+ +

サポートされている場合、色コントロールはOSのデフォルトの色選択機能を呼び出して選択できるようにする傾向があります。下記のスクリーンショットはmacOSの Firefox の例です:

+ +

firefox for android email keyboard, with ampersand displayed 既定では.

+ +

次のライブサンプルも試してみてください:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/color-example/index.html", '100%', 200)}}

+ +

戻り値は常に小文字で 6桁の16進数の色です。

+ +
+

: color 入力は Internet Explorerではサポートされていません。

+
+ +

スキルをテストしましょう!

+ +

この記事の最後に到着しましたが、最も大事な情報を覚えていますか?次に進む前に、この情報を保持しているか検証するテストがあります — Test your skills: HTML5 controls を見てください。

+ +

まとめ

+ +

ここで HTML5 フォーム入力タイプの旅が終了です。動作が特殊なため簡単に分類できないその他のコントロールタイプも少しあって、それを知るのも大事です。次の記事で扱います。

+ +

{{PreviousMenuNext("Learn/Forms/Basic_native_form_controls", "Learn/Forms/Other_form_controls", "Learn/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/html_forms_in_legacy_browsers/index.html b/files/ja/learn/forms/html_forms_in_legacy_browsers/index.html new file mode 100644 index 0000000000..edc9009df2 --- /dev/null +++ b/files/ja/learn/forms/html_forms_in_legacy_browsers/index.html @@ -0,0 +1,219 @@ +--- +title: 古いブラウザでの HTML フォーム +slug: Learn/Forms/HTML_forms_in_legacy_browsers +tags: + - Example + - Forms + - Guide + - HTML + - Intermediate + - Web +translation_of: Learn/Forms/HTML_forms_in_legacy_browsers +--- +
{{LearnSidebar}}{{PreviousMenuNext("Learn/HTML/Forms/Sending_forms_through_JavaScript", "Learn/HTML/Forms/Styling_HTML_forms", "Learn/HTML/Forms")}}
+ +

すべての Web 開発者は、Web が非常につらい場所であることをいち早く (時に痛いほど) 学びます。もっともいまいましいのは古いブラウザです。まあそれは受け入れて、我々が「古いブラウザ」と言うとき、みんなが古いバージョンの Internet Explorer を念頭に置いています...しかしそれだけではありません。ESR バージョンのような1年経過した Firefox もまた古いブラウザです。そしてモバイルの世界では? ブラウザも OS もアップデートできないときは?そう、最新ではない標準的なブラウザを搭載している多くの古い Android 携帯電話または iPhone があります。これらも古いブラウザです。

+ +

残念ながら、そのような困難に対処するのも仕事の一部です。幸い、古いブラウザによって起きる問題の 80% 程度を解決する助けになることがわかっている秘訣がいくつかあります。

+ +

問題について学ぶ

+ +

実のところもっとも重要なことは、一般的なパターンを理解するためにそれらのブラウザに関するドキュメントを読むことです。例えば、多くの場合 CSS のサポート状況が HTML フォームにおける最大の問題です。あなたはスタートとして適切な場所にいます。使用したい要素 (または DOM インターフェイス) のサポート状況を確認しましょう。MDN では Web ページで使用できる多くの要素、プロパティあるいは API について、実装状況の一覧表を入手できます。しかし、驚くほど役に立つであろうリソースが他にもあります:

+ +

ブラウザベンダーのドキュメント

+ + + +

独自のドキュメント

+ + + +

物事をシンプルに

+ +

HTML フォームは複雑なやりとりを伴うことから、一つの経験則があります: 可能な限りシンプルにしてください (日本語版)。フォームを "より立派に" あるいは "高機能に" したいケースはたくさんありますが、効率的なフォームの作成はデザインや技術の問題ではありません。それを忘れないように、UX For The Masses にあるフォームのユーザビリティに関する記事を読む時間をとってください。

+ +

Graceful Degradation は Web 開発者の最高の味方

+ +

Graceful Degradation と Progressive Enhancement は、一度に幅広いブラウザをサポートすることにより、すばらしいものを構築可能にする開発パターンです。新しいブラウザで何かを構築するときにそれが動作すると確信したい場合は、あれやこれやで古いブラウザにおいて Graceful Degradation を行っています。

+ +

HTML フォームに関する例をいくつか見ていきましょう。

+ +

HTML input のタイプ

+ +

HTML5 でもたらされた新たな input のタイプは、退行手段がとてもわかりやすいことから、すばらしいものです。ブラウザが {{HTMLElement("input")}} 要素の {{htmlattrxref("type","input")}} 属性の値を知らない場合は、値が text であるかのようにフォールバックします。

+ +
<label for="myColor">
+  Pick a color
+  <input type="color" id="myColor" name="color">
+</label>
+ + + + + + + + + + + + + + +
Chrome 24Firefox 18
Screen shot of the color input on Chrome for Mac OSXScreen shot of the color input on Firefox for Mac OSX
+ +

CSS 属性セレクタ

+ +

CSS の属性セレクタHTML フォーム でとても有用ですが、一部の古いブラウザはサポートしていません。その場合、慣例では type と同等の class で二重化します:

+ +
<input type="number" class="number">
+ +
input[type=number] {
+  /* こちらは一部のブラウザで機能しません */
+}
+
+input.number {
+  /* こちらはどのブラウザでも動作するでしょう */
+}
+ +

以下の記述では役に立たず (冗長であるため)、一部のブラウザで機能しない可能性があります:

+ +
input[type=number],
+input.number {
+  /* セレクタのひとつが理解できない場合は規則全体が無視されるため、
+     これは一部のブラウザで動作しません */
+}
+ +

フォームボタン

+ +

HTML フォームでボタンを定義する方法は 2 つあります:

+ + + +

{{HTMLElement("input")}} は、要素セレクタを使用して CSS を適用したい場合に若干難しいことになります:

+ +
<input type="button" class="button" value="click me">
+ +
input {
+  /* この規則は、input 要素で定義するボタンのデフォルトのレンダリングを無効にします */
+  border: 1px solid #CCC;
+}
+
+input.button {
+  /* これはデフォルトのレンダリングを復元しません */
+  border: none;
+}
+
+input.button {
+  /* こちらも同様です! 実際は、あらゆるブラウザでこれを行う標準的な方法はありません */
+  border: auto;
+}
+ +

{{HTMLElement("button")}} 要素は、起こりうる 2 つの問題に悩まされます:

+ + + +
<!-- ボタンをクリックすると "A" ではなく "<em>Do A</em>" を送信する場合があります -->
+<button type="submit" name="IWantTo" value="A">
+  <em>Do A</em>
+</button>
+ +

プロジェクトの制約に基づいて、どちらの解決策を選択するかはあなた次第です。

+ +

CSS を手放そう

+ +

HTML フォームと古いブラウザにおける最大の問題は CSS のサポートです。Property compatibility table for form widgets の記事の複雑さからおわかりいただけるとおり、これはとても難しい問題です。テキスト系の要素でいくらか調整が (サイズや文字色など) 可能であるとしても、それらには必ず副作用があります。残された最善の方法は、HTML フォームのウィジェットに一切スタイルを設定しないことです。ただし、それでも周囲のアイテムにはスタイルを設定してもかまいません。もしあなたがプロフェッショナルで、顧客が要求するようなことがあれば、JavaScript によるウィジェットの再構築といった難易度が高い技術について調査してみるとよいでしょう。しかしそのような場合でも、顧客の愚かさを変えることをためらってはいけません。

+ +

機能検出とポリフィル

+ +

JavaScript は新しいブラウザにおいてすばらしい技術ですが、古いブラウザでは多くの問題を抱えています。

+ +

控えめな JavaScript

+ +

最大の問題のひとつは、API を利用できるかです。そのため、"{{原語併記("控えめな", "unobtrusive")}}" JavaScript によって取り組むことベストプラクティスであると考えられています。これは、2 つの要件によって定義される開発パターンです:

+ + + +

The principles of unobtrusive JavaScript (原文は Peter-Paul Koch 氏によって Dev.Opera.com 向けに記述され、現在は Docs.WebPlatform.org に移動しました) で、これらのアイデアを明快に説明しています。

+ +

Modernizr ライブラリ

+ +

欠けている API を提供することで、すばらしい "ポリフィル" が大きな助けになるケースが多数あります。ポリフィルは、古いブラウザにおける機能性の "穴を埋める" 小さな JavaScript コードです。ポリフィルは任意の機能のサポート状況を改善するために使用できますが、JavaScript 向けに使用するのは CSS や HTML 向けより低リスクです。JavaScript が動作しない可能性は多数あります(ネットワークの問題、スクリプトの競合 など)。しかし JavaScript については、控えめな JavaScript を念頭に置いて取り組む場合はポリフィルが欠けたとしても、重大な問題にはなりません。

+ +

欠けている API に対してポリフィルを適用する最善の方法は、Modernizr ライブラリおよびそこからスピンオフしたプロジェクトである YepNope を使用することです。Modernizr は、機能が利用できるかを確認して適宜対応を行えるようにするためのライブラリです。YepNope は、条件付きで読み込みを行うライブラリです。

+ +

サンプルは以下のとおりです:

+ +
Modernizr.load({
+  // ブラウザが HTML5 の form validation API をサポートしているかを確認します
+  test : Modernizr.formvalidation,
+
+  // ブラウザがサポートしない場合は、指定したポリフィルを読み込みます
+  nope : form-validation-API-polyfill.js,
+
+  // どの場合でも、API に依存するコアアプリのファイルを読み込みます
+  both : app.js,
+
+  // 両方のファイルを読み込んだら、アプリを初期化するためにこの関数を呼び出します
+  complete : function () {
+    app.init();
+  }
+});
+ +

好都合なことに、Modernizr チームはすばらしいポリフィルのリストを管理しています。必要なポリフィルを選びましょう。

+ +
+

注記: Modernizr は、控えめな JavaScript や Graceful Degradation のテクニックに取り組むことを支援するためのすばらしい機能を搭載しています。Modernizr のドキュメントをご覧ください。

+
+ +

パフォーマンスに注意を払う

+ +

Modernizr のようなスクリプトはパフォーマンスを非常に意識していますが、200キロバイトのポリフィルをロードするとアプリケーションのパフォーマンスに影響を与えます。これは従来のブラウザでは特に重要です。多くは JavaScript エンジンが非常に遅く、すべての polyfill の実行はユーザにとって苦痛になります。パフォーマンスはそれ自体が課題ですが、従来のブラウザはそれに非常に敏感です。基本的にそれらは遅く、より多くのポリフィルが必要で、より多くの JavaScript を処理する必要があります。そのため現代のブラウザと比較して二重の負担をかけていることになります。従来のブラウザでコードをテストして、実際のパフォーマンスを確認しましょう。場合によっては、一部の機能を削除すると、すべてのブラウザでまったく同じ機能を使用するよりも優れたユーザーエクスペリエンスが得られます。最後に念のため、常にエンドユーザのことを考えるようにしてください。

+ +

おわりに

+ +

お分かりのように、従来のブラウザを扱うことは単にフォームに関することだけではありません。テクニック一式です。 しかし、それらすべてをマスターすることは、この記事の範囲を超えています。

+ +

この HTML フォームガイドのすべての記事を読んでいれば、フォームの使用に慣れているはずです。新しいテクニックやヒントを見つけた場合は、ガイドの改善にご協力ください。

+ +

{{PreviousMenuNext("Learn/HTML/Forms/Sending_forms_through_JavaScript", "Learn/HTML/Forms/Styling_HTML_forms", "Learn/HTML/Forms")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/forms/index.html b/files/ja/learn/forms/index.html new file mode 100644 index 0000000000..aa428b7db7 --- /dev/null +++ b/files/ja/learn/forms/index.html @@ -0,0 +1,96 @@ +--- +title: HTML フォーム +slug: Learn/Forms +tags: + - Beginner + - Featured + - Forms + - Guide + - HTML + - Landing + - Learn + - Web +translation_of: Learn/Forms +--- +
{{LearnSidebar}}
+ +

このモジュールでは、ウェブフォームの習得に役立つ一連の記事をそろえています。ウェブフォームは、ユーザーとやり取りするための強力なツールです — 通常、ユーザーデータを集めたり、ユーザーインターフェイスの制御に使われてきました。しかし歴史的および技術的な理由から、その能力を十分に生かして使用する方法は、必ずしも明確とは限りません。このガイドではウェブフォームの構造からスタイル、データの検証やサーバーへの送信まであらゆる本質的な側面を取り上げます。

+ +

前提条件

+ +

このモジュールを始める前に、少なくとも HTML 入門を一通り読んでおくべきです。ここでは簡単に理解できる{{anch("Introductory guides", "導入ガイド")}}が見つかり、ネイティブフォームコントロールガイドも利用できます。

+ +

しかしそれ以外のモジュールは HTML 知識よりも少し高度です — ページにフォームウィジェットを置くのは簡単ですが、高度なフォームの機能や CSS や JavaScript を使わないと実際に活用できません。このため、その他の節を見る前にまずは CSSJavaScript を学んでおくのをお勧めします。

+ +

上記のテキストは、ウェブフォームを HTML、CSS、JavaScript エリアに混ぜずに、独立したモジュールに置くことの良い指針です — フォーム要素は HTML要素より複雑で、最大限利用するには、関連する CSS と JavaScript テクニックを最大限活用する。

+ +
+

: 自分のファイルを作ることができないコンピューター/タブレット/その他の端末で作業している場合、(大半の) コード例を JSBinGlitch といったオンラインコーディングプログラムで試すことができます。

+
+ +

導入ガイド

+ +
+
初めてのウェブフォーム
+
このシリーズの最初の記事では ウェブフォーム作成のほんの最初を提供し、それには簡単なフォームのデザインや、適切な HTML 要素で実装することや、CSS で簡単なスタイルづけをすることや、データがサーバーに送られる方法などが含まれます。
+
HTML フォームの構築方法
+
基本的な話と共に、ここではフォームの色々な部分に構造と意味を与えるのに使う要素を詳しく見ていきます。
+
+ +

どんなフォームウィジェットが利用できますか?

+ +
+
ネイティブフォームウィジェット
+
オリジナルの HTML {{htmlelement("input")}}タイプの詳細や、色々なデータ型を集めるのにどんなオプションが使えるかを見ていきます。
+
HTML5 入力タイプ
+
ここでは <input> 要素を深く見ていき、HTML5 リリースにて追加された入力タイプや、さまざまな UI コントロールとデータコレクションの改良を見ます。さらには、{{htmlelement('output')}} 要素も見ます。
+
その他のフォームコントロール
+
次には<input> 以外のフォームコントロールと関連するツール、例えば {{htmlelement('select')}}、{{htmlelement('textarea')}}、{{htmlelement('meter')}}、{{htmlelement('progress')}} を見ていきます。
+
+ +

フォームのスタイルガイド

+ +
+
ウェブフォームをスタイリングする
+
この記事では基本的なスタイリング作業のために知るべき基本を含む、フォームを CSS でスタイリングするための入門を紹介します。
+
ウェブフォームの高度なスタイリング
+
ここでは、スタイルが難しい要素を扱うために必要な、さらに高度なフォームスタイリングテクニックを紹介します。
+
UI 擬似クラス
+
現在の状態に応じて制御される HTML フォームコントロールを可能とする、UI 擬似クラス の入門です。
+
+ +

フォームデータを検証して送信する

+ +
+
クライアント側のフォームデータ検証
+
データ送信だけで充分ではありません — ユーザーがフォームに記入したデータが、我々が処理に成功するような正しいフォーマットであり、アプリケーションが破綻しないかを確認する必要があります。またユーザーが正しくフォームを記入してアプリを使うのにイライラしないよう手助けしたいです。フォーム検証はこの目的を達成するのに役立ちます — この記事では知っておくべきことを教えます。
+
フォームデータの送信
+
この記事ではユーザーがフォームを送信するときに何が起こるのかを見ます — データがどこに行くのか、そしてそこでデータを受けたらどう扱うかなどです。また、フォームデータ送信に関連するセキュリティ上の懸念もその一部です。
+
+ +

高度なガイド

+ +

下記の記事は学習コースに必須ではないですが、上記のテクニックをマスターしてそれ以上知りたいときには、興味深く役立つものでしょう。

+ +
+
カスタムフォームコントロールの作成方法
+
場合によってはネイティブのフォームウィジェットが必要となるものを提供していないことがあるでしょう。例えばスタイリングや機能で。この場合、生の HTML から自分自身のフォームウィジェットを作る必要があります。この記事ではその方法と考慮すべき点を、実際のケーススタディと一緒に説明します。
+
JavaScript によるフォームの送信
+
この記事では、標準フォームの送信ではなく、カスタム JavaScript 内で HTTP リクエストを組み立てて送る方法を見ます。またそうしたい理由と、そうする意味も見ていきます(FormData オブジェクトの使用もご覧ください)。
+
+ +
+
フォームコントロール用の CSS プロパティ互換性テーブル
+
この最後の記事では、どの CSS プロパティがフォーム要素に互換性があるのかを引くことのできるハンディリファレンスを提供します。
+
+ +
+
+ +

関連情報

+ + diff --git a/files/ja/learn/forms/other_form_controls/index.html b/files/ja/learn/forms/other_form_controls/index.html new file mode 100644 index 0000000000..6c6f825de4 --- /dev/null +++ b/files/ja/learn/forms/other_form_controls/index.html @@ -0,0 +1,333 @@ +--- +title: その他のフォームコントロール +slug: Learn/Forms/Other_form_controls +tags: + - Beginner + - Controls + - Example + - Forms + - Guide + - HTML + - Web + - Widgets +translation_of: Learn/Forms/Other_form_controls +--- +
{{LearnSidebar}}{{PreviousMenuNext("Learn/Forms/HTML5_input_types","Learn/Forms/Styling_HTML_forms", "Learn/Forms")}}
+ +

ドロップダウンリストや複数行のテキストフィールドから、{{htmlelement('output')}}要素(前の記事で見ました)やプログレスバーといったその他の役立つ機能まで、非-<input> 型のフォーム要素の機能を詳しく見ていきます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーと、HTML の基本的な理解。
目的:非-<input> 型のフォーム機能と、HTML を用いた実装方法の理解。
+ +

複数行のテキストフィールド

+ +

複数行のテキストフィールドは、{{HTMLElement("input")}}要素ではなく{{HTMLElement("textarea")}} 要素で指定されます。

+ +
<textarea cols="30" rows="8"></textarea>
+ +

これは次のように描画されます:

+ +

{{EmbedLiveSample("Multi-line_text_fields", 120, 120)}}

+ +

 <textarea> と通常の単一行テキストフィールドとの主な違いは、ユーザーが送信データの中に改行を (リターンを押すことで) 入れることができます。

+ +

<textarea> は閉じタグを取ることができて、既定のテキストは開始タグと終了タグの間にあるべきです。これに対し、{{HTMLElement("input")}} は閉じタグのない空要素です — value 属性の中に既定の値が入ります。

+ +

注意として、<textarea> 要素 (その他の HTML 要素、CSS、JavaScript を含む)には何でも入れられるものの、この性質により、プレーンテキストコンテンツのように描画されます(非フォームコントロールでの contenteditable を使うと、プレーンテキストの代わりに HTML/リッチコンテンツの API を使用できます)。

+ +

視覚的には、入力テキストは改行されて、既定ではフォームコントロールはリサイズ可能です。モダンブラウザーにはテキストエリアのサイズを増減できるドラッグハンドルがあります。

+ +

下記のスクリーンショットは macOS での Firefox 71 と Safari 13 、Windows10 での Edge 18, Yandex 14, Firefox 71, Chrome 79 における既定の、フォーカスされた、無効な<textarea> 要素を表示しています。

+ +

The default, focused, and disabled <textarea> element in Firefox 71 and Safari 13 on Mac OSX and Edge 18, Yandex 14, Firefox and Chrome on Windows 10.

+ +
+

: 多少は面白いテキストエリアの使用例は、このシリーズの最初の記事の が見つかります (ソースコードも見てください)。

+
+ +

複数行レンダリングの制御

+ +

{{htmlelement("textarea")}} では、複数行にまたがってレンダリングするのを制御する 3 つの属性を受け付けます:

+ +
+
{{htmlattrxref("cols","textarea")}}
+
テキストコントロールの (カラム) 幅を、平均的なキャラクター幅で指定します。これは <textarea>をリサイズすることで変更でき、また CSS で上書きもできるため、有効な開始時の幅です。何も指定されていない場合、デフォルト値は 20 です。
+
{{htmlattrxref("rows","textarea")}}
+
コントロールの行数を指定します。これは <textarea>をリサイズすることで変更でき、また CSS で上書きもできるため、有効な開始時の高さです。何も指定されていない場合、デフォルト値は 2 です。
+
{{htmlattrxref("wrap","textarea")}}
+
コントロールがどのようにテキストを改行するかを制御します。値は soft (デフォルト値)、この値では送信されるテキストは改行されないが、ブラウザーでレンダリングされるテキストは改行される、hard (この値を使うには cols 属性を指定する必要がある)、この値では送信テキストとレンダリングされるテキストの両方が改行される、off、この値では改行が停止、を取ります。
+
+ +

テキストエリアのリサイズの制御

+ +

<textarea> をリサイズできるかは CSS の resize プロパティで制御されます。とりうる値は次の通り:

+ + + +

これがどのように動作するのかのデモは、{{cssxref("resize")}} リファレンスページの最初にあるインタラクティブな例で遊んでみてください。

+ + + +

ドロップダウン コントロールは、ユーザー インターフェイスのスペースをあまり取らずに、ユーザーがさまざまなオプションから選択できるようにするためのシンプルな方法です。HTML には、選択ボックスオートコンプリート ボックスという 2 つの形式のドロップダウン コンテンツがあります。どちらの場合も相互作用は同じです。コントロールを有効にすると、ブラウザーにはユーザーが選択できる値のリストが表示されます。

+ +
+

: すべてのドロップダウンボックスの例は、GitHub の drop-down-content.html にあります (ライブでもご覧ください)。

+
+ +

選択ボックス

+ +

単純なセレクトボックスは、1 つ以上の {{HTMLElement("option")}} 要素を子要素として持つ {{HTMLElement("select")}}要素で作成され、それぞれが可能な値のうちの 1 つを指定します。

+ +
+
<select id="simple" name="simple">
+  <option>Banana</option>
+  <option selected>Cherry</option>
+  <option>Lemon</option>
+</select>
+
+ +

{{EmbedLiveSample("Simple", 120, 120)}}

+ +

必要に応じて、希望する {{HTMLElement("option")}}要素の{{htmlattrxref("selected", "option")}}属性を用いて、セレクトボックスのデフォルト値を設定することができます。
+ - このオプションは、ページが読み込まれたときにあらかじめ選択されています。

+ +

{{HTMLElement("option")}}要素は、{{HTMLElement("optgroup")}}要素の中に入れ子にすることができ、視覚的に関連付けられた値のグループを作成することができます。

+ +
+
<select id="groups" name="groups">
+  <optgroup label="fruits">
+    <option>Banana</option>
+    <option selected>Cherry</option>
+    <option>Lemon</option>
+  </optgroup>
+  <optgroup label="vegetables">
+    <option>Carrot</option>
+    <option>Eggplant</option>
+    <option>Potato</option>
+  </optgroup>
+</select>
+
+ +

{{EmbedLiveSample("groups", 120, 120)}}

+ +

{{HTMLElement("optgroup")}}要素では、label属性の値が入れ子になったオプションの値の前に表示されます。ブラウザーは通常、それらをオプションから視覚的に離して(すなわち太字にしたり、入れ子レベルを変えたりして)表示しますので、実際のオプションと混同される可能性は低くなります。

+ +

{{HTMLElement("option")}}要素に明示的な value属性が設定されている場合、そのオプションが選択された状態でフォームが送信された時にその値が送信されます。上の例のように value属性を省略した場合は、{{HTMLElement("option")}}要素の内容が値として使われます。そのため、value属性は必要ありませんが、セレクトボックスに視覚的に表示されている値とは異なる値を短くしたり、サーバーに送信したい理由があるかもしれません。

+ +

例えば、:

+ +
<select id="simple" name="simple">
+  <option value="banana">Big, beautiful yellow banana</option>
+  <option value="cherry">Succulent, juicy cherry</option>
+  <option value="lemon">Sharp, powerful lemon</option>
+</select>
+ +

デフォルトでは、選択ボックスの高さは、単一の値を表示するのに十分です。オプションの size 属性は、セレクトにフォーカスがない場合に表示されるオプションの数を制御します。

+ +

複数選択の選択ボックス

+ +

デフォルトでは、セレクトボックスは、ユーザーに単一の値を選択 さ せ る だけです。{{HTMLElement("select")}}要素に{{htmlattrxref("multiple", "select")}}属性を追加することで、オペレーティングシステムが提供するデフォルトのメカニズム(例えば、Cmd/Ctrl を押しながらデスクトップ上で複数の値をクリックするなど)を使用して、ユーザーが複数の値を選択できるようにすることができます。

+ +
<select id="multi" name="multi" multiple size="2">
+  <optgroup label="fruits">
+     <option>Banana</option>
+     <option selected>Cherry</option>
+     <option>Lemon</option>
+   </optgroup>
+   <optgroup label="vegetables">
+     <option>Carrot</option>
+     <option>Eggplant</option>
+     <option>Potato</option>
+   </optgroup>
+</select>
+ +

{{EmbedLiveSample("Multiple_choice_select_box", 120, 120)}}

+ +
+

: 複数選択可能なセレクトボックスの場合、セレクトボックスはドロップダウンコンテンツとして値を表示しないことに気づくでしょう - 代わりに、すべての値がリストに一度に表示され、オプションの size属性はウィジェットの高さを決定します。

+
+ +
注: {{HTMLElement("select")}}要素をサポートするすべてのブラウザーは、{{htmlattrxref("multiple", "select")}}属性もサポートしています。
+ +

オートコンプリートのボックス

+ +

You can provide suggested, automatically-completed values for form widgets using 表示する値を指定する {{HTMLElement("option")}} 子要素つきの {{HTMLElement("datalist")}} 要素を使って、フォームウィジェット用のオートコンプリートの提案値を提供できます。  <datalist> には idが必要です。

+ +

データリストは、 id の値が拘束するデータリストの値となる {{htmlattrxref("list","input")}} 属性を用いて、 {{htmlelement("input")}} 要素 (つまりtextemail の入力タイプ) に拘束されます。

+ +
+

データリストがフォームウィジェットに関連づけられると、オプションはユーザーが入力するオートコンプリートテキストに使われます。典型的には、これはユーザーが入力に打ち込んだものにマッチするドロップダウンボックスで表示されます。

+ +

例を見てみましょう。

+ +
<label for="myFruit">What's your favorite fruit?</label>
+<input type="text" name="myFruit" id="myFruit" list="mySuggestion">
+<datalist id="mySuggestion">
+  <option>Apple</option>
+  <option>Banana</option>
+  <option>Blackberry</option>
+  <option>Blueberry</option>
+  <option>Lemon</option>
+  <option>Lychee</option>
+  <option>Peach</option>
+  <option>Pear</option>
+</datalist>
+ +

{{EmbedLiveSample("first_autocomplete", 120, 120)}}

+
+ +

Datalist support and fallbacks

+ +

Almost all browsers support datalist, but if you are still supporting older browsers such as IE versions below 10, there is a trick to provide a fallback:

+ +
<label for="myFruit">What is your favorite fruit? (With fallback)</label>
+<input type="text" id="myFruit" name="fruit" list="fruitList">
+
+<datalist id="fruitList">
+  <label for="suggestion">or pick a fruit</label>
+  <select id="suggestion" name="altFruit">
+    <option>Apple</option>
+    <option>Banana</option>
+    <option>Blackberry</option>
+    <option>Blueberry</option>
+    <option>Lemon</option>
+    <option>Lychee</option>
+    <option>Peach</option>
+    <option>Pear</option>
+  </select>
+</datalist>
+
+ +

{{EmbedLiveSample("Datalist_support_and_fallbacks", 120, 120)}}

+ +

Browsers that support the {{HTMLElement("datalist")}} element will ignore all the elements that are not {{HTMLElement("option")}} elements, with the datalist working as expected. Old browsers that don't support the {{HTMLElement("datalist")}} element will display the label and the select box.

+ +

The following screenshot shows the datalist fallback as rendered in Safari 6:

+ +

Screenshot of the datalist element fallback with Safari on Mac OS

+ +

If you use this fallback, ensure the data for both the <input> and the <select> are collected server-side.

+ +

Less obvious datalist uses

+ +

According to the HTML specification, the {{htmlattrxref("list","input")}} attribute and the {{HTMLElement("datalist")}} element can be used with any kind of widget requiring a user input. This leads to some uses of it that might seem a little non-obvious.

+ +

例えば、in browsers that support {{htmlelement("datalist")}} on range input types, a small tick mark will be displayed above the range for each datalist {{htmlelement("option")}} value. You can see an implementation example of this on the <input type="range"> reference page.

+ +

And browsers that support {{htmlelement('datalist')}}s and <input type="color"> should display a customized palette of colors as the default, while still making the full color palette available.

+ +

In this case, different browsers behave differently from case to case, so consider such uses as progressive enhancement, and ensure they degrade gracefully.

+ +

その他のフォーム機能

+ +

There are a few other form features that are not as obvious as the ones we have already mentioned, but still useful in some situations, so we thought it would be worth giving them a brief mention.

+ +
+

: You can find the examples from this section on GitHub as other-examples.html (see it live also).

+
+ +

メーターとプログレスバー

+ +

Meters and progress bars are visual representations of numeric values.

+ +

Progress

+ +

A progress bar represents a value that changes over time up to a maximum value specified by the {{htmlattrxref("max","progress")}} attribute. Such a bar is created using a {{ HTMLElement("progress")}} element.

+ +
<progress max="100" value="75">75/100</progress>
+ +

{{EmbedLiveSample("Progress", 120, 120)}}

+ +

This is for implementing anything requiring progress reporting, such as the percentage of total files downloaded, or the number of questions filled in on a questionnaire.

+ +

The content inside the {{HTMLElement("progress")}} element is a fallback for browsers that don't support the element and for screen readers to vocalize it.

+ +

Meter

+ +

A meter bar represents a fixed value in a range delimited by {{htmlattrxref("max","meter")}} and {{htmlattrxref("min","meter")}} values. This value is visually rendered as a bar, and to know how this bar looks, we compare the value to some other set values:

+ + + +

All browsers that implement the {{HTMLElement("meter")}} element use those values to change the color of the meter bar:

+ + + +

Such a bar is created using a {{HTMLElement("meter")}} element. This is for implementing any kind of meter, 例えば、a bar showing total space used on a disk, which turns red when it starts to get full.

+ +
<meter min="0" max="100" value="75" low="33" high="66" optimum="50">75</meter>
+ +

{{EmbedLiveSample("Meter", 120, 120)}}

+ +

The content inside the {{HTMLElement("meter")}} element is a fallback for browsers that don't support the element and for assistive technologies to vocalize it.

+ +

Support for {{HTMLElement("progress")}} and {{HTMLElement("meter")}} is fairly good — there is no support in Internet Explorer, but other browsers support it well.

+ +

スキルをテストしよう!

+ +

この記事の終わりまで到達しましたが、最も重要な情報を覚えていますか?次に移る前に、この情報を保持しているか検証するテストがあります — Test your skills: Other controls を見てください。

+ +

まとめ

+ +

最も後のいくつかの記事で見てきたように、利用できるフォーム要素にはいろいろな種類がたくさんあります。一見してすべてを詳しく覚えておく必要はなく、詳細について調べたいだけ、記事に戻ることができます。

+ +

いろいろなフォームコントロールの背後にある HTML をざっと理解したので、それらのスタイル設定について見ていきましょう。

+ +

{{PreviousMenuNext("Learn/Forms/HTML5_input_types","Learn/Forms/Styling_HTML_forms", "Learn/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/property_compatibility_table_for_form_controls/index.html b/files/ja/learn/forms/property_compatibility_table_for_form_controls/index.html new file mode 100644 index 0000000000..c24af4a732 --- /dev/null +++ b/files/ja/learn/forms/property_compatibility_table_for_form_controls/index.html @@ -0,0 +1,2017 @@ +--- +title: フォームウィジェット向けプロパティ実装状況一覧 +slug: Learn/Forms/Property_compatibility_table_for_form_controls +tags: + - Advanced + - CSS + - Forms + - Guide + - HTML + - NeedsUpdate + - Web + - ウェブ + - ガイド + - フォーム + - 高度 +translation_of: Learn/Forms/Property_compatibility_table_for_form_controls +--- +
{{learnsidebar}}
+ +

以下の実装状況一覧表で、 HTML フォーム向けの CSS の対応状況を要約しています。 HTML フォームや CSS の複雑さにより、これらの表は完全なリファレンスであるとはいえません。それでも、何ができて何ができないかの見識を得られるでしょう。また、どうすればよいかを知る助けにもなるでしょう。

+ +

表の見方

+ +

+ +

それぞれのプロパティで取り得る値は 4 種類あります。

+ +
+
Yes
+
プロパティは各ブラウザー間で、一貫してほどよく対応されています。もっとも、特殊なケースでは奇妙な副作用に直面するかもしれません。
+
Partial
+
プロパティは動作しますが、たびたび奇妙な副作用や一貫性のなさに直面するかもしれません。まず副作用について熟知しているのでなければ、これらのプロパティは避けるべきでしょう。
+
No
+
プロパティは動作しない、または頼りにできるほどの一貫性がありません。
+
n.a.
+
当該ウィジェットに対して、そのプロパティは意味がありません。
+
+ +

レンダリング

+ +

それぞれのプロパティで可能なレンダリング方法は 2 種類あります:

+ +
+
N (Normal)
+
プロパティをそのまま適用することを表します。
+
T (Tweaked)
+
下記の追加ルールとともにプロパティを適用することを表します:
+
+ +
* {
+/* これは、WebKit ベースのブラウザーでネイティブのルックアンドフィールを無効にします */
+  -webkit-appearance: none;
+  appearance: none;
+
+/* Internet Explorer用 */
+  background: none;
+}
+ +

実装状況一覧表

+ +

全体的な動作

+ +

全体的なレベルで多くのブラウザーに共通する動作があります:

+ +
+
{{cssxref("border")}}, {{cssxref("background")}}, {{cssxref("border-radius")}}, {{cssxref("height")}}
+
これらのプロパティのいずれかを使用すると、一部のブラウザーでネイティブのルックアンドフィールが部分的に、あるいは全面的に無効になることがあります。これらを使用する際は注意してください。
+
{{cssxref("line-height")}}
+
このプロパティの対応状況は各ブラウザー間で一貫性がありませんので、避けるべきでしょう。
+
{{cssxref("text-decoration")}}
+
このプロパティはフォームウィジェットにおいて Opera が対応していません。
+
{{cssxref("text-overflow")}}
+
Opera、Safari および IE9 ではフォームウィジェットでこのプロパティに対応していません。
+
{{cssxref("text-shadow")}}
+
Opera は {{cssxref("text-shadow")}} をフォームウィジェットで対応していません。また、IE9 はまったく対応していません。
+
+ +

テキストフィールド

+ +

{{htmlelement("input/text", "text")}}, {{htmlelement("input/search", "search")}}, and {{htmlelement("input/password", "password")}} の入力タイプを見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}Partial[1][2]Yes +
    +
  1. WebKit ブラウザーは (主に Mac OSX や iOS で)、検索フィールドでネイティブのルックアンドフィールを使用します。従って、検索フィールドにこのプロパティを適用できるようにするために、-webkit-appearance:none を使用することが必要です。
  2. +
  3. Windows 7 で Internet Explorer 9 は、 background:none が適用されなければ境界を適用しません。
  4. +
+
{{cssxref("border")}}Partial[1][2]Yes +
    +
  1. WebKit ブラウザは (主に Mac OSX や iOS で)、検索フィールドでネイティブのルックアンドフィールを使用します。従って、検索フィールドにこのプロパティを適用できるようにするために、-webkit-appearance:none を使用することが必要です。
  2. +
  3. Windows 7 で Internet Explorer 9 は、background:none が適用されなければ境界を適用しません。
  4. +
+
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}Partial[1][2]Yes +
    +
  1. WebKit ブラウザは (主に Mac OSX や iOS で)、検索フィールドでネイティブのルックアンドフィールを使用します。従って、検索フィールドにこのプロパティを適用できるようにするために、-webkit-appearance:none を使用することが必要です。
  2. +
  3. Windows 7 で Internet Explorer 9 は、background:none が適用されなければ境界を適用しません。
  4. +
+
テキストとフォント
{{cssxref("color")}}[1]YesYes +
    +
  1. {{cssxref("border-color")}} プロパティが設定されていない場合、一部の WebKit ベースブラウザは {{htmlelement("textarea")}} において、フォントだけでなく境界にも {{cssxref("color")}} プロパティを適用します。
  2. +
+
{{cssxref("font")}}YesYes{{cssxref("line-height")}} の注意事項をご覧ください。
{{cssxref("letter-spacing")}}YesYes
{{cssxref("text-align")}}YesYes
{{cssxref("text-decoration")}}PartialPartialOpera に関する注意事項をご覧ください。
{{cssxref("text-indent")}}Partial[1]Partial[1] +
    +
  1. IE9 はこのプロパティを {{htmlelement("textarea")}} のみで、Opera は単一行のテキストフィールドのみで対応しています。
  2. +
+
{{cssxref("text-overflow")}}PartialPartial
{{cssxref("text-shadow")}}PartialPartial
{{cssxref("text-transform")}}YesYes
境界と背景
{{cssxref("background")}}Partial[1]Yes +
    +
  1. WebKit ブラウザは (主に Mac OSX や iOS で)、検索フィールドでネイティブのルックアンドフィールを使用します。従って、検索フィールドにこのプロパティを適用できるようにするために、-webkit-appearance:none を使用することが必要です。Windows 7 で Internet Explorer 9 は、background:none が適用されなければ境界を適用しません。
  2. +
+
{{cssxref("border-radius")}}Partial[1][2]Yes +
    +
  1. WebKit ブラウザは (主に Mac OSX や iOS で)、検索フィールドでネイティブのルックアンドフィールを使用します。従って、検索フィールドにこのプロパティを適用できるようにするために、-webkit-appearance:none を使用することが必要です。Windows 7 で Internet Explorer 9 は、background:none が適用されなければ境界を適用しません。
  2. +
  3. Opera では、明示的に境界が設定されている場合にのみ {{cssxref("border-radius")}} が適用されます。
  4. +
+
{{cssxref("box-shadow")}}NoPartial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
+ +

ボタン

+ +

{{htmlelement("input/button", "button")}}{{htmlelement("input/submit", "submit")}}, and {{htmlelement("input/reset", "reset")}} input types and the {{htmlelement("button")}} 要素を見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}Partial[1]Yes +
    +
  1. Mac OSX や iOS では、WebKit ベースブラウザでこのプロパティが適用されません。
  2. +
+
{{cssxref("border")}}PartialYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}Partial[1]Yes +
    +
  1. Mac OSX や iOS では、WebKit ベースブラウザでこのプロパティが適用されません。
  2. +
+
テキストとフォント
{{cssxref("color")}}YesYes
{{cssxref("font")}}YesYes{{cssxref("line-height")}} の注意事項をご覧ください。
{{cssxref("letter-spacing")}}YesYes
{{cssxref("text-align")}}NoNo
{{cssxref("text-decoration")}}PartialYes
{{cssxref("text-indent")}}YesYes
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}PartialPartial
{{cssxref("text-transform")}}YesYes
境界と背景
{{cssxref("background")}}YesYes
{{cssxref("border-radius")}}Yes[1]Yes[1] +
    +
  1. Opera では、明示的に境界が設定されている場合にのみ {{cssxref("border-radius")}} が適用されます。
  2. +
+
{{cssxref("box-shadow")}}NoPartial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
+ +

数値

+ +

 {{htmlelement("input/number", "number")}} 入力タイプを見てください。フィールドの値の変更に使用するスピンボタンのスタイルを変更するための、標準的な方法はありません。Safari では、スピンボタンがフィールドの外部にあることは知っておくべきです。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}Partial[1]Partial[1] +
    +
  1. Opera ではスピンボタンが拡大されて、フィールドの内容物を隠す場合があります。
  2. +
+
{{cssxref("border")}}YesYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}Partial[1]Partial[1] +
    +
  1. Opera ではスピンボタンが拡大されて、フィールドの内容物を隠す場合があります。
  2. +
+
テキストとフォント
{{cssxref("color")}}YesYes
{{cssxref("font")}}YesYes{{cssxref("line-height")}} の注意事項をご覧ください。
{{cssxref("letter-spacing")}}YesYes
{{cssxref("text-align")}}YesYes
{{cssxref("text-decoration")}}PartialPartial
{{cssxref("text-indent")}}YesYes
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}PartialPartial
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}NoNo +

対応していますが、ブラウザ間で頼りにできるほどの一貫性はありません。

+
{{cssxref("border-radius")}}NoNo
{{cssxref("box-shadow")}}NoNo
+ +

チェックボックスとラジオボタン

+ +

{{htmlelement("input/checkbox", "checkbox")}}{{htmlelement("input/radio", "radio")}} 入力タイプを見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}No[1]No[1] +
    +
  1. 一部のブラウザでは、マージンを追加したりウィジェットを引き伸ばしたりします。
  2. +
+
{{cssxref("height")}}No[1]No[1] +
    +
  1. 一部のブラウザでは、マージンを追加したりウィジェットを引き伸ばしたりします。
  2. +
+
{{cssxref("border")}}NoNo
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}NoNo
テキストとフォント
{{cssxref("color")}}N.A.N.A.
{{cssxref("font")}}N.A.N.A.
{{cssxref("letter-spacing")}}N.A.N.A.
{{cssxref("text-align")}}N.A.N.A.
{{cssxref("text-decoration")}}N.A.N.A.
{{cssxref("text-indent")}}N.A.N.A.
{{cssxref("text-overflow")}}N.A.N.A.
{{cssxref("text-shadow")}}N.A.N.A.
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}NoNo
{{cssxref("border-radius")}}NoNo
{{cssxref("box-shadow")}}NoNo
+ +

セレクトボックス (単一行)

+ +

{{htmlelement("select")}}{{htmlelement("optgroup")}}{{htmlelement("option")}} 要素を見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}Partial[1]Partial[1] +
    +
  1. このプロパティは {{htmlelement("select")}} 要素では問題ありませんが、 {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素では効果がありません。
  2. +
+
{{cssxref("height")}}NoYes
{{cssxref("border")}}PartialYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}No[1]Partial[2] +
    +
  1. プロパティは適用されますが、Mac OSX ではブラウザ間の一貫性がありません。
  2. +
  3. このプロパティは {{htmlelement("select")}} 要素には適用されますが、 {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素では処理に一貫性がありません。
  4. +
+
テキストとフォント
{{cssxref("color")}}Partial[1]Partial[1] +
    +
  1. Mac OSX では、WebKit ベースのブラウザがネイティブウィジェットでこのプロパティに対応せず、また {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素では Opera を含めてまったく対応していません。
  2. +
+
{{cssxref("font")}}Partial[1]Partial[1] +
    +
  1. Mac OSX では、WebKit ベースのブラウザがネイティブウィジェットでこのプロパティに対応せず、また {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素では Opera を含めてまったく対応していません。
  2. +
+
{{cssxref("letter-spacing")}}Partial[1]Partial[1] +
    +
  1. IE9 は {{HTMLElement("select")}}、 {{htmlelement("option")}} および {{htmlelement("optgroup")}} 要素でこのプロパティに対応していません。Mac OSX で WebKit ベースのブラウザは、 {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素でこのプロパティに対応していません。
  2. +
+
{{cssxref("text-align")}}No[1]No[1] +
    +
  1. Windows 7 で IE9 および Mac OSX で WebKit ベースのブラウザは、このウィジェットで本プロパティに対応していません。
  2. +
+
{{cssxref("text-decoration")}}Partial[1]Partial[1] +
    +
  1. Firefox のみがこのプロパティを完全対応しています。 Opera はこのプロパティに全く対応しておらず、また他のブラウザは {{htmlelement("select")}} 要素のみに対応しています。
  2. +
+
{{cssxref("text-indent")}}Partial[1][2]Partial[1][2] +
    +
  1. ほとんどのブラウザは、{{htmlelement("select")}} 要素のみでこのプロパティに対応しています。
  2. +
  3. IE9 はこのプロパティに対応していません。
  4. +
+
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}Partial[1][2]Partial[1][2] +
    +
  1. ほとんどのブラウザは、{{htmlelement("select")}} 要素のみでこのプロパティに対応しています。
  2. +
  3. IE9 はこのプロパティに対応していません。
  4. +
+
{{cssxref("text-transform")}}Partial[1]Partial[1] +
    +
  1. ほとんどのブラウザは、{{htmlelement("select")}}要素のみでこのプロパティに対応しています。
  2. +
+
境界と背景
{{cssxref("background")}}Partial[1]Partial[1] +
    +
  1. ほとんどのブラウザは、{{htmlelement("select")}}要素のみでこのプロパティに対応しています。
  2. +
+
{{cssxref("border-radius")}}Partial[1]Partial[1]
{{cssxref("box-shadow")}}NoPartial[1]
+ +

Firefox では{{htmlelement("select")}} 要素の下矢印を変更する方法はありません。.

+ +

セレクトボックス (複数行)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}YesYes
{{cssxref("border")}}YesYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}Partial[1]Partial[1] +
    +
  1. Opera は {{cssxref("padding-top")}} および {{cssxref("padding-bottom")}} に {{htmlelement("select")}} 要素で対応していません。
  2. +
+
テキストとフォント
{{cssxref("color")}}YesYes
{{cssxref("font")}}YesYes{{cssxref("line-height")}} の注意事項をご覧ください。
{{cssxref("letter-spacing")}}Partial[1]Partial[1] +
    +
  1. IE9 は {{htmlelement("select")}}{{htmlelement("option")}} および {{htmlelement("optgroup")}} 要素でこのプロパティに対応していません。Mac OSX で WebKit ベースのブラウザは、 {{htmlelement("option")}} 要素や {{htmlelement("optgroup")}} 要素でこのプロパティに対応していません。
  2. +
+
{{cssxref("text-align")}}No[1]No[1] +
    +
  1. Windows 7 で IE9 および Mac OSX で WebKit ベースのブラウザは、このウィジェットで本プロパティに対応していません。
  2. +
+
{{cssxref("text-decoration")}}No[1]No[1] +
    +
  1. Firefox および IE9 以上のみが対応しています。
  2. +
+
{{cssxref("text-indent")}}NoNo
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}NoNo
{{cssxref("text-transform")}}Partial[1]Partial[1] +
    +
  1. ほとんどのブラウザは、 {{htmlelement("select")}} 要素のみでこのプロパティに対応します。
  2. +
+
境界と背景
{{cssxref("background")}}YesYes
{{cssxref("border-radius")}}Yes[1]Yes[1] +
    +
  1. Opera では、明示的に境界が設定されている場合にのみ {{cssxref("border-radius")}} が適用されます。
  2. +
+
{{cssxref("box-shadow")}}NoPartial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
+ +

Datalist

+ +

  {{htmlelement("datalist")}} and {{htmlelement("input")}} 要素と list 属性を見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}NoNo
{{cssxref("height")}}NoNo
{{cssxref("border")}}NoNo
{{cssxref("margin")}}NoNo
{{cssxref("padding")}}NoNo
テキストとフォント
{{cssxref("color")}}NoNo
{{cssxref("font")}}NoNo
{{cssxref("letter-spacing")}}NoNo
{{cssxref("text-align")}}NoNo
{{cssxref("text-decoration")}}NoNo
{{cssxref("text-indent")}}NoNo
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}NoNo
{{cssxref("text-transform")}}NoNo
境界と背景
{{cssxref("background")}}NoNo
{{cssxref("border-radius")}}NoNo
{{cssxref("box-shadow")}}NoNo
+ +

ファイルピッカー

+ +

  {{htmlelement("input/file", "file")}} 入力タイプを見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}NoNo
{{cssxref("height")}}NoNo
{{cssxref("border")}}NoNo
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}NoNo
テキストとフォント
{{cssxref("color")}}YesYes
{{cssxref("font")}}No[1]No[1] +
    +
  1. 対応されていますが、ブラウザ間で頼りにできるほどの一貫性はありません。
  2. +
+
{{cssxref("letter-spacing")}}Partial[1]Partial[1] +
    +
  1. ほとんどのブラウザは、このプロパティを選択ボタンにも適用します。
  2. +
+
{{cssxref("text-align")}}NoNo
{{cssxref("text-decoration")}}NoNo
{{cssxref("text-indent")}}Partial[1]Partial[1] +
    +
  1. ウィジェットの外側に左マージンがあるような状態になります。
  2. +
+
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}NoNo
{{cssxref("text-transform")}}NoNo
境界と背景
{{cssxref("background")}}No[1]No[1] +
    +
  1. 対応されていますが、ブラウザ間で頼りにできるほどの一貫性はありません。
  2. +
+
{{cssxref("border-radius")}}NoNo
{{cssxref("box-shadow")}}NoPartial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
+ +

日付選択

+ +

{{htmlelement("input/date", "date")}}{{htmlelement("input/time", "time")}} 入力タイプを見てください。多くのプロパティが対応されていますが、ブラウザー間で頼りにできるほどの一貫性はありません。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}NoNo
{{cssxref("height")}}NoNo
{{cssxref("border")}}NoNo
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}NoNo
テキストとフォント
{{cssxref("color")}}NoNo
{{cssxref("font")}}NoNo
{{cssxref("letter-spacing")}}NoNo
{{cssxref("text-align")}}NoNo
{{cssxref("text-decoration")}}NoNo
{{cssxref("text-indent")}}NoNo
{{cssxref("text-overflow")}}NoNo
{{cssxref("text-shadow")}}NoNo
{{cssxref("text-transform")}}NoNo
境界と背景
{{cssxref("background")}}NoNo
{{cssxref("border-radius")}}NoNo
{{cssxref("box-shadow")}}NoNo
+ +

カラーピッカー

+ +

 {{htmlelement("input/color", "color")}} 入力タイプを見てください。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}No[1]Yes +
    +
  1. Opera はこのプロパティを、select ウィジェットと同じ制限事項のもとに処理します。
  2. +
+
{{cssxref("border")}}YesYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}No[1]Yes +
    +
  1. Opera はこのプロパティを、select ウィジェットと同じ制限事項のもとに処理します。
  2. +
+
テキストとフォント
{{cssxref("color")}}N.A.N.A.
{{cssxref("font")}}N.A.N.A.
{{cssxref("letter-spacing")}}N.A.N.A.
{{cssxref("text-align")}}N.A.N.A.
{{cssxref("text-decoration")}}N.A.N.A.
{{cssxref("text-indent")}}N.A.N.A.
{{cssxref("text-overflow")}}N.A.N.A.
{{cssxref("text-shadow")}}N.A.N.A.
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}No[1]No[1] +
    +
  1. 対応されていますが、ブラウザ間で頼りにできるほどの一貫性はありません。
  2. +
+
{{cssxref("border-radius")}}No[1]No[1]
{{cssxref("box-shadow")}}No[1]No[1]
+ +

メーターとプログレスバー

+ +

{{htmlelement("meter")}}{{htmlelement("progress")}} 要素を見てください:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}YesYes
{{cssxref("border")}}PartialYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}YesPartial[1] +
    +
  1. Chrome は調整された (Tweaked) 状態の要素に {{cssxref("padding")}} プロパティが適用されていると、{{HTMLElement("progress")}} 要素や {{HTMLElement("meter")}} 要素を隠します。
  2. +
+
テキストとフォント
{{cssxref("color")}}N.A.N.A.
{{cssxref("font")}}N.A.N.A.
{{cssxref("letter-spacing")}}N.A.N.A.
{{cssxref("text-align")}}N.A.N.A.
{{cssxref("text-decoration")}}N.A.N.A.
{{cssxref("text-indent")}}N.A.N.A.
{{cssxref("text-overflow")}}N.A.N.A.
{{cssxref("text-shadow")}}N.A.N.A.
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}No[1]No[1] +
    +
  1. 対応されていますが、ブラウザ間で頼りにできるほどの一貫性はありません。
  2. +
+
{{cssxref("border-radius")}}No[1]No[1]
{{cssxref("box-shadow")}}No[1]No[1]
+ +

Range

+ +

{{htmlelement("input/range", "range")}} 入力タイプを見てください。スライダーのつまみのスタイルを変更するための標準的な方法はなく、また Opera ではウィジェットのデフォルトの表示を調整する方法がありません。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}Partial[1]Partial[1] +
    +
  1. Chrome や Opera はウィジェットの周りに余白を追加します。また Windows 7 の Opera では、スライダーのつまみが引き伸ばされます。
  2. +
+
{{cssxref("border")}}NoYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}Partial[1]Yes +
    +
  1. {{cssxref("padding")}} は適用されますが、視覚的な効果はありません。
  2. +
+
テキストとフォント
{{cssxref("color")}}N.A.N.A.
{{cssxref("font")}}N.A.N.A.
{{cssxref("letter-spacing")}}N.A.N.A.
{{cssxref("text-align")}}N.A.N.A.
{{cssxref("text-decoration")}}N.A.N.A.
{{cssxref("text-indent")}}N.A.N.A.
{{cssxref("text-overflow")}}N.A.N.A.
{{cssxref("text-shadow")}}N.A.N.A.
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}No[1]No[1] +
    +
  1. 対応されていますが、ブラウザ間で頼りにできるほどの一貫性はありません。
  2. +
+
{{cssxref("border-radius")}}No[1]No[1]
{{cssxref("box-shadow")}}No[1]No[1]
+ +

画像ボタン

+ +

{{htmlelement("input/image", "image")}} 入力タイプを見てください:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティNT備考
CSS ボックスモデル
{{cssxref("width")}}YesYes
{{cssxref("height")}}YesYes
{{cssxref("border")}}YesYes
{{cssxref("margin")}}YesYes
{{cssxref("padding")}}YesYes
テキストとフォント
{{cssxref("color")}}N.A.N.A.
{{cssxref("font")}}N.A.N.A.
{{cssxref("letter-spacing")}}N.A.N.A.
{{cssxref("text-align")}}N.A.N.A.
{{cssxref("text-decoration")}}N.A.N.A.
{{cssxref("text-indent")}}N.A.N.A.
{{cssxref("text-overflow")}}N.A.N.A.
{{cssxref("text-shadow")}}N.A.N.A.
{{cssxref("text-transform")}}N.A.N.A.
境界と背景
{{cssxref("background")}}YesYes
{{cssxref("border-radius")}}Partial[1]Partial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
{{cssxref("box-shadow")}}Partial[1]Partial[1] +
    +
  1. IE9 はこのプロパティに対応していません。
  2. +
+
+ +

{{PreviousMenu("Learn/HTML/Forms/Advanced_styling_for_HTML_forms", "Learn/HTML/Forms")}}

+ +

このモジュール

+ +

学習コース

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/sending_and_retrieving_form_data/index.html b/files/ja/learn/forms/sending_and_retrieving_form_data/index.html new file mode 100644 index 0000000000..7370945b88 --- /dev/null +++ b/files/ja/learn/forms/sending_and_retrieving_form_data/index.html @@ -0,0 +1,341 @@ +--- +title: フォームデータの送信 +slug: Learn/Forms/Sending_and_retrieving_form_data +tags: + - Beginner + - CodingScripting + - Guide + - HTML + - HTTP + - Web + - セキュリティ + - ファイル + - フォーム +translation_of: Learn/Forms/Sending_and_retrieving_form_data +--- +
{{LearnSidebar}}{{PreviousMenu("Learn/Forms/Form_validation", "Learn/Forms")}}
+ +

フォームがクライアント側で検証されたら、フォームの送信は大丈夫です。前の記事では検証をカバーしたので、送信する準備はできています! この記事では、ユーザーがフォームを送信したときに何が起こるか、つまりデータがどこへ行くのか、そこに来たときにどう扱うのかを見ます。また、フォームデータの送信に関連するセキュリティの考慮事項のいくつかも見てみます。

+ + + + + + + + + + + + +
前提知識:基本的なコンピューターリテラシー, HTML の理解, HTTP およびサーバーサイドプログラミングの基本的な知識。
目標:フォームデータが送信されたら何が起こるかを、データがサーバー上でどのように処理されるかの基本的な考えも含めて理解すること。
+ +

まずは、フォームが送信されたときにデータに何が起こるかを考えてみましょう。

+ +

クライアント/サーバー構成について

+ +

ウェブはごく基本的なクライアント/サーバー構成に基づいており、簡単に言うと次のようになります。クライアント (通常はウェブブラウザー) は HTTP プロトコルを使用して、サーバー (ほとんどの場合 ApacheNginxIISTomcat などのウェブサーバー) にリクエストを送ります。サーバーは同じプロトコルを使用して、リクエストに応答します。

+ +

基本的なクライアント/サーバー構成

+ +

クライアント側において、HTML フォームはサーバーへデータを送信する HTTP リクエストを組み立てるのための、便利でユーザーに使いやすい手段でしかありません。フォームによって、ユーザーが HTTP リクエストで渡す情報を提供することができるようになります。

+ +
+

メモ: どのようにクライアント/サーバー構成が動作するかについてもっと知りたい場合は、サーバーサイド Web サイトプログラミング入門モジュールをお読みください。

+
+ +

クライアント側: データ送信方法の定義

+ +

{{HTMLElement("form")}} 要素で、データを送信する方法を定義します。その属性すべてが、ユーザーが送信ボタンを押すと送信されるリクエストを調整できるように設計されています。もっとも重要な属性は {{htmlattrxref("action","form")}} と {{htmlattrxref("method","form")}} の 2 つです。

+ +

action 属性

+ +

action 属性は、どこにデータを送信するかを定義します。値は妥当な相対/絶対 URL でなければなりません。この属性が与えられなかった場合は、フォームが含まれているページの URL にデータが送信されます。

+ +

この例では、データを絶対 URL の http://example.com に送信します。

+ +
<form action="http://example.com">
+ +

こちらは、相対 URL を使用しています。データは同一オリジン上の別の URL に送信されます。

+ +
<form action="/somewhere_else">
+ +

以下のように属性を指定しない場合は、{{HTMLElement("form")}} 要素はフォームが表示されているページ自身に対してデータを送信します。

+ +
<form>
+ +
+

メモ: HTTPS (secure HTTP) プロトコルを使用して URL を指定することができます。このようにすると、フォーム自体が HTTP でアクセスされる安全ではないページで提供される場合でも、データはリクエストの残りの部分とともに暗号化されます。一方、フォームが安全なページ提供されていても、{{htmlattrxref("action","form")}} 属性で安全ではない HTTP の URL を指定すると、どのブラウザーでもデータを送信する際にユーザーに対してセキュリティの警告を表示します。これは、データが暗号化されないためです。

+
+ +

非ファイル型のフォームコントロールの名前/値は&記号で結合された name=value ペアでサーバーに送られます。action の値は、サーバー側の検証を含め入力データを扱うサーバーのファイルです。サーバーは応答して、一般的にはデータを処理して action 属性で定義された URL を読み込み、新しいページの読み込み (または action が同じページを指している場合は既存ページのリフレッシュ)を引き起こします。

+ +

データがどう送られるかは method 属性に依存します。

+ +

method 属性

+ +

method 属性は、どのようにデータを送信するかを定義します。HTTP プロトコルはリクエストを実行するための方法をいくつか提供しています。HTML フォームのデータは複数の方法で送信することができます。もっとも一般的なものは GET メソッドと POST メソッドです。

+ +

これら2つのメソッドの違いを理解するために、一歩戻って HTTP の動作についてみていきましょう。ウェブ上のリソースにたどり着こうとするたびに、ブラウザーは URL へリクエストを送信します。HTTP リクエストは 2 つの部分で構成されます。ブラウザーの機能に関する包括的なメタデータのセットを持つヘッダーと、指定されたリクエストをサーバーが処理するために必要な情報を持つ本文です。

+ +

GET メソッド

+ +

GET メソッドは、サーバーに対して指定したリソースを返すよう求めるためにブラウザーが使用するメソッドです。"やあサーバー、このリソースをくれよ。" この場合、ブラウザーは空の本文を送信します。本文が空であるため、フォームをこのメソッドで送信する場合はデータを URL の後に付加します。

+ +

以下のフォームについて考えてみましょう。

+ +
<form action="http://foo.com" method="get">
+  <div>
+    <label for="say">What greeting do you want to say?</label>
+    <input name="say" id="say" value="Hi">
+  </div>
+  <div>
+    <label for="to">Who do you want to say it to?</label>
+    <input name="to" id="to" value="Mom">
+  </div>
+  <div>
+    <button>Send my greetings</button>
+  </div>
+</form>
+ +

GET メソッドが使用されているので、フォームを送信するときにブラウザーのアドレスバーに www.foo.com/?say=Hi&to=Mom という URL が見えるでしょう。

+ +

URL に追加されたデータは名前/値の組の連続です。URL の Web アドレスが終了した後、疑問符 (?) に続いて、名前/値の組が、それぞれアンパサンド (&) で区切られて入ります。この場合、2 つのデータの断片がサーバーに渡されます。

+ + + +

HTTP リクエストは次のようになります。

+ +
GET /?say=Hi&to=Mom HTTP/1.1
+Host: foo.com
+ +
+

メモ: この例は GitHub にあります。— get-method.html を参照してください (ライブはこちら).

+
+ +

POST メソッド

+ +

POST メソッドは少し異なります。これは、HTTP リクエストの本文で提供したデータを考慮したレスポンスの要求を、ブラウザーがサーバーに送信するためのメソッドです。"やあサーバー、このデータを見て適切な結果を返してよ。" このメソッドを使用してフォームを送信する場合は、データが HTTP リクエストの本文の後に追加されます。

+ +

例を見てみましょう。— これは前述の GET の節で見たものと同じフォームですが、{{htmlattrxref("method","form")}} 属性が post に設定されています。

+ +
<form action="http://foo.com" method="post">
+  <div>
+    <label for="say">What greeting do you want to say?</label>
+    <input name="say" id="say" value="Hi">
+  </div>
+  <div>
+    <label for="to">Who do you want to say it to?</label>
+    <input name="to" id="to" value="Mom">
+  </div>
+  <div>
+    <button>Send my greetings</button>
+  </div>
+</form>
+ +

フォームをが POST メソッドで送信されると、URL にはデータが追加されず、HTTP リクエストは次のように、リクエスト本文にデータが含まれた形になります。

+ +
POST / HTTP/1.1
+Host: foo.com
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 13
+
+say=Hi&to=Mom
+ +

Content-Length ヘッダーは本文の長さを、また Content-Type ヘッダーはサーバーに送信するリソースの種類を表します。これらのヘッダーについて少し説明しましょう。

+ +
+

メモ: この例は GitHub で見つけることができます。— post-method.html を参照してください (ライブ版も見てください)。

+
+ +

HTTP リクエストの表示

+ +

当然ながら HTTP リクエストはユーザーには表示されません (見たいのであれば、Firefox ネットワークモニターChrome デベロッパー ツールなどのツールが必要です)。例のように、フォームのデータは Chrome の Network タブに以下のように表示されます。フォームの送信後に、以下のように操作してください。

+ +
    +
  1. 開発者ツールを開く
  2. +
  3. "Network" を選択
  4. +
  5. "All" を選択
  6. +
  7. "Name" タブから "foo.com" を選択
  8. +
  9. "Headers" を選択
  10. +
+ +

これで下の画像にあるように、フォームデータを取得することができます。

+ +

+ +

ユーザーに表示されるのは呼び出された URL のみです。前述のように、GET リクエストはユーザーが URL バーの中でデータを見ることができますが、POST リクエストではそうではありません。これは 2 つの理由でとても重要です。

+ +
    +
  1. パスワード (あるいは何らかの機密データ) を送信する必要がある場合は、GET メソッドを使用してはいけません。データが URL バーに表示されるリスクがあり、とても危険です。
  2. +
  3. 大量のデータを送信する必要があるなら、POST が好ましいメソッドです。これは、URL の長さ制限があるブラウザーが存在するためです。加えて、多くのサーバーは受け入れる URL の長さを制限しています。
  4. +
+ +

サーバー側: データの取得

+ +

どちらの HTTP メソッドを選択しても、サーバーが受け取る文字列は、キー/値の組のリストとしてデータを取得するために解析されます。このリストにアクセスする方法は、使用する開発プラットフォームや、使用するであろう特定のフレームワークに依存します。

+ +

PHP の例

+ +

PHP は、データにアクセスするためのグローバルオブジェクトを提供します。POST メソッドを使用したと仮定すると、データを取得してユーザーに表示する例は以下のとおりです。もちろん、データに対して何をするかはあなた次第です。データを表示したり、データベースに保管したり、メールで送信したり、他の手段で処理したりするでしょう。

+ +
<?php
+  // $_POST グローバル変数は、POST メソッドで送信されたデータへ名前でアクセスを可能にする
+  // GET メソッドで送信されたデータにアクセスするには、$_GET が使用できる
+  $say = htmlspecialchars($_POST['say']);
+  $to  = htmlspecialchars($_POST['to']);
+
+  echo  $say, ' ', $to;
+?>
+ +

この例では送信されたデータを含むページを表示します。これはサンプルの php-example.html ファイル、つまり以前 methodPOSTactionphp-example.php の時に見たサンプルフォームを含むファイルアクションの中で見ることができます。送信されると、フォームデータは上記のブロックの PHP コードを含む php-example.php へ送信されます。コードが実行されると、ブラウザーの出力は Hi Mom になります。

+ +

+ +
+

メモ: この例はブラウザーにローカルに読み込んだ時には動作しません。— ブラウザーは PHP コードを解釈できないので、フォームがブラウザーに送信されると、PHP ファイルをダウンロードしようとするでしょう。動作させるためには、この例を何らかの PHP サーバー経由で実行する必要があります。ローカルの PHP のテストには、MAMP (Mac および Windows) や AMPPS (Mac, Windows, Linux) がいいでしょう。

+ +

MAMP を使って MAMP Pro がない (または MAMP Pro デモトライアルの有効期限が切れた)場合、動作させるのにトラブルが起こるかもしれません。再び動作させるには MAMP アプリを閉じて、MAMP > Preferences > PHPメニューから "Standard Version:" を "7.2.x" (x はあなたがどのバージョンをインストールしたかによります)にするといいことがわかっています。

+
+ +

Python の例

+ +

この例は、同じこと (与えられたデータをウェブページに表示する) を Python で行います。これはテンプレートの表示やフォームデータの受付などのために Flask フレームワークを使用しています (python-example.py を参照してください)。

+ +
from flask import Flask, render_template, request
+app = Flask(__name__)
+
+@app.route('/', methods=['GET', 'POST'])
+def form():
+    return render_template('form.html')
+
+@app.route('/hello', methods=['GET', 'POST'])
+def hello():
+    return render_template('greeting.html', say=request.form['say'], to=request.form['to'])
+
+if __name__ == "__main__":
+    app.run()
+ +

次のように、上記のコードでは 2 つのテンプレートが参照されます。(自分の環境で実行する場合、これらは templates というサブディレクトリにあり、python-example.pyファイルと同じディレクトリにある必要があります):

+ + + +
+

メモ: 繰り返しますが、このコードはブラウザーに直接読み込もうとしても動作しません。Python は PHP とは若干異なる動作をします。— ローカルでこのコードを実行するには、Python/PIP をインストールする必要があり、それから pip3 install flask を使用して Flask をインストールしてください。この時点で python3 python-example.py を実行し、ブラウザーで localhost:5000 に移動することで実行することができるでしょう。

+
+ +

その他の言語やフレームワーク

+ +

フォームの操作に使用できるサーバー側の技術は、Perl、Java、.Net、Ruby などたくさんあります。もっとも好きなものを選びましょう。しかしそれらの技術を直接使用することは、扱いにくいため一般的ではないことが特筆に値します。以下のような、フォームをより簡単に扱えるようにする多くの高品質フレームワークのひとつを使用する方がより一般的です:

+ + + +

言うまでもなく、これらのフレームワークを使用したとしても、フォームでの作業が必ずしも簡単になるとは限りません。しかし、すべての機能を自分で 1 から書こうとするよりずっと簡単で、また多くの時間を節約できるでしょう。

+ +
+

メモ: サーバー側言語やフレームワークまで説明することはこの記事の範囲を超えます。上記のリンクが参考になりますので、学習してみてください。

+
+ +

特別な場合: ファイル送信

+ +

ファイルは HTML フォームで特別なケースです。他のデータがすべてテキストデータである中、ファイルはバイナリーデータ (あるいはそのように考えられるデータ) です。HTTP はテキストのプロトコルであるため、バイナリーデータを扱うための特別な要件があります。

+ +

enctype 属性

+ +

この属性で Content-Type HTTP ヘッダーの値を指定できます。このヘッダーはサーバーに対して送信するデータの種類を伝えることから、とても重要です。既定値は application/x-www-form-urlencoded です。人間の言葉では、「これは URL 形式でエンコードされたフォームデータです。」という意味です。

+ +

しかしファイルを送信したい場合は、さらに 2 つのステップを踏む必要があります。

+ + + +

例:

+ +
<form method="post" enctype="multipart/form-data">
+  <div>
+    <label for="file">Choose a file</label>
+    <input type="file" id="file" name="myFile">
+  </div>
+  <div>
+    <button>Send the file</button>
+  </div>
+</form>
+ +
+

警告: 多くのサーバーは悪用を防ぐために、ファイルや HTTP リクエストの長さを制限しています。

+
+ +

一般的なセキュリティへの配慮

+ +

サーバーにデータを送信するたびに、セキュリティについて考える必要があります。HTML フォームはサーバーに対するもっともよくある攻撃の入口 (攻撃が行われる場所) になります。問題が HTML フォーム自身から発生することはありません — サーバーがどのようにデータを扱うかによります。

+ +

server-side の学習トピックの Website security の記事では、一般的な攻撃とその防御を詳細に扱っています。そちらへ行って記事を確認し、何が起こり得るかを理解してください。

+ +

疑い深くあれ: ユーザーを信用してはいけません

+ +

さて、これらの脅威に対してどう対抗するのでしょうか? これは本ガイドの内容を超える話題です。それでも、覚えておくとよいルールがいくつかあります。もっとも重要なルールは、自分自身も含めユーザーを決して信用してはならないことです。信頼されているユーザーでさえハイジャックされるかもしれません。

+ +

サーバーに来るすべてのデータを確認およびサニタイズしなければなりません。いつでもです。例外はありません。

+ + + +

これら 3 つのルールに従うと、多くのあるいはほとんどの問題を避けられるでしょう。ただし、適格の第三者によるセキュリティレビューを受けることもよい考えです。発生し得る問題のすべてを見いだしたとは考えないようにしてください。

+ +

まとめ

+ +

ご覧いただいたように、フォームデータの送信は簡単ですが、アプリケーションを安全にするのは容易ではありません。フロントエンドの開発者はデータのセキュリティモデルを定義すべき者ではないことを忘れないようにしてください。今後見ていくようにクライアント側でのデータ検証も可能ですが、クライアント側で実際に何が起きているかを知ることはできませんので、サーバー側でその検証内容を信用することはできません。

+ +

このチュートリアルを順番に終えた場合、フォームのマークアップとスタイル設定の方法、クライアント側での検証の方法、フォーム送信の理解ができているでしょう。

+ +

関連情報

+ +

ウェブアプリケーションのセキュア化についてさらに学びたいのでしたら、次のリソースをよく読んでください。

+ + + +

{{PreviousMenuNext("Learn/HTML/Forms/The_native_form_widgets", "Learn/HTML/Forms/Form_validation", "Learn/HTML/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/sending_forms_through_javascript/index.html b/files/ja/learn/forms/sending_forms_through_javascript/index.html new file mode 100644 index 0000000000..3b3ba71ce2 --- /dev/null +++ b/files/ja/learn/forms/sending_forms_through_javascript/index.html @@ -0,0 +1,396 @@ +--- +title: JavaScript によるフォームの送信 +slug: Learn/Forms/Sending_forms_through_JavaScript +tags: + - Advanced + - Example + - Forms + - Forms Guide + - Guide + - HTML + - HTML forms + - JavaScript + - Learn + - Security + - Web + - Web Forms +translation_of: Learn/Forms/Sending_forms_through_JavaScript +--- +
{{LearnSidebar}}
+ +

HTML フォームは HTTP リクエストを宣言的に送信できます。しかし、フォームは 、例えば XMLHttpRequest のように JavaScript 経由で送信する HTTP リクエストを準備することもできます。この記事ではその方法を探ります。

+ +

フォームは必ずしもフォームであるとは限らない

+ +

PWA や SPA やフレームワークベースのアプリが現れたことで、HTML フォームを、応答データを受け取ったときに、新しい文書を読み込むことなくデータ送信するのに使用することが次第に一般的になってきました。最初になぜいろいろなアプローチが必要となるのかを話しましょう。

+ +

グローバルインターフェイスの制御を取得

+ +

次の記事で述べる標準の HTML フォーム送信では、データが送信された URL がロードされます。つまり、ブラウザーウィンドウは全ページロードで移動します。ページ全体の読み込みを回避すると、ちらつきのような視覚上の問題や、ネットワークの遅延を避けて、よりスムーズな操作を提供できます。

+ +

最近の多くの UI は、HTML フォームを使用してユーザーからの入力を収集します。ユーザーがデータを送信しようとすると、アプリケーションはバックグラウンドでデータを非同期的に制御して送信し、変更が必要な UI の部分のみを更新します。

+ +

任意のデータを非同期に送信することは、"Asynchronous JavaScript And XML" を表す頭字語である AJAX として知られています。

+ +

その違いは?

+ +

{{domxref("XMLHttpRequest")}} (XHR) DOM オブジェクトで HTTP リクエストを作成して送信し、結果を取得することができます。歴史的には、{{domxref("XMLHttpRequest")}} は交換フォーマットとして XML を取得して送信するように設計されていました。しかし、JSON は XML に取って代わっています。しかし、XML も JSON もフォームデータリクエストのエンコーディングには適合しません。フォームデータ (application/x-www-form-urlencoded) は、キーと値のペアの URL エンコードされたリストで構成されています。バイナリーデータを送信するために、HTTP リクエストは multipart/form-data に再形成されます

+ +
+

注記: Fetch API は最近 XHR の代わりによく使われます — これは XHR のモダンで更新されたバージョンであり、同様に動作しますが利点もあります。この記事で見る大半の XHR コードは Fetch で置き換えられます。

+
+ +

フロントエンド (ブラウザーで実行されるコード) とバックエンド (サーバーで実行されるコード) を制御すれば、JSON/XML を送信して必要に応じて処理することができます。

+ +

しかし、サードパーティのサービスを利用したい場合、サービスが要求する零式でデータ送信する必要があります。

+ +

ではどのようにしてそのようなデータを送信するのでしょうか?以下に必要となるさまざまなテクニックがあります。

+ +

フォームデータの送信

+ +

フォームデータを送信するには、従来の方法から新しい {{domxref("FormData")}} オブジェクトまで 3 つの方法があります。それらを詳しく見てみましょう。

+ +

手作業での XMLHttpRequest の作成

+ +

{{domxref("XMLHttpRequest")}} は、HTTP リクエストを作成する最も安全で信頼性の高い方法です。{{domxref("XMLHttpRequest")}} を使用してフォームデータを送信するには、URL エンコードしたデータを準備し、フォームデータリクエストの詳細に従ってください。

+ +

例を見てみましょう:

+ +
<button>Click Me!</button>
+ +

JavaScript はこうです:

+ +
const btn = document.querySelector('button');
+
+function sendData( data ) {
+  console.log( 'Sending data' );
+
+  const XHR = new XMLHttpRequest();
+
+  let urlEncodedData = "",
+      urlEncodedDataPairs = [],
+      name;
+
+  // data オブジェクトを、URL エンコードしたキーと値のペアの配列に変換します
+  for( name in data ) {
+    urlEncodedDataPairs.push( encodeURIComponent( name ) + '=' + encodeURIComponent( data[name] ) );
+  }
+
+ // キーと値のペアをひとつの文字列に連結して、Web ブラウザーのフォーム送信方式に
+ // 合うよう、エンコードされた空白をプラス記号に置き換えます。
+  urlEncodedData = urlEncodedDataPairs.join( '&' ).replace( /%20/g, '+' );
+
+  // データが正常に送信された場合に行うことを定義します
+  XHR.addEventListener( 'load', function(event) {
+    alert( 'Yeah! Data sent and response loaded.' );
+  } );
+
+  // エラーが発生した場合に行うことを定義します
+  XHR.addEventListener( 'error', function(event) {
+    alert( 'Oops! Something went wrong.' );
+  } );
+
+  // リクエストをセットアップします
+  XHR.open( 'POST', 'https://example.com/cors.php' );
+
+  // フォームデータの POST リクエストを扱うために必要な HTTP ヘッダを追加します
+  XHR.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
+
+  // 最後に、データを送信します
+  XHR.send( urlEncodedData );
+}
+
+btn.addEventListener( 'click', function() {
+  sendData( {test:'ok'} );
+} )
+
+ +

そして、結果は以下のとおりです:

+ +

{{EmbedLiveSample("Building_an_XMLHttpRequest_manually", "100%", 50)}}

+ +
+

注記: この {{domxref("XMLHttpRequest")}} の使用は、第三者の Web サイトにデータを送信したい場合にも、同一生成元ポリシーの対象となります。クロスオリジンリクエストの場合は、CORS と HTTP のアクセス制御が必要です。

+
+ +

XMLHttpRequest と FormData オブジェクトの使用

+ +

HTTP リクエストを手作業で作成するのは大変なことです。幸いなことに、最近の XMLHttpRequest 仕様では {{domxref("FormData")}} オブジェクトを使ってフォームデータリクエストを処理する便利で簡単な方法が提供されています。

+ +

{{domxref("FormData")}} オブジェクトは、送信用のフォームデータを作成したり、送信方法を管理するフォーム要素内のデータを取得するために使用できます。{{domxref("FormData")}} オブジェクトは "書き込み専用" であることに注意してください。つまり、変更することはできますが、内容を取得することはできません。

+ +

このオブジェクトの使い方は FormData オブジェクトの使用で詳述されていますが、2 つの例があります。

+ +

独立した FormData オブジェクトを使用する

+ +
<button type="button" onclick="sendData({test:'ok'})">Click Me!</button>
+ +

HTML のサンプルはおわかりでしょう。JavaScript はこうです。

+ +
const btn = document.querySelector('button');
+
+function sendData(data) {
+  const XHR = new XMLHttpRequest(),
+        FD  = new FormData();
+
+  // データを FormData オブジェクトに投入します
+  for(name in data) {
+    FD.append(name, data[name]);
+  }
+
+  // データが正常に送信された場合に行うことを定義します
+  XHR.addEventListener('load', function(event) {
+    alert('Yeah! Data sent and response loaded.');
+  });
+
+  // エラーが発生した場合に行うことを定義します
+  XHR.addEventListener('error', function(event) {
+    alert('Oups! Something goes wrong.');
+  });
+
+  // リクエストをセットアップします
+  XHR.open('POST', 'http://ucommbieber.unl.edu/CORS/cors.php');
+
+  // FormData オブジェクトを送信するだけです。HTTP ヘッダは自動的に設定されます
+  XHR.send(FD);
+}
+ +

そして、結果は以下のとおりです:

+ +

{{EmbedLiveSample("Using_a_standalone_FormData_object", "100%", 50)}}

+ +

form 要素に紐づけた FormData を使用する

+ +

FormData オブジェクトを {{HTMLElement("form")}} 要素に紐づけることもできます。これにより、フォームに含まれるデータを表す FormData をすばやく得ることができます。

+ +

HTML の部分はかなり典型的です:

+ +
<form id="myForm">
+  <label for="myName">Send me your name:</label>
+  <input id="myName" name="name" value="John">
+  <input type="submit" value="Send Me!">
+</form>
+ +

しかし、JavaScript がフォームを乗っ取ります。

+ +
window.addEventListener("load", function () {
+  function sendData() {
+    const XHR = new XMLHttpRequest();
+
+    // FormData オブジェクトと form 要素を紐づけます
+    const FD  = new FormData(form);
+
+    // データが正常に送信された場合に行うことを定義します
+    XHR.addEventListener("load", function(event) {
+      alert(event.target.responseText);
+    });
+
+    // エラーが発生した場合に行うことを定義します
+    XHR.addEventListener("error", function(event) {
+      alert('Oups! Something goes wrong.');
+    });
+
+    // リクエストをセットアップします
+    XHR.open("POST", "http://ucommbieber.unl.edu/CORS/cors.php");
+
+    // 送信したデータは、ユーザーがフォームで提供したものです
+    XHR.send(FD);
+  }
+
+  // form 要素にアクセスしなければなりません
+  const form = document.getElementById("myForm");
+
+  // フォームの submit イベントを乗っ取ります
+  form.addEventListener("submit", function (event) {
+    event.preventDefault();
+
+    sendData();
+  });
+});
+ +

そして、結果は以下のとおりです:

+ +

{{EmbedLiveSample("Using_FormData_bound_to_a_form_element", "100%", 50)}}

+ +

フォームの {{domxref("HTMLFormElement.elements", "elements")}} プロパティを使用してフォーム内のすべてのデータ要素のリストを取得し、それらを一度に 1 つずつ手動で管理することで、このプロセスにさらに関わることができます。詳細については、{{SectionOnPage("/ja/docs/Web/API/HTMLFormElement.elements", "要素リストの内容にアクセスする")}}の例を参照してください。

+ +

バイナリーデータを扱う

+ +

<input type="file"> ウィジェットを含むフォームで {{domxref("FormData")}} オブジェクトを使用すると、データは自動的に処理されます。しかし、バイナリーデータを手動で送るには、追加でやるべきことがあります。

+ +

現代の Web には、バイナリーデータのソースが多数あります。たとえば、{{domxref("FileReader")}}、{{domxref("HTMLCanvasElement","Canvas")}}、WebRTC などです。残念ながら、一部の従来のブラウザーではバイナリーデータにアクセスできないか、または複雑な回避策が必要です。これらのレガシーケースはこの記事の範囲外です。FileReader API について詳しく知りたい場合は、Web アプリケーションからファイルを扱うを読んでください。

+ +

{{domxref("FormData")}} をサポートするバイナリーデータを送信するのは簡単です。append() メソッドを使用すれば完了です。手動でやらなければならないならば、それはトリッキーです。

+ +

以下の例ではバイナリーデータへのアクセスに {{domxref("FileReader")}} API を使用しており、また手作業でマルチパートのフォームデータを作成しています:

+ +
<form id="myForm">
+  <p>
+    <label for="i1">text data:</label>
+    <input id="i1" name="myText" value="Some text data">
+  </p>
+  <p>
+    <label for="i2">file data:</label>
+    <input id="i2" name="myFile" type="file">
+  </p>
+  <button>Send Me!</button>
+</form>
+ +

ご覧のとおり、HTML は標準の <form> です。不思議なところは何もありません。「魔法」は JavaScript にあります。

+ +
// DOM ノードにアクセスしたいため、
+// ページをロードしたときにスクリプトを初期化します。
+window.addEventListener('load', function () {
+
+  // この変数は、フォームデータを格納するために使用します。
+  const text = document.getElementById("i1");;
+  const file = {
+        dom    : document.getElementById("i2"),
+        binary : null
+      };
+
+  // ファイルコンテンツへのアクセスに FileReader API を使用します。
+  const reader = new FileReader();
+
+  // FileReader API は非同期であるため、ファイルの読み取りが完了したときに
+  // その結果を保存しなければなりません。
+  reader.addEventListener("load", function () {
+    file.binary = reader.result;
+  });
+
+  // ページを読み込んだとき、すでに選択されているファイルがあればそれを読み取ります。
+  if(file.dom.files[0]) {
+    reader.readAsBinaryString(file.dom.files[0]);
+  }
+
+  // 一方、ユーザーがファイルを選択したらそれを読み取ります。
+  file.dom.addEventListener("change", function () {
+    if(reader.readyState === FileReader.LOADING) {
+      reader.abort();
+    }
+
+    reader.readAsBinaryString(file.dom.files[0]);
+  });
+
+  // sendData 関数がメインの関数です。
+  function sendData() {
+    // 始めに、ファイルが選択されている場合はファイルの読み取りを待たなければなりません。
+    // そうでない場合は、関数の実行を遅延させます。
+    if(!file.binary && file.dom.files.length > 0) {
+      setTimeout(sendData, 10);
+      return;
+    }
+
+    // マルチパートのフォームデータリクエストを構築するため、
+    // XMLHttpRequest のインスタンスが必要です。
+    const XHR      = new XMLHttpRequest();
+
+    // リクエストの各パートを定義するためのセパレータが必要です。
+    const boundary = "blob";
+
+    // 文字列としてリクエストのボディを格納します。
+    let data     = "";
+
+    // そして、ユーザーがファイルを選択したときに
+    if (file.dom.files[0]) {
+      // リクエストのボディに新たなパートを作ります
+      data += "--" + boundary + "\r\n";
+
+      // フォームデータであることを示します (他のものになる場合もあります)
+      data += 'content-disposition: form-data; '
+      // フォームデータの名前を定義します
+            + 'name="'         + file.dom.name          + '"; '
+      // 実際のファイル名を与えます
+            + 'filename="'     + file.dom.files[0].name + '"\r\n';
+      // ファイルの MIME タイプを与えます
+      data += 'Content-Type: ' + file.dom.files[0].type + '\r\n';
+
+      // メタデータとデータの間に空行を置きます
+      data += '\r\n';
+
+      // リクエストのボディにバイナリーデータを置きます
+      data += file.binary + '\r\n';
+    }
+
+    // テキストデータの場合はシンプルです。
+    // リクエストのボディに新たなパートを作ります
+    data += "--" + boundary + "\r\n";
+
+    // フォームデータであることと、データの名前を示します。
+    data += 'content-disposition: form-data; name="' + text.name + '"\r\n';
+    // メタデータとデータの間に空行を置きます
+    data += '\r\n';
+
+    // リクエストのボディにテキストデータを置きます。
+    data += text.value + "\r\n";
+
+    // 完了したら、リクエストのボディを "閉じます"。
+    data += "--" + boundary + "--";
+
+    // データが正常に送信された場合に行うことを定義します
+    XHR.addEventListener('load', function(event) {
+      alert('Yeah! Data sent and response loaded.');
+    });
+
+    // エラーが発生した場合に行うことを定義します
+    XHR.addEventListener('error', function(event) {
+      alert('Oups! Something goes wrong.');
+    });
+
+    // リクエストをセットアップします
+    XHR.open('POST', 'http://ucommbieber.unl.edu/CORS/cors.php');
+
+    // マルチパートのフォームデータの POST リクエストを扱うために必要な HTTP ヘッダを追加します。
+    XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);
+    XHR.setRequestHeader('Content-Length', data.length);
+
+    // 最後に、データを送信します
+    // Firefox のバグ 416178 により、send() の代わりに sendAsBinary() を使用することが必要です。
+    XHR.sendAsBinary(data);
+  }
+
+  // 少なくとも、フォームにアクセスしなければなりません。
+  const form   = document.getElementById("myForm");
+
+  // submit イベントを乗っ取ります。
+  form.addEventListener('submit', function (event) {
+    event.preventDefault();
+    sendData();
+  });
+});
+ +

そして、結果は以下のとおりです:

+ +

{{EmbedLiveSample("Dealing_with_binary_data", "100%", 150)}}

+ +

まとめ

+ +

ブラウザーや扱うデータタイプによっては、JavaScript を介してフォームデータを送信するのが簡単な場合と難しい場合があります。{{domxref("FormData")}} オブジェクトが一般的な答えであり、レガシーブラウザーで polyfill を使用することをためらってはいけません。

+ +

このモジュール

+ +

学習コース

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/styling_html_forms/index.html b/files/ja/learn/forms/styling_html_forms/index.html new file mode 100644 index 0000000000..4634665cef --- /dev/null +++ b/files/ja/learn/forms/styling_html_forms/index.html @@ -0,0 +1,398 @@ +--- +title: HTML フォームへのスタイル設定 +slug: Learn/Forms/Styling_HTML_forms +tags: + - CSS + - Example + - Forms + - Guide + - HTML + - Intermediate + - Web +translation_of: Learn/Forms/Styling_web_forms +--- +

{{LearnSidebar}}{{PreviousMenuNext("Learn/Forms/Other_form_controls","Learn/Forms/Advanced_form_styling","Learn/Forms")}}

+ +

前の記事ではウェブフォームを構築するのに必要な HTML のすべてを見てきました。この記事ではフォームコントロールにスタイル設定する CSS の使い方に進みます。これは歴史的に難しかったです — フォームコントロールは大きく変わり CSS を使ったフォームのカスタマイズは簡単になりました— しかし古いブラウザーが引退してモダンブラウザーが多くの機能を与えるため、より簡単になりました。

+ + + + + + + + + + + + +
前提条件: +

基本的なコンピューターリテラシーと、HTMLCSS の基本的な理解。

+
目的:フォームのスタイル設定の問題を理解し、役立つスタイル付けのテクニックを学ぶこと。
+ +

なぜ CSS によるフォームウィジェットへのスタイル設定は困難であるか?

+ +

1995年頃に HTML 2 仕様へフォームコントロールが追加されました。CSS は 1996年までリリースされず、その後も少しのブラウザーによって十分サポートされませんでした。ブラウザーはフォームコントロールの管理や表示について下層の OS に頼ることを選択しました。

+ +

CSS が HTML のスタイル設定できるようになってからも、ユーザーは各プラットフォームの視覚的な外見に慣れていましたので、ブラウザーベンダーはフォームコントロールをスタイル付け可能にすることに乗り気ではありませんでした。しかしこれは変わりました。ウェブサイトのオーナーはこれまでよりも、サイト全体に適するスタイルを欲しており、ウェブプラットフォームはこれを実現可能にしました。

+ +

いくつかのフォームウィジェットでは、コントロールをスタイル設定できるように作成し直すのは難しいですが、ユーザービリティを破綻させないよう気をつける必要はあるものの、CSS を使って多くのフォーム機能をスタイル設定できます。

+ +

CSS を伴ってもすべてのウィジェットが同等に作成されるわけではありません

+ +

いまだに、フォームで CSS を使用する際に困ることが存在します。この問題は、3 つのカテゴリーに分けられます。

+ +

良好

+ +

いくつかの要素はプラットフォーム間の問題があるとしても、ほとんど問題なくスタイルを設定できます。これらは以下の構造的な要素が含まれます:

+ +
    +
  1. {{HTMLElement("form")}}
  2. +
  3. {{HTMLElement("fieldset")}} と {{HTMLElement("legend")}}
  4. +
  5. 単一行のテキスト {{HTMLElement("input")}} (例 text, url, email...のタイプ) <input type="search">を除く
  6. +
  7. 複数行の {{HTMLElement("textarea")}}
  8. +
  9. ボタン ({{HTMLElement("input")}} と {{HTMLElement("button")}}の両方)
  10. +
  11. {{HTMLElement("label")}}
  12. +
  13. {{HTMLElement("output")}}
  14. +
+ +

不良

+ +

一部の要素はほとんどスタイル設定ができず、時に CSS3 の高度な知識やトリックが必要になるかもしれません。

+ +
    +
  1. チェックボックスとラジオボタン
  2. +
  3. <input type="search">
  4. +
+ +

これら特殊なケースをどのように扱うかについては、HTML フォームへの高度なスタイル設定の記事で見ていきます。

+ +

劣悪

+ +

一部の要素は、CSS でスタイルを設定できません。たとえば次のもの:

+ + + +

これらの要素をスタイル設定するのに関して何ができるかについては、HTML フォームへの高度なスタイル設定の記事で見ていきます。

+ +

これらすべてのウィジェットの主な問題は、ウィジェットの構造がとても複雑であるという事実と、(コントロールの width や margin の変更といった)基本的なスタイル設定を超えると、現在の CSS では(例えばカレンダー日付ピッカーや、選択肢のリストを表示する<select>のボタンのような)ウィジェットの細かい部分すべてにスタイルを設定できるほどの表現力がないことによります。

+ +

これらのウィジェットを完全にカスタマイズしたい場合は、HTML, CSS, JavaScript を使って独自のものを作成する必要があります。それはこのコアフォームの記事の範囲を超えますが、高度な記事のカスタムウィジェットの作成方法の記事で説明します。

+ +
+

: フォームコントロールの内部コンポーネントにスタイル設定するプロプライエタリな CSS 疑似要素、例えば {{cssxref('::-moz-range-track')}}がありますが、これはブラウザー同士で整合していないので、これに頼るべきではありません。これについては後程でも触れます。

+
+ +

良好

+ +

CSS でのスタイル設定が容易な要素は、振る舞いが他の HTML 要素とほとんど同じであるため、問題に直面することはないでしょう。ただし、ブラウザー間でユーザーエージェントのスタイルシートが若干矛盾するかもしれませんので、より簡単にスタイルを設定できるようにするためのトリックがあります。

+ +

上記で述べた基本的な CSS ツールと同じく、いくつかのセレクターが与えられます — UI 疑似クラス — これにより現在の UI の状態に基づくスタイル設定ができます。これは次の記事である、UI 疑似クラスで扱います。

+ +

この記事の最後で基本的なフォームコントロールのスタイル設定と配置について理解できる実例を詳しく見ていきます。しかしその前に、知っておくと良いフォームスタイル設定の特定の面をいくつか述べておきます。

+ +

フォントとテキスト

+ +

CSS のフォントやテキストの機能は、任意のウィジェットで容易に使用できます (また、フォームウィジェットで {{cssxref("@font-face")}} も使用できます)。ただし、ブラウザーの動作にしばしば矛盾があります。デフォルトで、一部のブラウザーは親から {{cssxref("font-family")}} や {{cssxref("font-size")}} を継承しません。代わりに多くのブラウザーでは、システムのデフォルトの体裁を使用します。フォームの体裁を他のコンテンツと一致させるには、以下のルールをスタイルシートに追加するとよいでしょう:

+ +
button, input, select, textarea {
+  font-family : inherit;
+  font-size   : 100%;
+}
+ +

{{cssxref('inherit')}} のプロパティ値で、プロパティ値は計算された親要素のプロパティ値に一致するようになります。つまり親の値を継承します。

+ +

以下のスクリーンショットで違いを示します。左側は Mac OS X の Chrome における<input type="text">, <input type="date">, {{htmlelement('select')}}, {{htmlelement('textarea')}}, <input type="submit">, <button> 要素の既定のレンダリングで、プラットフォームのデフォルトフォントスタイルを使用しています。右側は同じ要素ですが、フォントを調和させるスタイルルールを適用したものです。

+ +

Form controls with default and inherited font families. 既定では, some types are serif and others are sans serif. Inheriting should change the fonts of all to the parent's font family - in this case a paragraph. Oddly, input of type submit does not inherit from the parent paragraph.

+ +

既定はいろいろと変わります。継承により、フォントは親のフォントファミリーに変更されます — ここでは親コンテナのデフォルトの serif フォントです。ほぼすべてそうですが、例外として Chrome では<input type="submit"> は親段落を継承しません。むしろ、{{cssxref('font-family#Values', 'font-family: system-ui')}}を使います。これは同等な入力タイプの中で <button> 要素を使う理由です!

+ +

フォームはシステムのデフォルトスタイルを使用するか、コンテンツに合うよう設計されたカスタムスタイルを使用するかについては多くの議論があります。これを決めるのは、設計者としてサイトやウェブアプリケーションを作成するあなた次第です。

+ +

ボックスモデル

+ +

すべてのテキストフィールドは、CSS のボックスモデルに関する全プロパティ ({{cssxref("width")}}、{{cssxref("height")}}、{{cssxref("padding")}}、{{cssxref("margin")}}、および {{cssxref("border")}}) を完全にサポートしています。ただし前述のとおり、ブラウザーがウィジェットを表示する際はシステムのデフォルトスタイルに依存します。コンテンツに対してそれらをどのように混ぜ合わせるかを決めるのは、あなた次第です。ウィジェットでネイティブのルックアンドフィールを維持したいのでしたら、ウィジェットのサイズを調和させたい場合に若干の問題に直面するでしょう。

+ +

これは各ウィジェットがボーダー、パディング、マージンについて独自のルールを持っているためです。このためさまざまなウィジェットを同じサイズにしたい場合に、{{cssxref("box-sizing")}} プロパティを使用しなければなりません:

+ +
input, textarea, select, button {
+  width : 150px;
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+}
+ +

下のスクリーンショットで、左の列は<input type="radio">, <input type="checkbox">, <input type="range">, <input type="text">, <input type="date"> input, {{htmlelement('select')}}, {{htmlelement('textarea')}},<input type="submit">, {{htmlelement('button')}} の既定の描画、右の列は同じ要素に上のルールを使用して作成したものです。各種のウィジェットのプラットフォームのデフォルトルールと比較して、すべての要素が同じ領域を占めるようにすることが可能な点に注目してください。

+ +

box model properties effect most input types.

+ +

スクリーンショットで明白でないことはラジオボタンとチェックボックスコントロールが同じであるが、水平位置が {{cssxref('width')}} プロパティで与えられる 150px の中心にあることです。他のブラウザーではウィジェットを中心揃えにしませんが、割り当てられたスペースに付着させます。

+ +

legend 配置

+ +

{{HTMLElement("legend")}} 要素はポジショニングを除いて、スタイル設定の問題はありません。既定では、それは親 {{HTMLElement("fieldset")}} の上ボーダーの前面に、左上の隅の近くに配置されます。これを他の場所、例えば fieldset内のどこかや、左下の隅に配置するには、配置に頼る必要があります。

+ +

下記の例を見てください:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/native-form-widgets/positioned-legend.html", '100%', 400)}}

+ +

この方法で legend を配置するには、次の CSS を使います(簡単のため他の宣言は削除しています):

+ +
fieldset {
+  position: relative;
+}
+
+legend {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+}
+ +

<fieldset> も配置される必要があり、<legend> がそれに合わせて位置が決まるように (そうでなければ <legend><body>に合わせて位置決めされます)

+ +

{{HTMLElement("legend")}} 要素はアクセシビリティのためとても重要です — これはアシスト技術により fieldset 内の各フォーム要素のラベルとして話されます — が、上のようなテクニックの使用は良いです。legend コンテンツは同じ方法で話されます; 単に見た目の位置が変更されます。

+ +
+

: <legend>の位置決めに役立つ{{cssxref("transform")}}プロパティも使用できますが、例えばa transform: translateY();を使って配置するとき、移動はするものの <fieldset> の枠に劣悪なギャップができて、除去が困難です。

+
+ +

特定のスタイル設定の例

+ +

HTML フォームにスタイルを設定する方法の具体例を見ていきましょう。以下のような "はがき" 風の連絡フォームを作成します。完成バージョンはこちら

+ +

この例に従うには、postcard-start.html ファイルをコピーして、次のやり方に従ってください。

+ +

HTML

+ +

HTML は、ガイドの最初の記事で使用したものより少しだけ複雑です。いくつか ID やタイトルを追加しています。

+ +
<form>
+ <h1>to: Mozilla</h1>
+
+  <div id="from">
+    <label for="name">from:</label>
+    <input type="text" id="name" name="user_name">
+  </div>
+
+  <div id="reply">
+    <label for="mail">reply:</label>
+    <input type="email" id="mail" name="user_email">
+  </div>
+
+  <div id="message">
+    <label for="msg">Your message:</label>
+    <textarea id="msg" name="user_message"></textarea>
+  </div>
+
+  <div class="button">
+    <button type="submit">Send your message</button>
+  </div>
+</form>
+ +

上記のコードを HTML の body に追加します。

+ +

Organizing your assets

+ +

ここからがおもしろいところです! コードを書き始める前に、ここでは 3 つの追加要素が必要です:

+ +
    +
  1. はがきの背景 — この画像をダウンロードして作業している HTML ファイルと同じディレクトリーに保存します。
  2. +
  3. タイプライター風フォント: fontsquirrel.com の "Secret Typewriter"  — TTF ファイルを上記と同じディレクトリーにダウンロードします。
  4. +
  5. 手書き風フォント: fontsquirrel.com の "Journal"  — TTF ファイルを上記と同じディレクトリーにダウンロードします。
  6. +
+ +

始める前にフォントの処理が必要です:

+ +
    +
  1. fontsquirrel Webfont Generator に移動します。
  2. +
  3. フォームを使って、両方のフォントファイルをアップロードして webfont キットを生成します。キットをコンピューターにダウンロードします。
  4. +
  5. zip ファイルを展開します。
  6. +
  7. 展開した中身には 2 つの .woff ファイルと 2 つの .woff2 ファイルがあります。このファイルを、前と同じ fonts というディレクトリーにコピーします。各フォントの 2 つのファイルはブラウザー互換性を最大化するのに使います; より詳しい情報は Web fonts の記事を見てください。
  8. +
+ +

CSS

+ +

ここから例の CSS を見ていきましょう。{{htmlelement("style")}} 要素の中にすべてのコードブロックを一つ一つ追加します。

+ +

全体レイアウト

+ +

まず、{{cssxref("@font-face")}} ルールと、すべての{{HTMLElement("body")}} と {{HTMLElement("form")}} 要素に設定するスタイルを定義して準備します。fontsquirrel 出力が上記で述べたものと異なる場合、stylesheet.css ファイル内にダウンロード済みの webfont キットの中から正しい @font-face ブロックを見つけることができます(下記の @font-face ブロックをそれで置換し、パスをフォントファイルのものに更新する必要があります):

+ +
@font-face {
+    font-family: 'handwriting';
+    src: url('fonts/journal-webfont.woff2') format('woff2'),
+         url('fonts/journal-webfont.woff') format('woff');
+    font-weight: normal;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: 'typewriter';
+    src: url('fonts/veteran_typewriter-webfont.woff2') format('woff2'),
+         url('fonts/veteran_typewriter-webfont.woff') format('woff');
+    font-weight: normal;
+    font-style: normal;
+}
+
+body {
+  font  : 1.3rem sans-serif;
+  padding : 0.5em;
+  margin  : 0;
+  background : #222;
+}
+
+form {
+  position : relative;
+  width  : 740px;
+  height : 498px;
+  margin : 0 auto;
+  padding: 1em;
+  box-sizing: border-box;
+  background : #FFF url(background.jpg);
+
+  /* we create our grid */
+  display  : grid;
+  grid-gap : 20px;
+  grid-template-columns : repeat(2, 1fr);
+  grid-template-rows    : 10em 1em 1em 1em;
+}
+ +

注意として、フォームをレイアウトするのに CSS GridFlexbox を使っています。これで、タイトルやフォーム要素といった各要素を配置できます:

+ +
h1 {
+  font : 1em "typewriter", monospace;
+  align-self : end;
+}
+
+#message {
+   grid-row: 1 / 5;
+}
+
+#from, #reply {
+   display: flex;
+}
+ +

ラベルとコントロール

+ +

そして、フォーム要素自体に対するスタイル設定を始めます。まずは、{{HTMLElement("label")}} に適切なフォントを割り当てましょう。

+ +
label {
+  font : .8em "typewriter", sans-serif;
+}
+ +

テキストフィールドには、共通のルールがいくつか必要です。{{cssxref("border")}} や {{cssxref("background")}} の削除と {{cssxref("padding")}} や {{cssxref("margin")}} の再定義を行います。

+ +
input, textarea {
+  font    : 1.4em/1.5em "handwriting", cursive, sans-serif;
+  border  : none;
+  padding : 0 10px;
+  margin  : 0;
+  width   : 80%;
+  background : none;
+}
+ +

これらフィールドのひとつがフォーカスを得たときに、ライトグレー色で透過する背景で強調します。一部のブラウザーで付加されるデフォルトのフォーカス強調を取り除くため、{{cssxref("outline")}} プロパティを追加することが重要ですので注意してください。

+ +
input:focus, textarea:focus {
+  background   : rgba(0,0,0,.1);
+  border-radius: 5px;
+}
+ +

テキストフィールドのスタイル設定が完了して、次は単一行および複数行のテキストフィールドの表示が同じになるよう調整しなければなりません。これは、一般的にこれらのデフォルト表示が同じでないためです。

+ +

Tweaking the textareas

+ +

{{HTMLElement("textarea")}} 要素はデフォルトでブロック要素としてレンダリングされるようにします。ここで重要なことは、{{cssxref("resize")}} プロパティと {{cssxref("overflow")}} プロパティの 2 つです。ここでは固定サイズでデザインしているため、ユーザーが複数行のテキストフィールドをリサイズできないように resize プロパティを使用します。{{cssxref("overflow")}} プロパティは、ブラウザー間でのフィールドの一貫性を向上させるために使用します。これのデフォルト値が auto であるブラウザーと scroll であるブラウザーが存在します。この例では、すべてのブラウザーが auto になるようにするのがよいでしょう。

+ +
textarea {
+  display : block;
+
+  padding : 10px;
+  margin  : 10px 0 0 -10px;
+  width   : 100%;
+  height  : 90%;
+
+  border-right: 1px solid;
+
+  /* resize  : none; */
+  overflow: auto;
+}
+ +

送信ボタンにスタイル設定する

+ +

{{HTMLElement("button")}} 要素は、CSS によってより便利になります。疑似要素を含めて、行いたいことが何でもできます!

+ +
button {
+  padding      : 5px;
+  font         : bold .6em sans-serif;
+  border       : 2px solid #333;
+  border-radius: 5px;
+  background   : none;
+  cursor       : pointer;
+  transform    : rotate(-1.5deg);
+}
+
+button:after {
+  content      : " >>>";
+}
+
+button:hover,
+button:focus {
+  outline     : none;
+  background  : #000;
+  co
+ +

最終結果

+ +

これでよし! フォームは次のようになるでしょう:

+ +

+ +
+

: 例が期待どおり動かず、われわれのバージョンを確認したい場合、GitHub にあります — ライブ版を見てください (ソースコードも見てください)。

+
+ +

スキルを試しましょう!​

+ +

この記事の終わりまで到達しました。しかし、肝要な点を思い起こせるでしょうか?次に進む前に、テストによって知識の定着を試すことができます——スキルテスト:スタイリングの基本をご覧ください。

+ +

まとめ

+ +

ご覧いただいたとおり、テキストフィールドとボタンだけでフォームを作成する限りでは、CSS を使用したスタイル設定は容易です。次の記事では、"不良" や "劣悪" カテゴリに入っているウィジェットの扱い方を見ていきます。

+ +

{{PreviousMenuNext("Learn/Forms/Other_form_controls","Learn/Forms/Advanced_form_styling","Learn/Forms")}}

+ +

このモジュール

+ +

E

+ + + +

上級トピック

+ +

セクション

+ + diff --git a/files/ja/learn/forms/the_native_form_widgets/index.html b/files/ja/learn/forms/the_native_form_widgets/index.html new file mode 100644 index 0000000000..f0ddcdc09d --- /dev/null +++ b/files/ja/learn/forms/the_native_form_widgets/index.html @@ -0,0 +1,339 @@ +--- +title: 基本的なネイティブフォームコントロール +slug: Learn/Forms/The_native_form_widgets +tags: + - Example + - Forms + - Guide + - HTML + - Intermediate + - Web +translation_of: Learn/Forms/Basic_native_form_controls +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Forms/How_to_structure_a_web_form", "Learn/Forms/HTML5_input_types", "Learn/Forms")}}
+ +

直前の記事では、機能的なウェブフォームの例をマークアップし、いくつかのフォームコントロールとよくある構造要素を導入し、アクセシビリティのベストプラクティスを見てきました。次にさまざまなフォームコントロールやウィジェットの機能を詳しく見ていきます — 色々な種類のデータを集めるのにどんなオプションが使えるのかを見ていきます。とりわけこの記事では、ウェブの初期からある全てのブラウザーで利用できる、オリジナルのフォームコントロールを見ていきます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーと、基本的な HTML の理解
目的:データを収集するためにブラウザーで使用できるネイティブフォームウィジェットの種類と、それらを HTML を使用して実装する方法を理解する。
+ +

{{HTMLelement('form')}}, {{HTMLelement('fieldset')}}, {{HTMLelement('legend')}}, {{HTMLelement('textarea')}}, {{HTMLelement('label')}}, {{HTMLelement('button')}},  {{HTMLelement('input')}}といったフォーム要素については既に見てきました。この記事では次を網羅します:

+ + + +
+

: この記事で説明されている機能のほとんどは、ブラウザー間で幅広くサポートされています。これに対する例外に注意しましょう。より正確な詳細が必要な場合は、HTML フォーム要素のリファレンス、特に広範囲にわたる <input> 型の参照を参照してください。

+
+ +

テキスト入力フィールド

+ +

テキスト {{htmlelement("input", "入力")}} フィールドは最も基本的なフォームウィジェットです。これらはユーザーがあらゆる種類のデータを入力できるとても便利な方法です。

+ +
+

: HTML フォームのテキストフィールドは単純なプレーンテキストの入力コントロールです。つまり、これらを使ってリッチエディット (太字、斜体など) を実行することはできません。見かけるすべてのリッチテキストエディタは、HTML、CSS、および JavaScript で作成されたカスタムウィジェットです。

+
+ +

すべてのテキストフィールドに共通する動作があります:

+ + + +
+

: {{htmlelement("input")}} 要素は、type 属性によってさまざまなフォームとなるため、、HTML要素の中でも特別です。単一行のテキストフィールド、テキスト入力のないコントロール、時間と日付のコントロール、チェックボックス、カラーピッカー、ボタンといったテキスト入力のないコントロールなど、ほとんどのタイプのフォームウィジェットの作成に使用されます。

+
+ +

単一行のテキストフィールド

+ +

単一行のテキストフィールドは、{{htmlattrxref("type","input")}} 属性値が text に設定されている {{HTMLElement("input")}} 要素を使用するか、{{htmlattrxref("type","input")}} 属性を指定しない場合( text がデフォルト値になり)に作成されます。{{htmlattrxref("type","input")}} 属性に指定した値がブラウザーに認識されない場合 (たとえば type="color" を指定してブラウザーがネイティブの色ピッカーをサポートしていない場合)、この属性の値のテキストは代替値になります。

+ +
+

: GitHub の single-line-text-fields.html に、すべての単一行テキストフィールドタイプの例があります (こちらも参照してください)。

+
+ +

これは基本的な単一行のテキストフィールドの例です。

+ +
<input type="text" id="comment" name="comment" value="I'm a text field">
+ +

単一行のテキストフィールドは、ひとつだけ厳密な制約があります: 改行を含むテキストを入力した場合、ブラウザーはデータを送信する前に改行を取り除きます。

+ +

下記のスクリーンショットは macOS での Firefox 71 と Safari と Windows 10 の Chrome 79 と Edge 18 にて、既定の、フォーカスされた、無効にされたテキスト入力を示しています。

+ +

Screenshot of the disabled attribute and default :focus styles on a text input in Firefox, Safari, Chrome and Edge.

+ +
+

HTML5 では {{htmlattrxref("type","input")}} 属性に専用の値を追加することで、基本的な単一行のテキストフィールドを拡張しています。これらの値もやはり {{HTMLElement("input")}} 要素を単一行のテキストフィールドにしますが、フィールドに対して追加の制約や機能を付加します。

+
+ +

パスワードフィールド

+ +

このタイプのフィールドは、{{htmlattrxref("type","input")}} 属性の値 password を使用して設定できます:

+ +
<input type="password" id="pwd" name="pwd">
+ +

password の値は入力したテキストに対する特別な制約は付加しませんが、フィールドの値を隠します(例、ドットやアスタリスク)ので読むことができません。

+ +

これはユーザーインターフェイスの機能でしかないことに注意してください。テキストは JavaScript を使用してあなた自身でエンコードしなければ、平文で送信されてしまい、セキュリティには良くありません — 悪い組織がデータを遮ってパスワードや、クレジットカードデータや、送信したあらゆるものを盗むことがあります。ユーザーからこれを保護するためにはフォームを含むあらゆるページをセキュア通信でホストし (つまり https:// ... アドレスにて) 、データ送信前に暗号化することです。

+ +

最近のブラウザーは、安全でない接続を介してフォームデータを送信することによるセキュリティへの影響を認識しており、ユーザーが安全でないフォームを使用しないように警告を実装しています。Firefox が実装しているものの詳細については、安全でないパスワードをご覧ください。

+ +

隠しコンテンツ

+ +

もう1つのオリジナルなテキストコントロールは hidden 入力タイプです。これは他のフォームデータとともにサーバー送信されるがユーザーからは見えないデータを持つのに使われています — 例えば命令を発行するときにサーバーにタイムスタンプを送りたい場合。これは隠れているので、ユーザーが見ることも、意図せずに値を編集することもなく、フォーカスを得ることもないしスクリーンリーダーが気づくこともありません。

+ +
<input type="hidden" id="timestamp" name="timestamp" value="1286705410">
+
+ +

このような要素を作成する場合は、name 属性と value 属性の設定が必要です。この値は JavaScript にて動的にセットできます。hidden 入力タイプには関連したラベルはありません。

+ +

その他のテキストタイプ、{{HTMLElement("input/search", "search")}}, {{HTMLElement("input/url", "url")}}, と{{HTMLElement("input/tel", "tel")}}, は HTML5 で追加されました。これは次のチュートリアルの「HTML5 入力タイプ」にて網羅されます。

+ +

チェック可能アイテム:チェックボックスとラジオボタン

+ +

チェック可能アイテムは、そのものや、関連したラベルをクリックすることで状態を変更できるコントロールです。チェック可能アイテムは 2 種類あります: チェックボックスとラジオボタンです。どちらもデフォルトでチェックするかを示すために、checked 属性を使用します。

+ +

これらのウィジェットは、他のフォームウィジェットと同じようには動作しない点が特筆されます。ほとんどのフォームウィジェットではフォームを送信すると、name 属性を持つすべてのウィジェットは値がなくても送信します。チェック可能アイテムでは、それらがチェックされている場合にのみ値を送信します。チェックされていない場合は、name も含めて何も送信しません。チェックされているが値がない場合、name が on という値で送信されます。

+ +
+

: このセクションの例は、checkable-items.html として GitHub にあります (こちらも参照してください)。

+
+ +

最大限のユーザービリティ/アクセシビリティを実現するために、関連項目の各リストを {{htmlelement("fieldset")}} で囲み、リストの全体的な説明を示す {{htmlelement("legend")}} で囲むことをお勧めします。{{htmlelement("label")}}/{{htmlelement("input")}} 要素の個々のペアは、それぞれ独自のリスト項目 (または同様のもの) に含める必要があります。関連した {{htmlelement('label')}} はラジオボタンやチェックボックスの直後に、{{htmlelement("legend")}}の中身にラジオボタンやチェックボックスのグループの説明が置かれます。これは上の例に示されています。

+ +

チェックボックス

+ +

チェックボックスは、type 属性を {{HTMLElement("input/checkbox", "checkbox")}} に設定した {{HTMLElement("input")}} 要素で作成します。

+ +
<input type="checkbox" checked id="carrots" name="carrots" value="carrots">
+ +

checked 属性を含んだチェックボックスはページ読み込み時に自動的にチェックされます。チェックボックスまたはその関連ラベルをチェックするとチェックボックスのオン/オフがトグルされます。

+ +

下記のスクリーンショットは macOS での Firefox 71 と Safari と Windows 10 の Chrome 79 と Edge 18 にて、既定の、フォーカスされた、無効にされたチェックボックスを示しています。

+ +

Default, focused and disabled Checkboxes in Firefox 71 and Safari 13 on Mac and Chrome 79 and Edge 18 on Windows 10

+ +
+

: checked 属性のあるあらゆるチェックボックスやラジオボタンには、チェックされていない場合でも、対応する {{cssxref(':default')}} 仮想クラスがあります。現在チェックされているものには{{cssxref(':checked')}} 仮想クラスがあります。

+
+ +

チェックボックスのオンオフ性質により、チェックボックスは、規定のチェックボックスを拡張してトグルスイッチのように見えるボタンを作っている開発者やデザイナーにとって、トグルボタンとして考えられます。ここで動作する例を 見ることができます(ソースコードも見られます)。

+ +

ラジオボタン

+ +

ラジオボタンは、{{htmlattrxref("type","input")}} 属性を radio に設定した {{HTMLElement("input")}} 要素で作成します。

+ +
<input type="radio" checked id="soup" name="meal">
+ +

いくつかのラジオボタンをまとめることができます。{{htmlattrxref("name","input")}} 属性で同じ値を共有すると、それらのラジオボタンは同じボタングループに属するとみなされます。グループ内でボタンは同時に 1 つだけチェックできます。つまり、あるラジオボタンをチェックすると、他のラジオボタンは自動的にチェックが外れます。フォームを送信するときは、チェックしているラジオボタンのみの値を送信します。何もチェックしていない場合はラジオボタンの集まり全体が未知の状態であるとみなし、フォーム送信時は値を送信しません。

+ +
<fieldset>
+  <legend>What is your favorite meal?</legend>
+  <ul>
+    <li>
+      <label for="soup">Soup</label>
+      <input type="radio" checked id="soup" name="meal" value="soup">
+    </li>
+    <li>
+      <label for="curry">Curry</label>
+      <input type="radio" id="curry" name="meal" value="curry">
+    </li>
+    <li>
+      <label for="pizza">Pizza</label>
+      <input type="radio" id="pizza" name="meal" value="pizza">
+    </li>
+  </ul>
+</fieldset>
+ +

下記のスクリーンショットは macOS での Firefox 71 と Safari と Windows 10 の Chrome 79 と Edge 18 にて、チェックなしとチェックされたラジオボタン、フォーカスされた、また無効でチェックなしとチェックされたラジオボタンを示しています。

+ +

Radio buttons on Firefox 71 and Safari 13 on Mac and Chrome 79 and Edge 18 on Windows 10

+ +

ボタン

+ +

ラジオボタンはその名に反して、実際のボタンではありません。実際のボタンを見てみましょう! ボタンを生成するには、3 種類の入力タイプがあります:

+ +
+
{{原語併記("送信", "Submit")}}
+
フォームデータをサーバーに送信します。{{HTMLElement("button")}} 要素の場合、type 属性 (または type の無効な値) を省略すると、送信ボタンが表示されます。
+
{{原語併記("リセット", "Reset")}}
+
すべてのフォームウィジェットをデフォルト値にリセットします。
+
button
+
自動的な効果のないボタンで、JavaScript コードを用いてカスタマイズできるもの。
+
+ +

それから、{{htmlelement("button")}} 要素それ自体もあります。これは値が submitreset または button である type 属性をとり、上記の 3 つの <input> 種別を模倣できます。この 2 つの主な違いは実際の <button> 要素の方が多くのスタイル設定できることです。

+ +
+

: image 入力タイプもボタンとしてレンダリングされます。それはあとで見ます。

+
+ +
+

: このセクションの例は button-examples.html として GitHub にあります (こちらも参照してください)。

+
+ +

ボタンは {{HTMLElement("button")}} 要素か {{HTMLElement("input")}} 要素で作成します。どの種類のボタンを表示するかを指定するのは、{{htmlattrxref("type","input")}} 属性の値です:

+ +

送信

+ +
<button type="submit">
+    This a <br><strong>submit button</strong>
+</button>
+
+<input type="submit" value="This is a submit button">
+ +

リセット

+ +
<button type="reset">
+    This a <br><strong>reset button</strong>
+</button>
+
+<input type="reset" value="This is a reset button">
+ +

無名

+ +
<button type="button">
+    This an <br><strong>anonymous button</strong>
+</button>
+
+<input type="button" value="This is an anonymous button">
+ +

ボタンは {{HTMLElement("button")}} 要素でも {{HTMLElement("input")}} 要素でも、常に同じ動作になります。上記のサンプルでわかるように、{{HTMLElement("button")}} 要素はラベルとして HTML コンテンツを使用できて、これは開始と終了の<button>タグの間に挿入されます。一方で{{HTMLElement("input")}} 要素は空要素です。つまり value 属性の中にラベルが挿入され、このためプレーンテキストのコンテンツのみ使用できます。

+ +

下記の例は macOS での Firefox 71 と Safari と Windows 10 の Chrome 79 と Edge 18 にて、既定の、フォーカスされた、無効なボタンを示しています。

+ +

Default, focused and disabled button input types in Firefox 71 and Safari 13 on Mac and Chrome 79 and Edge 18 on Windows 10

+ +

画像ボタン

+ +

画像ボタンコントロールは {{HTMLElement("img")}} 要素とまったく同じように表示されますが、ユーザーがクリックすると送信ボタン (前述) のように動作します。

+ +

画像ボタンは、{{htmlattrxref("type","input")}} 属性を image に設定した {{HTMLElement("input")}} 要素で作成します。

+ +

この要素は {{HTMLElement("img")}} 要素とまったく同じ属性をサポートして、さらにフォームボタンがサポートする属性もすべてサポートします。

+ +
<input type="image" alt="Click me!" src="my-img.png" width="80" height="30" />
+ +

画像ボタンをフォームの送信に使用する際にこのウィジェットは自身の値を送信しませんが、代わりに画像上でクリックした位置の X 座標と Y 座標を送信します (座標は画像に対して相対的、つまり画像の左上隅が座標 0, 0 になります)。座標は 2 つのキーと値の組として送信されます。

+ + + +

サンプルをご覧ください。フォームの画像上の座標 (123, 456) でクリックすると、 get メソッド経由で送信されて、以下のような値の追加された URL が送信されます:

+ +
http://foo.com?pos.x=123&pos.y=456
+ +

これは "hot map" を作成するためにとても便利な手段です。これらの値がどのように送信あるいは取得されるかについては、フォームデータの送信の記事で詳しく説明します。

+ +

ファイルピッカー

+ +

初期のHTMLであった最後の <input> タイプがあります: ファイル入力タイプです。フォームで、ファイルをサーバーに送信できます。この特定操作については以下の記事で詳しく説明します: フォームデータの送信。ファイルピッカーウィジェットで、ユーザーは送信するファイルを 1 つ以上選択できます。

+ +

ファイルピッカーウィジェットを作成するには、{{htmlattrxref("type","input")}} 属性を file に設定した {{HTMLElement("input")}} 要素を使用します。{{htmlattrxref("accept","input")}} 属性を使用して、受け入れるファイルの種類を制限できます。加えて、ユーザーが複数のファイルを選択できるようにしたい場合は、{{htmlattrxref("multiple","input")}} 属性を付加します。

+ +

+ +

以下の例では、画像ファイルを要求するファイルピッカーを作成しています。ユーザーは複数のファイルを指定できます。

+ +
<input type="file" name="file" id="file" accept="image/*" multiple>
+ +

いくつかのモバイルデバイスでは、ファイルピッカーは、次のようにキャプチャー情報を accept 属性に追加することで、端末のカメラやマイクでキャプチャーされた写真、動画、オーディオにアクセスできます:

+ +
<input type="file" accept="image/*;capture=camera">
+<input type="file" accept="video/*;capture=camcorder">
+<input type="file" accept="audio/*;capture=microphone">
+ +

共通属性

+ +

フォームウィジェットを定義するために使用される要素の多くは、独自の属性をいくつか持っています。ただし、すべてのフォーム要素に共通の一連の属性があり、それによりウィジェットをある程度制御できます。共通属性のリストは以下のとおりです。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名既定値説明
autofocusfalseこの真偽値属性を使用すると、ユーザーがページをロードするときに、たとえば別のコントロールを入力して上書きしない限り、要素に自動的に入力フォーカスするように指定できます。この属性を指定できるのは、文書内の 1 つのフォーム関連要素だけです。
disabledfalseこの真偽値属性は、ユーザーが要素と対話できないことを示します。この属性が指定されていない場合、要素はそれを含む要素 (例えば {{HTMLElement("fieldset")}}) からその設定を継承します。disabled 属性が設定されている包含要素がない場合は、その要素が有効になります。
formウィジェットが関連付けられている <form> 要素。属性の値は、同じ文書内の {{HTMLElement("form")}} 要素の id 属性でなければなりません。理論的には、フォームウィジェットを {{HTMLElement("form")}} 要素の外側に設定できます。しかし実際には、その機能をサポートするブラウザーはありません。
name要素の名前。これはフォームデータとともに送信されます。
value要素の初期値
+ +

スキルをテストしましょう!

+ +

この記事の最後に到着しましたが、最も大事な情報を覚えていますか? 次に進む前に、この情報を保持しているか検証するテストがあります — Test your skills: Basic controls を見てください。

+ +

まとめ

+ +

上で見たように、利用可能なフォーム要素には多くの異なるタイプがあります。一度にこれらの詳細の全てを覚えておく必要はありません。詳細について調べるために好きなだけこの記事に戻ることができます 。

+ +

この記事では古い入力タイプをカバーしてきました — これは HTML の初期の頃に導入されたオリジナルで、すべてのブラウザーでよくサポートされます。次のセクションでは、HTML 5 で追加された新しい type 属性の値を見ていきます。

+ +

{{PreviousMenuNext("Learn/Forms/How_to_structure_a_web_form", "Learn/Forms/HTML5_input_types", "Learn/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/ui_pseudo-classes/index.html b/files/ja/learn/forms/ui_pseudo-classes/index.html new file mode 100644 index 0000000000..8ab11cbac6 --- /dev/null +++ b/files/ja/learn/forms/ui_pseudo-classes/index.html @@ -0,0 +1,616 @@ +--- +title: UI 擬似クラス +slug: Learn/Forms/UI_pseudo-classes +tags: + - Beginner + - CSS + - Example + - Forms + - Guide + - HTML + - Pseudo-classes + - Styling + - Web +translation_of: Learn/Forms/UI_pseudo-classes +--- +

{{LearnSidebar}}{{PreviousMenuNext("Learn/Forms/Advanced_form_styling", "Learn/Forms/Form_validation", "Learn/Forms")}}

+ +

In the previous articles we covered the styling of various form controls, in a general manner. This included some usage of pseudo-classes, 例えば、using :checked to target a checkbox only when it is selected. In this article, we will explore in detail the different UI pseudo-classes available to us in modern browsers for styling forms in different states.

+ + + + + + + + + + + + +
前提条件:Basic computer literacy, and a basic understanding of HTML and CSS, including general knowledge of pseudo-classes and pseudo-elements.
目的:To understand what parts of forms are hard to style, and why; to learn what can be done to customize them.
+ +

疑似クラスで何が利用できるか?

+ +

The original pseudo-classes available to us (as of CSS 2.1) that are relevant to forms are:

+ + + +

These basic pseudo-classes should be familiar to you now. More recently, the CSS Selector Level 3 and CSS Basic UI Level 3 added more pseudo-classes related to HTML forms that provide several other useful targetting conditions that you can take advantage of. We'll discuss these in more detail in the sections below, but briefly, the main ones we'll be looking at are:

+ + + +

There are many others too, but the ones listed above are the most obviously useful. Some of the others are aimed at solving very specific niche problems, or simply not very well supported in browsers yet. The ones listed above all have pretty good browser support, but of course you should test your form implementations carefully to make sure they work for your target audience.

+ +
+

: A number of the pseudo-classes discussed here are concerned with styling form controls based on their validation state (is their data valid, or not?) You'll learn much more about setting and controlling validation constraints in our next article — Client-side form validation — but for now we'll keep things simple with regards to form validation, so it doesn't confuse things.

+
+ +

入力が必須か否かでスタイル設定する

+ +

One of the most basic concepts with regards to client-side form validation is whether a form input is required (it has to be filled in before the form can be submitted) or optional.

+ +

{{htmlelement('input')}}, {{htmlelement('select')}}, and {{htmlelement('textarea')}} elements have a required attribute available which, when set, means that you have to fill in that control before the form will successfully submit. 例えば、:

+ +
<form>
+  <fieldset>
+    <legend>Feedback form</legend>
+    <div>
+      <label for="fname">First name: </label>
+      <input id="fname" name="fname" type="text" required>
+    </div>
+    <div>
+      <label for="lname">Last name: </label>
+      <input id="lname" name="lname" type="text" required>
+    </div>
+    <div>
+      <label for="email">Email address (include if you want a response): </label>
+      <input id="email" name="email" type="email">
+    </div>
+    <div><button>Submit</button></div>
+  </fieldset>
+</form>
+ +

Here, the first name and last name are required, but the email address is optional.

+ +

You can match these two states using the {{cssxref(':required')}} and {{cssxref(':optional')}} pseudo-classes. 例えば、if we apply the following CSS to the above HTML:

+ +
input:required {
+  border: 1px solid black;
+}
+
+input:optional {
+  border: 1px solid silver;
+}
+ +

The required controls would have a black border, and the optional control will have a silver border, like so:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/basic-required-optional.html", '100%', 400)}}

+ +

You can also try submitting the form without flling it in, to see the client-side validation error messages browsers give you 既定では.

+ +

The above form isn't bad, but it isn't great either. For a start, we are signalling required versus optional status using color alone, which isn't great for colorblind people. Second, the standard convention on the web for required status is an asterisk (*), or the word "required" being associated with the controls in question.

+ +

In the next section we'll look at a better example of indicating required fields using :required, which also digs into using generated content.

+ +
+

: You'll probably not find yourself using the :optional pseudo-class very often. Form controls are optional 既定では, so you could just do your optional styling 既定では, and add styles on top for required controls.

+
+ +
+

: If one radio button in a same-named group of radio buttons has the required attribute, all the radio buttons will be invalid until one is selected, but only the one with the attribute assigned will actually match {{cssxref(':required')}}.

+
+ +

疑似クラスでコンテンツを生成する

+ +

In previous articles we've seen usage of generated content, but we thought now would be a good time to talk about it in a bit more detail.

+ +

The idea is that we can use the ::before and ::after pseudo-elements along with the content property to make a chunk of content appear before or after the affected element. The chunk of content is not added to the DOM, so is invisible to screenreaders; it is part of the document's styles. Because it is a pseudo element, it can be targetted with styles in the same way that any actual DOM node can.

+ +

This is really useful when you want to add a visual indicator to an element, such as a label or icon, but don't want it to be picked up by assistive technologies. 例えば、in our custom radio buttons example, we use generated content to handle the placement and animation of the inner circle when a radio button is selected:

+ +
input[type="radio"]::before {
+  display: block;
+  content: " ";
+  width: 10px;
+  height: 10px;
+  border-radius: 6px;
+  background-color: red;
+  font-size: 1.2em;
+  transform: translate(3px, 3px) scale(0);
+  transform-origin: center;
+  transition: all 0.3s ease-in;
+}
+
+input[type="radio"]:checked::before {
+  transform: translate(3px, 3px) scale(1);
+  transition: all 0.3s cubic-bezier(0.25, 0.25, 0.56, 2);
+}
+ +

This is really useful — screenreaders already let their users know when a radio button or checkbox they encounter is checked/selected, so you don't want them to read out another DOM element that indicates selection — that could be confusing. Having a purely visual indicator solves this problem.

+ +
+

: This also shows how you can combine a pseudo-class and pseudo-element if required.

+
+ +

Back to our required/optional example from before, this time we'll not alter the appearance of the input itself — we'll use generated content to add an indicating label (see it live here, and see the source code here).

+ +

First of all, we'll add a paragraph to the top of the form to say what you are looking for:

+ +
<p>Required fields are labelled with "required".</p>
+ +

Screenreader users will get "required" read out as an extra bit of information when they get to each required input, which sighted users will get our label.

+ +

Since form inputs don't directly support having generated content put on them (this is because generated content is placed relative to an element's formatting box, but form inputs work more like replaced elements and therefore don't have one), we will add an empty <span> to hang the generated content on:

+ +
<div>
+  <label for="fname">First name: </label>
+  <input id="fname" name="fname" type="text" required>
+  <span></span>
+</div>
+ +

The immediate problem with this was that the span was dropping onto a new line below the input, because the input and label are both set with width: 100%. To fix this we style the parent <div> to become a flex container, but also tell it to wrap its contents onto new lines if the content becomes too long:

+ +
fieldset > div {
+  margin-bottom: 20px;
+  display: flex;
+  flex-flow: row wrap;
+}
+ +

The effect this has is that the label and input sit on separate lines because they are both width: 100%, but the <span> has a width of 0 so can sit on the same line as the input.

+ +

Now onto the generated content. We create it using this CSS:

+ +
input + span {
+  position: relative;
+}
+
+input:required + span::after {
+  font-size: 0.7rem;
+  position: absolute;
+  content: "required";
+  color: white;
+  background-color: black;
+  padding: 5px 10px;
+  top: -26px;
+  left: -70px;
+}
+ +

We set the <span> to position: relative simply so that we can set the generated content to position: absolute and position it relative to the <span> rather than the <body> (The generated content acts as though it is a child node of the element it is generated on, for the purposes of positioning).

+ +

Then we give the generated content the content "required", which is what we wanted our label to say, and style and position it as we want. The result is seen below.

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/required-optional-generated.html", '100%', 430)}}

+ +

データが妥当が否かでコントロールをスタイル設定する

+ +

The other really important, fundamental concept in form validation is whether a form control's data is valid or not (in the case of numerical data, we can also talk about in-range and out-of-range data). Form controls with constraint limitations can be targeted based on these states.

+ +

:valid と :invalid

+ +

You can target from control using the {{cssxref(":valid")}} and {{cssxref(":invalid")}} pseudo-classes. Some points worth bearing in mind:

+ + + +

Let's go in and look at a simple example of :valid/:invalid (see valid-invalid.html for the live version, and also check out the source code).

+ +

As in the previous example, we've got extra <span>s to generate content on, which we'll use to provide indicators of valid/invalid data:

+ +
<div>
+  <label for="fname">First name *: </label>
+  <input id="fname" name="fname" type="text" required>
+  <span></span>
+</div>
+ +

To provide these indicators, we use the following CSS:

+ +
input + span {
+  position: relative;
+}
+
+input + span::before {
+  position: absolute;
+  right: -20px;
+  top: 5px;
+}
+
+input:invalid {
+  border: 2px solid red;
+}
+
+input:invalid + span::before {
+  content: '✖';
+  color: red;
+}
+
+input:valid + span::before {
+  content: '✓';
+  color: green;
+}
+ +

As before, we set the <span>s to position: relative so that we can position the generated content relative to them. We then absolutely position different generated content depending on whether the form's data is valid or invalid — a green check or a red cross, respectively. To add a bit of extra urgency to the invalid data, we've also given the inputs a thick red border when invalid.

+ +
+

: We've used ::before to add these labels, as we were already using ::after for the "required" labels.

+
+ +

You can try it below:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/valid-invalid.html", '100%', 430)}}

+ +

Notice how the required text inputs are invalid when empty, but valid when they have something filled in. The email input on the other hand is valid when empty, as it is not required, but invalid when it contains something that is not a proper email address.

+ +

範囲内と範囲外のデータ

+ +

As we hinted at above, there are two other related pseudo-classes to consider — {{cssxref(":in-range")}} and {{cssxref(":out-of-range")}}. These match numeric inputs where range limits are specified by the {{htmlattrxref("min", "input")}} and {{htmlattrxref("max","input")}}, when their data is inside or outside the specified range, respectvely.

+ +
+

: Numeric input types are date, month, week, time, datetime-local, number, and range.

+
+ +

It is worth noting that inputs whose data is in-range will also be matched by the :valid pseudo-class, and inputs whose data is out-of-range will also be matched by the :invalid pseudo-class. So why have both? The issue is really one of semantics — out-of-range is a more specific type of invalid communication, so you might want to provide a different message for out-of-range inputs, which will be more helpful to users than just saying "invalid". You might even want to provide both.

+ +

Let's look at an example that does exactly this. Our out-of-range.html demo (see also the source code) builds on top of the previous example to provide out-of-range messages for the numeric inputs, as well as saying whether they are required.

+ +

The numeric input looks like this:

+ +
<div>
+  <label for="age">Age (must be 12+): </label>
+  <input id="age" name="age" type="number" min="12" max="120" required>
+  <span></span>
+</div>
+ +

And the CSS looks like this:

+ +
input + span {
+  position: relative;
+}
+
+input + span::after {
+  font-size: 0.7rem;
+  position: absolute;
+  padding: 5px 10px;
+  top: -26px;
+}
+
+input:required + span::after {
+  color: white;
+  background-color: black;
+  content: "Required";
+  left: -70px;
+}
+
+input:out-of-range + span::after {
+  color: white;
+  background-color: red;
+  width: 155px;
+  content: "Outside allowable value range";
+  left: -182px;
+}
+ +

This is a similar story to what we had before in the :required example, except that here we've split out the declarations that apply to any ::after content into a separate rule, and given the separate ::after content for :required and :out-of-range states their own content and styling. You can try it here:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/out-of-range.html", '100%', 430)}}

+ +

It is possible for the number input to be both required and out-of-range at the same time, so what happens then? Because the :out-of-range rule appears later in the source code than the :required rule, the cascade rules come into play, and the out of range message is shown.

+ +

This works quite nicely — when the page first loads, "Required" is shown, along with a red cross and border. When you've typed in a valid age (i.e. in the range of 12-120), the input turns valid. If however, you then change the age entry to one that is out of range, the "Outside allowable value range" message then pops up in place of "Required".

+ +
+

: To enter an invalid/out-of-range value, you'll have to actually focus the form and type it in using the keyboard. The spinner buttons won't let you increment/decrement the value outside the allowable range.

+
+ +

有効/無効や読み取り専用/読み書き可能の入力をスタイル設定する

+ +

An enabled element is an element that can be activated; it can be selected, clicked on, typed into, etc.  A disabled element on the other hand cannot be interacted with in any way, and its data isn't even sent to the server

+ +

These two states can be targeted using {{cssxref(":enabled")}} and {{cssxref(":disabled")}}. Why are disabled inputs useful? Well, sometimes if some data does not apply to a certain user, you might not even want to submit that data when they submit the form. A classic example is a shipping form — commonly you'll get asked if you want to use the same address for billing and shipping; if so, you can just send a single address to the server, and might as well just disable the billing address fields.

+ +

Let's have a look at an example that does just this. First of all, the HTML is a simple form containing text inputs, plus a checkbox to toggle disabling the billing address on and off. The billing address fields are disabled 既定では.

+ +
<form>
+  <fieldset id="shipping">
+    <legend>Shipping address</legend>
+    <div>
+      <label for="name1">Name: </label>
+      <input id="name1" name="name1" type="text" required>
+    </div>
+    <div>
+      <label for="address1">Address: </label>
+      <input id="address1" name="address1" type="text" required>
+    </div>
+    <div>
+      <label for="pcode1">Zip/postal code: </label>
+      <input id="pcode1" name="pcode1" type="text" required>
+    </div>
+  </fieldset>
+  <fieldset id="billing">
+    <legend>Billing address</legend>
+    <div>
+      <label for="billing-checkbox">Same as shipping address:</label>
+      <input type="checkbox" id="billing-checkbox" checked>
+    </div>
+    <div>
+      <label for="name" class="billing-label disabled-label">Name: </label>
+      <input id="name" name="name" type="text" disabled required>
+    </div>
+    <div>
+      <label for="address2" class="billing-label disabled-label">Address: </label>
+      <input id="address2" name="address2" type="text" disabled required>
+    </div>
+    <div>
+      <label for="pcode2" class="billing-label disabled-label">Zip/postal code: </label>
+      <input id="pcode2" name="pcode2" type="text" disabled required>
+    </div>
+  </fieldset>
+
+  <div><button>Submit</button></div>
+</form>
+ +

Now onto the CSS. The most relevant parts to this example are as follows:

+ +
input[type="text"]:disabled {
+    background: #eee;
+    border: 1px solid #ccc;
+}
+
+.disabled-label {
+  color: #aaa;
+}
+ +

We've directly selected the inputs we want to disable using input[type="text"]:disabled, but we also wanted to gray out the corresponding text labels. These weren't quite as easy to select, so we've used a class to provide them with that styling.

+ +

Now finally, we've used some JavaScript to toggle the disabling of the billing address fields:

+ +
// Wait for the page to finish loading
+document.addEventListener('DOMContentLoaded', function () {
+
+  // Attach `change` event listener to checkbox
+  document.getElementById('billing-checkbox').addEventListener('change', toggleBilling);
+}, false);
+
+function toggleBilling() {
+  // Select the billing text fields
+  let billingItems = document.querySelectorAll('#billing input[type="text"]');
+  // Select the billing text labels
+  let billingLabels = document.querySelectorAll('.billing-label');
+
+  // Toggle the billing text fields and labels
+  for (let i = 0; i < billingItems.length; i++) {
+    billingItems[i].disabled = !billingItems[i].disabled;
+
+    if(billingLabels[i].getAttribute('class') === 'billing-label disabled-label') {
+      billingLabels[i].setAttribute('class', 'billing-label');
+    } else {
+      billingLabels[i].setAttribute('class', 'billing-label disabled-label');
+    }
+  }
+}
+ +

It uses the change event to let the user enable/disable the billing fields, and toggle the styling of the associated labels.

+ +

You can see the example in action below (also see it live here, and see the source code):

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/enabled-disabled-shipping.html", '100%', 600)}}

+ +

Read-only and read-write

+ +

In a similar manner to :disabled and :enabled, the :read-only and :read-write pseudo-classes target two states that form inputs toggle between. Read-only inputs have their values submitted to the server, but the user can't edit them, whereas read-write means they can be edited — their default state.

+ +

An input is set to read-only using the readonly attribute. As an example, imagine a confirmation page where the developer has sent the details filled in on previous pages over to this page, with the aim of getting the user to check them all in one place, add any final data that is needed, and then confirm the order by submitting. At this point, all the final form data can be sent to the server in one go.

+ +

Let's look at what a form might look like (see readonly-confirmation.html for the live example; also see the source code).

+ +

A fragment of the HTML is as follows — note the readonly attribute:

+ +
<div>
+  <label for="name">Name: </label>
+  <input id="name" name="name" type="text"
+         value="Mr Soft" readonly>
+</div>
+ +

If you try the live example, you'll see that the top set of form elements are not focusable, however the values are submitted when the form is submitted. We've also styled the read-only form controls using the :read-only pseudo-class, like so:

+ +
input:-moz-read-only, textarea:-moz-read-only {
+  border: 0;
+  box-shadow: none;
+  resize: none;
+  background-color: white;
+}
+
+input:read-only, textarea:read-only {
+  border: 0;
+  box-shadow: none;
+  resize: none;
+  background-color: white;
+}
+ +

Yes, you've guessed it — Firefox only supports it with a prefix, hence having to double up the ruleset.

+ +
+

: :enabled and read-write are two more pseudo-classes that you'll probably rarely use, given that they describe the default states of input elements.

+
+ +

ラジオとチェックボックスの状態 — チェック済み、既定、中間

+ +

As we've seen in earlier articles in the module, {{HTMLElement("input/radio", "radio buttons")}} and {{HTMLElement("input/checkbox", "checkboxes")}} can be checked or unchecked. But there are a couple of other states to consider too:

+ + + +

:checked

+ +

When checked, they will be matched by the {{cssxref(":checked")}} pseudo-class.

+ +

The most common use of this is to add a different style onto the checkbox/radiobutton when it is checked, in cases where you've removed the system default styling with appearance: none; and want to build the styles back up yourself. We saw examples of this in the previous article, when we talked about Using appearence: none on radios/checkboxes.

+ +

As a recap, the :checked code from our Styled radio buttons example looks like so:

+ +
input[type="radio"]::before {
+  display: block;
+  content: " ";
+  width: 10px;
+  height: 10px;
+  border-radius: 6px;
+  background-color: red;
+  font-size: 1.2em;
+  transform: translate(3px, 3px) scale(0);
+  transform-origin: center;
+  transition: all 0.3s ease-in;
+}
+
+input[type="radio"]:checked::before {
+  transform: translate(3px, 3px) scale(1);
+  transition: all 0.3s cubic-bezier(0.25, 0.25, 0.56, 2);
+}
+ +

You can try it out here:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/styling-examples/radios-styled.html", '100%', 200)}}

+ +

Basically, we build the styling for the radio button "inner circle" using the ::before pseudo element, but set a scale(0) transform on it. We then use a transition to make it nicely animate into view when the radio is selected/checked. The advantage of using a transform rather than transitioning width/height is that you can use transform-origin to make it grow from the center of the circle, rather than having it appear to grow from the circle's corner.

+ +

:default と :indeterminate

+ +

As mentioned above, the {{cssxref(":default")}} pseudo-class matches radios/checkboxes that are checked 既定では, on page load, even when unchecked. This could be useful for adding an indicator to a list of options to remind the user what the defaults (or starting options) were, in case they want to reset their choices.

+ +

Also mentioned above radios/checkboxes will be matched by the {{cssxref(":indeterminate")}} pseudo-class when they are in a state where they are neither checked nor unchecked. But what does this mean? Elements that are indeterminate include:

+ + + +

This isn't something you'll likely use very often. One use case could be an indicator to tell users that they really need to select a radio button before they move on.

+ +

Let's look at a couple of modified versions of the previous example that remind the user what the default option was, and style the radio buttons when indeterminate. Both of these have the following HTML structure for the inputs:

+ +
<p>
+  <input type="radio" name="fruit" value="cherry" id="cherry">
+  <label for="cherry">Cherry</label>
+  <span></span>
+</p>
+ +

For the :default example, we've added the checked attribute to the middle radio button input, so it will be selected 既定では when loaded. We then style this with the following CSS:

+ +
input ~ span {
+  position: relative;
+}
+
+input:default ~ span::after {
+  font-size: 0.7rem;
+  position: absolute;
+  content: "Default";
+  color: white;
+  background-color: black;
+  padding: 5px 10px;
+  right: -65px;
+  top: -3px;
+}
+ +

This provides a little "Default" label on the one the was originally selected when the page loaded. Note here we are using the general sibling combinator (~) rather than the adjacent sibling combinator (+) — we need to do this because the <span> does not come right after the <input> in the source order.

+ +

See the live result below:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/radios-checked-default.html", '100%', 200)}}

+ +
+

: You can also find the example live on GitHub at radios-checked-default.html (also see the source code.)

+
+ +

For the :indeterminate example, we've got no default selected radio button — this is important — if there was, then there would be no indeterminate state to style. We style the indeterminate radio buttons with the following CSS:

+ +
input[type="radio"]:indeterminate {
+  border: 2px solid red;
+  animation: 0.4s linear infinite alternate border-pulse;
+}
+
+@keyframes border-pulse {
+  from {
+    border: 2px solid red;
+  }
+
+  to {
+    border: 6px solid red;
+  }
+}
+ +

This creates a fun little animated border on the radio buttons, which hopefully indicates that you need to select one of them!

+ +

See the live result below:

+ +

{{EmbedGHLiveSample("learning-area/html/forms/pseudo-classes/radios-checked-indeterminate.html", '100%', 200)}}

+ +
+

: You can also find the example live on GitHub at radios-checked-indeterminate.html (also see the source code.)

+
+ +
+

: You can find an interesting example involving indeterminate states on the <input type="checkbox"> reference page.

+
+ +

その他の疑似クラス

+ +

There are a number of other pseudo-classes of interest, and we don't have space to write about them all in detail here. Let's talk about a few more that you should take the time to investigate.

+ +

The following are fairly well-supported in modern browsers:

+ + + +

The following are also interesting, but as yet not well-supported in browsers:

+ + + +

まとめ

+ +

This completes our look at UI pseudo-classes that relate to form inputs. Keep playing with them, and create some fun form styles! Next up, we'll move on to something different — client-side form validation.

+ +

{{PreviousMenuNext("Learn/Forms/Advanced_form_styling", "Learn/Forms/Form_validation", "Learn/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + diff --git a/files/ja/learn/forms/your_first_form/example/index.html b/files/ja/learn/forms/your_first_form/example/index.html new file mode 100644 index 0000000000..bddf242144 --- /dev/null +++ b/files/ja/learn/forms/your_first_form/example/index.html @@ -0,0 +1,113 @@ +--- +title: 例 +slug: Learn/Forms/Your_first_form/Example +tags: + - CodingScripting + - HTML + - Web + - ガイド + - フォーム + - 例 + - 初心者 + - 学習 +translation_of: Learn/Forms/Your_first_form/Example +--- +

これは最初の HTML フォームの記事のサンプルコードです。

+ +

簡単なフォーム

+ +

HTML コンテンツ

+ +
<form action="/my-handling-form-page" method="post">
+  <div>
+    <label for="name">Name:</label>
+    <input type="text" id="name" name="user_name">
+  </div>
+
+  <div>
+    <label for="mail">E-mail:</label>
+    <input type="email" id="mail" name="user_email">
+  </div>
+
+  <div>
+    <label for="msg">Message:</label>
+    <textarea id="msg" name="user_message"></textarea>
+  </div>
+
+  <div class="button">
+    <button type="submit">Send your message</button>
+  </div>
+</form>
+ +

CSS コンテンツ

+ +
form {
+  /* Just to center the form on the page */
+  margin: 0 auto;
+  width: 400px;
+
+  /* To see the limits of the form */
+  padding: 1em;
+  border: 1px solid #CCC;
+  border-radius: 1em;
+}
+
+div + div {
+  margin-top: 1em;
+}
+
+label {
+  /* To make sure that all label have the same size and are properly align */
+  display: inline-block;
+  width: 90px;
+  text-align: right;
+}
+
+input, textarea {
+  /* To make sure that all text field have the same font settings
+     By default, textarea are set with a monospace font */
+  font: 1em sans-serif;
+
+  /* To give the same size to all text field */
+  width: 300px;
+
+  -moz-box-sizing: border-box;
+       box-sizing: border-box;
+
+  /* To harmonize the look & feel of text field border */
+  border: 1px solid #999;
+}
+
+input:focus, textarea:focus {
+  /* To give a little highligh on active elements */
+  border-color: #000;
+}
+
+textarea {
+  /* To properly align multiline text field with their label */
+  vertical-align: top;
+
+  /* To give enough room to type some text */
+  height: 5em;
+
+  /* To allow users to resize any textarea vertically
+     It works only on Chrome, Firefox and Safari */
+  resize: vertical;
+}
+
+.button {
+  /* To position the buttons to the same position of the text fields */
+  padding-left: 90px; /* same size as the label elements */
+}
+
+button {
+  /* This extra magin represent the same space as the space between
+     the labels and their text fields */
+  margin-left: .5em;
+}
+ +

結果

+ +

{{ EmbedLiveSample('A_simple_form', '100%', '280') }}

+ +

 

diff --git a/files/ja/learn/forms/your_first_form/index.html b/files/ja/learn/forms/your_first_form/index.html new file mode 100644 index 0000000000..d3e646269f --- /dev/null +++ b/files/ja/learn/forms/your_first_form/index.html @@ -0,0 +1,313 @@ +--- +title: 初めてのフォーム +slug: Learn/Forms/Your_first_form +tags: + - Beginner + - CodingScripting + - Example + - Forms + - Guide + - HTML + - Learn + - Web + - ウェブ + - ガイド + - フォーム + - 初心者向け + - 学習 +translation_of: Learn/Forms/Your_first_form +--- +
+
{{LearnSidebar}}{{NextMenu("Learn/Forms/How_to_structure_a_web_form", "Learn/Forms")}}
+
+ +

このシリーズの最初の記事では、簡単なフォームの設計、HTML フォームコントロールとその他の HTML 要素を使用した正しい実装、CSS によるとても簡単なスタイル付け、データをサーバーに送る方法を含めた、ウェブフォームを作成する本当に初歩的な経験をします。サブトピックは、モジュールの後で詳しく展開していきます。

+ + + + + + + + + + + + +
前提条件:基本的なコンピューターリテラシーと、HTML を理解するを理解していること。
目的:ウェブフォームとは何か、何に使うのか、どうデザインするのか、サンプル事例に必要な基本の HTML 要素について熟知する。
+ +

ウェブフォームとは何か?

+ +

ウェブフォームとは、ユーザーと ウェブサイトやアプリケーションとの対話の要となるもののひとつです。フォームによって、ユーザーは ウェブサイトへデータを送ることができます。それらのデータはたいてい ウェブサーバーに送られて処理、保存されたり (モジュール後半のサーバーにデータを送るを見てください) 、クライアント側ですぐにインターフェイスを更新する(例えば、リストに他の項目を追加したり、UI の機能を表示/非表示にしたり)こともあります。

+ +

ウェブフォームは 1 つ以上のフォームコントロール(ウィジェットともいいます)と、フォーム全体を構成するのに役立つ追加要素 — よく HTML フォームと呼ばれます — とで作られます。それらのコントロールはテキストフィールド (単一行または複数行)、ドロップダウンボックス、ボタン、チェックボックスあるいはラジオボタンがあります。たいていは {{htmlelement("input")}} 要素を使って作成されますが、その他の要素もあります。

+ +

フォームコントロールは、特定フォームの値が入力されるのを強制する(フォーム検証)ためにもプログラミングされ、視力のある・盲目のユーザーいずれにとっても、用途を説明するラベルと対になります。

+ +

フォームを設計する

+ +

コードを書き始める前に、そこから離れてフォームについて考える時間をとるとよいでしょう。簡単なモデルを作ると、あなたがユーザーに入力を依頼したいデータの適切なセットを定義することの助けになります。ユーザー体験 (UX) の観点では、フォームが大規模になるとユーザーが不満を持って離れるリスクが高まると覚えておくことが重要です。簡単に、かつ集中するようにしてください。本当に必要なことだけをたずねてください。

+ +

フォームの設計は、サイトやアプリケーションを構築する際の大事なステップです。フォームのユーザー体験まで扱うと本記事の対象を超えてしまいますが、そこまで踏み込みたい場合は以下の記事をご覧ください。

+ + + +

本記事では、シンプルな連絡フォームを作成します。簡単に図を描いてみましょう。

+ +

The form to build, roughly sketch

+ +

このフォームには、3 つのテキストフィールドと 1 つのボタンがあります。要するに、ユーザーへ名前(Name)、メールアドレス(E-mail)、送信したいメッセージ(Message)をたずねます。ボタンを押すと、データをウェブサーバーに送信します。

+ +

HTML を書きましょう

+ +

これで、HTML に移ってフォームのコードを書く準備ができました。連絡フォームを作るために、以下の HTML 要素を使用します: {{HTMLElement("form")}}、{{HTMLElement("label")}}、{{HTMLElement("input")}}、{{HTMLElement("textarea")}}、{{HTMLElement("button")}} です。

+ +

前に進む前に、簡単な HTML テンプレートをローカルにコピーします — ここにフォームの HTML を入力します。

+ +

{{HTMLElement("form")}} 要素

+ +

すべてのフォームは、以下のように {{HTMLElement("form")}} 要素から始まります:

+ +
<form action="/my-handling-form-page" method="post">
+
+</form>
+ +

これは、フォームを正式に定義します。これは {{HTMLElement("div")}} 要素や {{HTMLElement("p")}} 要素と同様にコンテナ要素ですが、フォームの動作を設定するための固有の属性もサポートします。すべての属性は省略可能ですが、少なくとも action 属性と method属性は常に設定することがベストプラクティスであると考えられます。

+ + + +
+

: これらの属性がどのように働くかについて、詳しくはフォームデータの送信と取得で説明しています。

+
+ +

今は、上の {{htmlelement("form")}} 要素を HTML 本文に追加します。

+ +

{{HTMLElement("label")}}、{{HTMLElement("input")}} および {{HTMLElement("textarea")}} 要素でウィジェットを追加する

+ +

連絡フォームはとてもシンプルで 3 つのテキストフィールドを持っており、それぞれに 対応した {{HTMLelement("label")}} がついています。

+ + + +

HTML コードで、それらは以下のようになります:

+ +
<form action="/my-handling-form-page" method="post">
+ <ul>
+  <li>
+    <label for="name">Name:</label>
+    <input type="text" id="name" name="user_name">
+  </li>
+  <li>
+    <label for="mail">E-mail:</label>
+    <input type="email" id="mail" name="user_email">
+  </li>
+  <li>
+    <label for="msg">Message:</label>
+    <textarea id="msg" name="user_message"></textarea>
+  </li>
+ </ul>
+</form>
+ +

フォームのコードを上に見えるようなものに更新してください。

+ +

{{HTMLelement("li")}} 要素はコードを扱いやすく構造化するとともに、スタイル設定を容易にするため (後述) に置いています。すべての {{HTMLElement("label")}} 要素における、 for 属性の使い方に注目してください。これは、ラベルとフォームウィジェットを関連付けるための正式な方法です。この属性は、対応するウィジェットの id を参照します。

+ +

これには利点があります — フォームコントロールにラベルを関連付けて、ユーザーがマウス、トラックパッド、タッチデバイスでラベルをクリックすると対応するウィジェットがアクティブになり、スクリーンリーダーのユーザーに読み上げられるアクセシブルな名前が提供されます。この属性の利点をさらに知りたいのでしたら、こちらの記事ですべてを詳しく説明しています: HTML フォームの構築方法.

+ +

{{HTMLElement("input")}} 要素でもっとも重要な属性は、type 属性です。この属性は {{HTMLElement("input")}} 要素の見た目や動作を定義するため、とても重要です。これは要素を根本的に変えるものですから、注意してください。詳しく知りたい場合は、ネイティブフォームウィジェットの記事をご覧ください。

+ + + +

大事なことを言い忘れましたが、<input><textarea></textarea> の構文に注意してください。これは HTML の変わったことの 1 つです。<input> タグは空要素です。つまり、終了タグは不要です。一方 {{HTMLElement("textarea")}} は空の要素ではないので、適切な終了タグで閉じる必要があります。これは HTML フォームの特定の機能、つまりデフォルト値の定義方法に影響があります。{{HTMLElement("input")}} 要素のデフォルト値を定義するには、次のように value 属性を使う必要があります。

+ +
<input type="text" value="デフォルトではこの要素にはこの文章が挿入されます" />
+ +

また、{{HTMLElement("textarea")}} のデフォルト値を定義したい場合は、デフォルト値を {{HTMLElement("textarea")}} の開始タグと終了タグの間に書いてください。以下のとおりです:

+ +
<textarea>
+デフォルトではこの要素にはこの文章が挿入されます
+</textarea>
+ +

{{HTMLElement("button")}} を追加する

+ +

フォームはほぼできあがりました。あとは、ユーザーがフォームに記入したらデータを"送信"するためのボタンを追加します。これは、{{HTMLElement("button")}} 要素を使用して簡単にできます: 閉じタグの </ul> の直前に次の行を追加します:

+ +
<li class="button">
+  <button type="submit">メッセージを送信</button>
+</li>
+ +

{{htmlelement("button")}} 要素は type 属性を受け付けます。3 種類の値を受け付けて、これは submitresetbutton です。

+ + + +
+

メモ: {{HTMLElement("input")}} 要素で対応する type を指定して、ボタンを作成することもできます。例えば <input type="submit"> のように。{{HTMLElement("button")}} 要素との大きな違いは、{{HTMLElement("input")}} 要素ではラベルとしてプレーンテキストしか許容しませんが、{{HTMLElement("button")}} 要素ではすべての HTML コンテンツを使用して、もっと複雑でクリエイティブなボタンコンテンツを作成できます。

+
+ +

CSS でフォームを少し見栄えよくしましょう

+ +

フォームのHTMLコードの記述ができました。このフォームをお気に入りのブラウザーで見ると、見栄えがよくないでしょう。

+ +
+

メモ: あなたの HTML コードが正しくないと考える場合、完成例と比較してみてください — first-form.html にあります (ライブ版も見てください)。

+
+ +

フォームを素敵にスタイル付けるのはトリッキーです。それはこの記事でフォームスタイリングについて教える範囲を超えています。なので当面はまずまずの見ばえとなる CSS を追加するだけにしましょう。

+ +

最初に、ページの HTML の head 内に {{htmlelement("style")}} 要素を追加します。次のようになります:

+ +
<style>
+
+</style>
+ +

style タグの中に、次の CSS を追加します:

+ +
form {
+  /* フォームをページの中央に置く */
+  margin: 0 auto;
+  width: 500px;
+  /* フォームの範囲がわかるようにする */
+  padding: 1em;
+  border: 1px solid #CCC;
+  border-radius: 1em;
+}
+
+ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+form li + li {
+  margin-top: 1em;
+}
+
+label {
+  /* すべてのラベルを同じサイズにして、きちんと揃える */
+  display: inline-block;
+  width: 90px;
+  text-align: right;
+}
+
+input, textarea {
+  /* すべてのテキストフィールドのフォント設定を一致させる
+     デフォルトで、textarea は等幅フォントが設定されている */
+  font: 1em sans-serif;
+
+  /* すべてのテキストフィールドを同じサイズにする */
+  width: 300px;
+  box-sizing: border-box;
+
+  /* テキストフィールドのボーダーの外見を同一にする */
+  border: 1px solid #999;
+}
+
+input:focus,
+textarea:focus {
+  /* アクティブな要素を少し強調する */
+  border-color: #000;
+}
+
+textarea {
+  /* 複数行のテキストフィールドをラベルにきちんと揃える */
+  vertical-align: top;
+
+  /* テキスト入力に十分な領域を与える */
+  height: 5em;
+}
+
+.button {
+  /* ボタンを他のテキストフィールドと同じ場所に置く */
+  padding-left: 90px; /* label 要素と同じサイズ */
+}
+
+button {
+  /* このマージンは、ラベルとテキストフィールドの間のスペースと
+     おおよそ同じスペースを表す */
+  margin-left: .5em;
+}
+ +

保存して再読み込みすると、フォームがよりきれいになりました。

+ +
+

メモ: Github の first-form-styled.html にあります(ライブ版も見てください)。

+
+ +

データをウェブサーバーに送信する

+ +

最後の、またもっともややこしいであろう部分が、サーバー側でのフォームデータの扱いです。{{HTMLElement("form")}} 要素は  action 属性と method 属性により、どこへどのようにデータを送信するかを定義できます。

+ +

フォームコントロールに name をつけます。これらの名前はクライアント側とサーバー側の両側で重要です。ブラウザー側ではそれぞれのデータにどのような名前をつけるかを示すものであり、サーバー側では名前によってそれぞれのデータを扱うことができます。フォームデータは名前/値のペアとしてサーバーに送信されます。

+ +

データに名前をつけるために、各々のデータを集めるフォームウィジェットの name 属性を使用しなければなりません。ここでもフォームのコードで見てみましょう:

+ +
<form action="/my-handling-form-page" method="post">
+ <ul>
+  <li>
+    <label for="name">Name:</label>
+    <input type="text" id="name" name="user_name" />
+  </li>
+  <li>
+    <label for="mail">E-mail:</label>
+    <input type="email" id="mail" name="user_email" />
+  </li>
+  <li>
+    <label for="msg">Message:</label>
+    <textarea id="msg" name="user_message"></textarea>
+  </li>
+
+  ...
+
+ +

この例では、フォームはそれぞれ "user_name"、"user_email"、"user_message" と名付けられた 3 つのデータを送信します。これらのデータは URL "/my-handling-form-page" へ、HTTP POST メソッドで送信します。

+ +

サーバー側では URL "/my-handling-form-page" のスクリプトが、HTTP リクエストに埋め込まれた 3 つのキーおよび値のアイテムリストとしてデータを受け取ります。スクリプトがデータを処理する方法は、あなた次第です。各サーバーサイド言語 (PHP、Python、Ruby、Java、C# など) は、これらのデータを扱う仕組みを持っています。これは本ガイドで踏み込んでいく範囲を超えますが、詳しく知りたい場合はフォームデータの送信と取得の記事にサンプルを載せていますのでご覧ください。

+ +

まとめ

+ +

おめでとうございます! 初めてのウェブフォームが完成しました。こちらが最終結果のデモです。

+ +

{{ EmbedLiveSample('A_simple_form', '100%', '240', '', 'Learn/Forms/Your_first_form/Example') }}

+ +

これはほんの開始点です、しかし — さて、より深く見ていくときが来ました。ウェブフォームはここで見てきたものよりさらに強力であり、ガイドの他の記事で残りの部分を習得できます。

+ +

{{NextMenu("Learn/Forms/How_to_structure_a_web_form", "Learn/Forms")}}

+ +

このモジュール

+ + + +

上級トピック

+ + -- cgit v1.2.3-54-g00ecf