【Webフォーム】ユーザーデータの扱い方-2(mdnノート)

75be4c7f0a61b86daf6cbe11f46db79e.png

8. UI疑似クラス

8.1 疑似クラスにはどのような種類がありますか?

(CSS 2.1 時点で) 使用できるプリミティブ フォーム関連の疑似クラスは次のとおりです。

  • :hover : マウス ポインターが上にある場合にのみ要素を選択します。

  • :focus : 要素がフォーカスされているとき (つまり、キーボードのタブ キーで選択されているとき) にのみ要素を選択します。

  • :active : 要素がアクティブなとき (つまり、クリックまたはキーボードの Return / Enter キーによって選択されたとき) にのみ要素を選択します。

CSS セレクター レベル 3 と CSS 基本 UI レベル 3 では、  HTML フォームに関連する疑似クラスがさらに追加されました。簡単に説明すると、主な内容は次のとおりです。

  • :required および :optional : 必須またはオプションのフォーム コントロール。

  • :valid と :invalid、:in-range と :out-of-range : フォーム コントロールの場合、設定されたフォーム検証制約に従って有効/無効、または範囲内/範囲外です

  • :enabled および :disabled、 :read-only および :read-write :有効または無効になっているフォーム コントロール(たとえば、disabled HTML 属性が設定されている)、および読み取り/書き込みまたは読み取り専用のフォーム コントロール (たとえば、 、読み取り専用の HTML 属性が設定されます)。

  • :checked、:indeterminate、および :default : それぞれ、不定状態 (チェック済みでもチェック済みでもない) にあるチェック済みのチェックボックスとラジオ ボタン、およびページ読み込み時にデフォルトで選択されるオプション (<input type="checkbox" など) > チェックされた属性セットを持つ、または選択された属性セットを持つ <option> 要素)。

注: ここで説明する疑似クラスの多くは、フォーム コントロールの検証状態 (データが有効かどうか) に基づいた検証制約の設定と制御に関係しています。検証制約の設定と制御については、次の記事「クライアント」で詳しく学びます。サイド フォーム検証の知識ですが、今のところは混乱しないようにフォーム検証については単純にしておきます。

8.2 入力コントロールのスタイル設定(必須かどうか)

フォーム入力が必須(フォームを送信する前に入力する必要がある)かオプションか
<input>、<select>、および <textarea> 要素にはすべて利用可能な必須属性があり、これを設定すると、フォームを正常に送信する前にコントロールに入力する必要があることを意味します。例えば:

2e5e578a7ed57048d6b4d1d69a1f0327.png

<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>

ここでは、姓名は必須ですが、電子メール アドレスはオプションです。

:required および :optional 擬似クラスを使用して両方の状態を照合できます 必須のフォーム コントロールには黒い枠があり、オプションのフォーム コントロールには銀色の枠が付いています。

input:required {
  border: 1px solid black;
}

input:optional {
  border: 1px solid silver;
}

フォームに記入せずに送信し、ブラウザがデフォルトで表示するクライアント側の検証エラー メッセージを観察します。
Web 上の標準的な規則では、必須状態をアスタリスク (*) で表し、「必須」という単語を関連するコントロールに関連付けます。

注: 同じ名前のラジオ ボタンのグループ内のラジオ ボタンに required 属性がある場合、いずれかのラジオ ボタンが選択されるまですべてのラジオ ボタンは無効になりますが、この属性が割り当てられたラジオ ボタンのみが実際に :required に一致します。

8.3 疑似クラスを使用したコンテンツの生成

::before および ::after 擬似要素と content 属性を使用して 、影響を受ける要素の前後にコンテンツのチャンクを表示します。
このコンテンツはDOM に追加されないため、スクリーン リーダーには表示されず、ドキュメントのスタイルの一部ですこれは、ラベルやアイコンなどの要素に視覚的なインジケーターを追加し
たいが、支援技術で検出できないようにしたい場合に便利です。

たとえば、 カスタム ラジオ ボタンの例では、生成されたコンテンツを使用して、ラジオ ボタンが選択されたときに内側の円の位置とアニメーションを処理します

79b2b7e96865bc8c609d8f89266bca43.png

<fieldset>
      <legend>Choose your favourite fruit</legend>

      <p>
        <label>
          <input type="radio" name="fruit" value="cherry">
          Cherry
        </label>
      </p>
      <p>
        <label>
          <input type="radio" name="fruit" value="banana">
          Banana
        </label>
      </p>
      <p>
        <label>
          <input type="radio" name="fruit" value="strawberry">
          Strawberry
        </label>
      </p>
    </fieldset>
input[type="radio"] {
  appearance: none; /* 移除默认外观 */
}

input[type="radio"] {
  width: 20px; /* 设置宽度为20px */
  height: 20px; /* 设置高度为20px */
  border-radius: 10px; /* 设置圆角半径为10px */
  border: 2px solid gray; /* 设置边框为2px实线灰色 */
  /* 调整单选框在文本基线上的位置 */
  vertical-align: -2px;
  outline: none; /* 移除轮廓线 */
}

input[type="radio"]::before {
  display: block; /* 设置display属性为block */
  content: " "; /* 设置内容为空格 */
  width: 10px; /* 设置宽度为10px */
  height: 10px; /* 设置高度为10px */
  border-radius: 6px; /* 设置圆角半径为6px */
  background-color: red; /* 设置背景颜色为红色 */
  font-size: 1.2em; /* 设置字体大小为1.2em */
  transform: translate(3px, 3px) scale(0); /* 设置变换效果,平移3px并缩放为0 */
  transform-origin: center; /* 设置变换原点为中心 */
  transition: all 0.3s ease-in; /* 设置过渡效果,所有属性在0.3秒内以ease-in方式过渡 */
}

input[type="radio"]:checked::before {
  transform: translate(3px, 3px) scale(1); /* 当单选框被选中时,设置变换效果,平移3px并缩放为1 */
  transition: all 0.3s cubic-bezier(0.25, 0.25, 0.56, 2); /* 设置过渡效果,所有属性在0.3秒内以cubic-bezier方式过渡 */
}

前の必須/オプションの例に戻りますが、今回は入力自体の外観は変更せず、生成されたコンテンツを使用してインジケーター ラベルを追加します。

e7673aeb49272f194a1108d7edd3563a.png

<form>
  <fieldset> <!-- 定义字段集 -->
    <legend>Feedback form</legend> <!-- 定义字段集标题 -->
    <p>Required fields are labelled with "required".</p> <!-- 添加段落说明 -->
    <div>
      <label for="fname">First name: </label> <!-- 定义标签 -->
      <input id="fname" name="fname" type="text" required> <!-- 定义文本输入框,要求必填 -->
      <span></span>
    </div>
    <div>
      <label for="lname">Last name: </label> <!-- 定义标签 -->
      <input id="lname" name="lname" type="text" required> <!-- 定义文本输入框,要求必填 -->
      <span></span>
    </div>
    <div>
      <label for="email">Email address (include if you want a response): </label> <!-- 定义标签 -->
      <input id="email" name="email" type="email"> <!-- 定义电子邮件输入框 -->
      <span></span>
    </div>
    <div><button>Submit</button></div> <!-- 定义提交按钮 -->
  </fieldset>
</form>
body {
  font-family: 'Josefin Sans', sans-serif; /* 设置字体为'Josefin Sans' */
  margin: 20px auto; /* 设置顶部和底部边距为20px,并使body水平居中 */
  max-width: 460px; /* 设置body的最大宽度为460px */
}

fieldset {
  padding: 10px 30px 0; /* 设置字段集的内边距 */
}

legend {
  color: white; /* 设置字段集标题的颜色为白色 */
  background: black; /* 设置字段集标题的背景颜色为黑色 */
  padding: 5px 10px; /* 设置字段集标题的内边距 */
}
/*由于 input 和 label 都设置了 width: 100%,span 会沉到输入框下一行中。为了修复这一点,我们令父 <div> 为弹性容器,同时令它如果内容变得太长,就把它的内容换行:这样做的效果是,标签和输入是分开的,因为它们都是 width: 100%,但 <span> 的宽度是 0,所以它可以和 input 位于同一行。*/
fieldset > div {
  margin-bottom: 20px; /* 设置字段集内直接子div元素的底部边距为20px */
  display: flex; /* 设置display属性为flex */
  flex-flow: row wrap; /* 设置flex容器为行方向并换行 */
}

button, label, input {
  display: block; /* 设置display属性为block */
  font-family: inherit; /* 继承父元素的字体 */
  font-size: 100%; /* 设置字体大小为100% */
  padding: 0; /* 设置内边距为0 */
  margin: 0; /* 设置外边距为0 */
  box-sizing: border-box; /* 设置盒模型为border-box */
  width: 100%; /* 设置宽度为100% */
  padding: 5px; /* 设置内边距为5px */
  height: 30px; /* 设置高度为30px */
}
input {
  box-shadow: inset 1px 1px 3px #ccc; /* 添加内阴影效果 */
  border-radius: 5px; /* 添加圆角效果 */
}

input:hover, input:focus {
  background-color: #eee; /* 当鼠标悬停或输入框获得焦点时,设置背景颜色为#eee */
}

input + span {
  position: relative; /* 设置相对定位 */
}

input:required + span::after {
  font-size: 0.7rem; /* 设置字体大小为0.7rem */
  position: absolute; /* 设置绝对定位 */
  content: "required"; /* 设置内容为"required" */
  color: white; /* 设置颜色为白色 */
  background-color: black; /* 设置背景颜色为黑色 */
  padding: 5px 10px; /* 设置内边距为5px和10px */
  top: -26px; /* 设置顶部边距为-26px */
  left: -70px; /* 设置左边距为-70px */
}

button {
  width: 60%; /* 设置宽度为60% */
  margin: 0 auto; /* 水平居中按钮 */
}

8.4 データが有効かどうかに基づいてコントロールにスタイルを追加する

フォーム検証におけるもう 1 つの非常に重要な基本概念は、フォーム コントロールのデータが有効かどうか、そして制約のあるフォーム コントロールをこれらの状態に従って配置できるかどうかです。
:valid および :invalid :
valid および :invalid 疑似クラスを使用して、フォーム コントロールを見つけます

  • 制約検証のないフォーム コントロールは常に有効であるため、常に :valid と一致します。

  • 必須セットがあり、値が設定されていないフォームコントロールは無効です。これらは :invalid および :required に一致します。

  • <input type="email"> や <input type="url"> などの組み込みの validation を持つコントロールは、 :invalid と一致します (ただし、空の場合は有効です)

  • 現在の値が min 属性と max 属性で指定された範囲制限の外にあるコントロールは、:invalid と一致しますが、後で説明するように、:out-of-range とも一致します。

  • 要素 :valid/:invalid を一致させる方法は他にもいくつかあります。これについては、クライアント側のフォーム検証の記事で説明します。

単純な:valid/:invalid の例:

4891f6c9397bbe31d4e14fb2ec0c303e.png

input + span {
  position: relative;/* 设置相对定位 */
}

input + span::before {
  position: absolute; /* 设置绝对定位 */
  right: -20px; /* 设置右边距为-20px */
  top: 5px; /* 设置顶部边距为5px */
}

input:invalid {
  border: 2px solid red; /* 当输入无效时,设置边框为2px实线红色 */
}

input:invalid + span::before {
  content: '?'; /* 当输入无效时,在相邻的span元素的:before伪元素中添加"?" */
  color: red; /* 设置颜色为红色 */
}

input:valid + span::before {
  content: '?'; /* 当输入有效时,在相邻的span元素的:before伪元素中添加"?" */
  color: green; /* 设置颜色为绿色 */
}

<span> をposition:relativeに設定して、生成されたコンテンツを相対的に配置できるようにしますフォームのデータが有効であるか無効であるかに応じて、生成された異なるコンテンツを絶対に配置します (それぞれ緑のチェックボックスまたは赤のバツ)。無効なデータに少し緊急性を加えるために、無効な場合は入力データを赤い枠線で太くしました。

注: ::after を使用して「必須」タグを追加しているため、これらのタグを追加するには ::before を使用します

範囲内データと範囲外データ
について考慮すべき 2 つの関連する疑似クラス( in-range と :out-of-range ) があります。これらは、指定された範囲内または範囲外のデータに対して、範囲制限が min および max でそれぞれ指定された数値入力と一致します。

注: 数値入力タイプには、日付、月、週、時刻、日時ローカル、数値、範囲が含まれます。範囲内のデータを持つ入力は :valid 疑似クラスによっても照合され、範囲外のデータを持つ入力も :invalid 疑似クラスによって照合されます。

例:

数値入力は次のようになります。

<div>
  <label for="age">Age (must be 12+): </label>
  <input id="age" name="age" type="number" min="12" max="120" required />
  <span></span>
</div>

CSS スタイルは次のようになります。

input + span {
  position: relative; /* 设置相对定位 */
}

input + span::after {
  font-size: 0.7rem; /* 设置字体大小为0.7rem */
  position: absolute; /* 设置绝对定位 */
  padding: 5px 10px; /* 设置内边距为5px和10px */
  top: -26px; /* 设置顶部边距为-26px */
}

input:required + span::after {
  color: white; /* 当输入要求必填时,设置颜色为白色 */
  background-color: black; /* 设置背景颜色为黑色 */
  content: "Required"; /* 设置内容为"Required" */
  left: -70px; /* 设置左边距为-70px */
}

input:out-of-range + span::after {
  color: white; /* 当输入超出范围时,设置颜色为白色 */
  background-color: red; /* 设置背景颜色为红色 */
  width: 155px; /* 设置宽度为155px */
  content: "Outside allowable value range"; /* 设置内容为"Outside allowable value range" */
  left: -182px; /* 设置左边距为-182px */
}

beae05b75e3c02d6abf144ff40ceee71.png

数値入力が必須である場合と範囲外である場合の両方が考えられます。では、どうなるでしょうか? :out-of-range ルールはソース コード内で :required ルールよりも後に現れるため、カスケード ルールが作用し、「範囲外」メッセージが表示されます。

ページが最初に読み込まれると、赤い十字と枠線が表示され、「必須」と表示されます。有効な年齢 (12 ~ 120 歳の範囲) を入力すると、そのエントリが有効になります。ただし、年齢入力を範囲外に変更すると、「必須」ではなく「許容値範囲外」というメッセージが表示されます

8.5 スタイル設定の有効化または無効化、読み取り専用または読み取り/書き込み入力コントロール

有効な要素はアクティブ化できる要素であり、選択、クリック、入力などが可能です。一方、無効化された要素はいかなる方法でも操作できず、そのデータはサーバーに送信されることさえありません: enabled および :disabled を使用して検索
できますこれを行う例を見てみましょう。その HTML コードは、テキスト入力と、請求先住所を無効にするためにオンとオフを切り替えるチェックボックスを備えたシンプルなフォームです。請求先住所フィールドはデフォルトでは無効になっています実行中のバージョンはここで見ることができます。

8f6e1226e1dcf30a4dade9f285cfab10.png

<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>
input[type="text"]:disabled { /* 选择所有被禁用的文本输入框 */
  background: #eee; /* 背景颜色为浅灰色 */
  border: 1px solid #ccc; /* 边框为1像素宽,实线,颜色为深灰色 */
}

.disabled-label { /* 选择所有类名为 disabled-label 的元素 */
  color: #aaa; /* 文本颜色为深灰色 */
}

最後に、JavaScript コードを使用して、請求先住所フィールドの無効状態を切り替えます

// 等待页面完成加载
document.addEventListener(
  "DOMContentLoaded",
  () => {
    // 向复选框附加 `change` 事件
    document
      .getElementById("billing-checkbox")
      .addEventListener("change", toggleBilling);
  },
  false,/*布尔值如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。*/
);

function toggleBilling() {
  // 选择账单文本字段
  const billingItems = document.querySelectorAll('#billing input[type="text"]');
  // 选择账单文本标签
  const billingLabels = document.querySelectorAll(".billing-label");

  // 切换账单文本字段和标签
  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");
    }
  }
}

これは、 変更イベントを使用して、ユーザーが請求フィールドを有効/無効にしたり、関連付けられたラベルのスタイルを切り替えたりできるようにします

読み取り専用または読み取り/書き込み
:disabled および :enabled と同様に、:read-only および :read-write 擬似クラスは、 フォーム入力の 2 つの状態を切り替えます。読み取り専用入力は値をサーバーに送信しますが、ユーザーは値を編集できません。一方、読み取り/書き込み入力は編集できることを意味します。これがデフォルトの状態です。readonly 属性を
使用して 、入力を読み取り専用にします

として、ユーザーがすべての詳細を 1 か所で確認し、必要な最終データを追加して、送信して注文を確認できるようにすることを目的として、開発者が前のページで入力した詳細を送信する確認ページを想像してください。それ。この時点で、すべての最終フォーム データを一度にサーバーに送信できます。(実行例については、readonly-confirmation.html を参照してください。

4d0bfe25d5bdcd7ed58ca8a138a31077.png

HTML のスニペットは次のとおりです 。readonly 属性に注意してください。

<div>
  <label for="name">Name: </label>
  <input id="name" name="name" type="text" value="Mr Soft" readonly />
</div>

この例を実行してみると、フォーム要素の最上位グループがフォーカス可能ではないことがわかり、フォームが送信されると、その値が送信されます。:read-only および :read-write 疑似クラスを使用して、フォーム コントロールのスタイルを設定し
ます

:is(
    input:read-only,
    input:-moz-read-only,
    textarea:-moz-read-only,
    textarea:read-only
  ) {
  /* 选择所有只读的输入框和文本域 */
  border: 0; /* 边框宽度为0 */
  box-shadow: none; /* 没有阴影 */
  background-color: white; /* 背景颜色为白色 */
}

:is(textarea:-moz-read-write, textarea:read-write) {
  /* 选择所有可写的文本域 */
  box-shadow: inset 1px 1px 3px #ccc; /* 内阴影为1像素宽,颜色为深灰色 */
  border-radius: 5px; /* 边框圆角半径为5像素 */
}

注:  :enabled と :read-write は、入力要素のデフォルト状態を 記述する、おそらくほとんど使用されない他の 2 つの疑似クラスです

8.6 ラジオボタンとチェックボタンの状態 - 選択状態、デフォルト状態、および中間状態

ラジオボタンとチェックボックスはオンまたはオフにできます。ただし、考慮すべき他の状態もいくつかあります

  • :default : ページ読み込み時にデフォルトでチェックされている (つまり、checked 属性を設定することによって) ラジオ ボタン/チェックボックスと一致します。これらは、ユーザーがチェックを外した場合でも、:default 疑似クラスと一致します 。

  • :indeterminate : ラジオ ボタン/チェックボックスがオンでも選択解除されていない場合、それらは中間状態であり、  :indeterminate 擬似クラスと一致します。詳細は以下に記載されています。

:checked
ラジオ ボタンまたはチェックボックスがチェックされている場合、それらは :checked 擬似クラスによって照合されます。
最も一般的な使用法は、チェックボックスまたはラジオ ボタンがチェックされているときに別のスタイルを追加することです。この場合は、システムのデフォルトのスタイルを削除しています。外観: none; の場合
スタイル付きラジオ ボタンの例の :checked コードは次のようになります。

input[type="radio"]::before {
  /* 选择所有单选按钮的伪元素 ::before */
  display: block; /* 显示为块级元素 */
  content: " "; /* 内容为空格 */
  width: 10px; /* 宽度为10像素 */
  height: 10px; /* 高度为10像素 */
  border-radius: 6px; /* 边框圆角半径为6像素 */
  background-color: red; /* 背景颜色为红色 */
  font-size: 1.2em; /* 字体大小为1.2em */
  transform: translate(3px, 3px) scale(0); /* 平移3像素并缩放为0 */
  transform-origin: center; /* 变换原点为中心 */
  transition: all 0.3s ease-in; /* 过渡时间为0.3秒,缓动函数为ease-in */
}

input[type="radio"]:checked::before {
  /* 选择所有被选中的单选按钮的伪元素 ::before */
  transform: translate(3px, 3px) scale(1); /* 平移3像素并缩放为1 */
  transition: all 0.3s cubic-bezier(0.25, 0.25, 0.56, 2); /* 过渡时间为0.3秒,缓动函数为cubic-bezier(0.25,0.25,0.56,2) */
}

2b053d0ec30ec447caf309265997a011.png

基本的に、::before 疑似要素を使用してラジオ ボタンの「内側の円」のスタイルを設定し、トランジション
を使用し て選択時に素敵なアニメーション を与えますトランジションの幅/高さの代わりに変換を使用することの良い点は、transform-origin を使用して、円の角から成長するように見せるのではなく、円の中心から成長させることができることです

:default および :indeterminate
:default 擬似クラスは、チェックされていない場合でも、ページ読み込み時にデフォルトでチェックされているラジオ ボタンまたはチェックボックスと一致します上記のラジオ ボタンまたはチェックボックスは、オンでもオフでもない場合、:indeterminate 擬似クラスと一致します。不確実な要素には次のようなものがあります。

  • <input/radio> input、同じ名前のグループ内のすべてのラジオ ボタンがオフの場合

  • <input/checkbox> JavaScript コードによって indeterminate 属性が true に設定される入力。

  • 値のない <progress> 要素。

デフォルトのオプションが何であるかをユーザーに思い出させ不確かな場合にラジオ ボタンのスタイルを設定する、前の例のいくつかの修正バージョン

<fieldset>
      <legend>Choose your favourite fruit</legend>

      <p>
        <input type="radio" name="fruit" value="cherry" id="cherry">
        <label for="cherry">Cherry</label>
        <span></span>
      </p>
      <p>
        <input type="radio" name="fruit" value="banana" id="banana" checked>
        <label for="banana">Banana</label>
        <span></span>
      </p>
      <p>
        <input type="radio" name="fruit" value="strawberry" id="strawberry">
        <label for="strawberry">Strawberry</label>
        <span></span>
      </p>
    </fieldset>

:default の例では、checked 属性を中央のラジオ ボタン input に追加したため、load 時にデフォルトでチェックされます次に、次の CSS を使用してスタイルを設定します。

input ~ span {
  /* 选择所有与 input 元素同级且在其后面的 span 元素 */
  position: relative; /* 定位方式为相对定位 */
}

input:default ~ span::after {
  /* 选择所有与默认 input 元素同级且在其后面的 span 元素的伪元素 ::after */
  font-size: 0.7rem; /* 字体大小为0.7rem */
  position: absolute; /* 定位方式为绝对定位 */
  content: "Default"; /* 内容为 "Default" */
  color: white; /* 文本颜色为白色 */
  background-color: black; /* 背景颜色为黑色 */
  padding: 5px 10px; /* 内边距为5像素上下,10像素左右 */
  right: -65px; /* 距离右边界-65像素 */
  top: -3px; /* 距离上边界-3像素 */
}

ページの読み込み時に最初に選択された項目には、小さな「デフォルト」タグが提供されます。ここでは、隣接兄弟コンビネータ (+) の代わりにユニバーサル兄弟コンビネータ (~)
を使用しています。 注:この例のライブ デモは、GitHub の radios-checked-default.html にもあります

:indeterminate の例では 、デフォルトで選択されたラジオ ボタンがありません。次の CSS を使用して不確定ラジオ ボタンをデザインします

c182079f99dc8548f124714323b3727a.png

input[type="radio"]:indeterminate {
  /* 选择所有处于不确定状态的单选按钮 */
  border: 2px solid red; /* 边框为2像素宽,实线,颜色为红色 */
  animation: 0.4s linear infinite alternate border-pulse; /* 动画名称为border-pulse,持续时间为0.4秒,线性缓动函数,无限循环,交替播放 */
}

@keyframes border-pulse {
  /* 定义关键帧动画 border-pulse */
  from {
    /* 起始状态 */
    border: 2px solid red; /* 边框为2像素宽,实线,颜色为红色 */
  }

  to {
    /* 结束状态 */
    border: 6px solid red; /* 边框为6像素宽,实线,颜色为红色 */
  }
}

注: この例の実例は、GitHub (radios-checked-indeterminate.html) にもあります。

選択された
状態と選択されていない状態に加えて、チェック ボックスには 3 番目の状態があります: indeterminate 。JavaScriptによって設定 されるHTMLInputElement オブジェクトの indeterminateプロパティほとんどのブラウザでは、不定状態のチェック ボックスにはボックスの内側に水平線が表示されますほとんどの場合、一般的なのは、チェックボックスがいくつかのサブオプション (チェックボックスも) を「所有」している場合です。すべてのサブオプションがオンになっている場合は、所有するチェックボックスがオンになり、どれもオンになっていない場合は、所有するチェックボックスがオフになります。1 つ以上のサブオプションの状態が他のサブオプションと異なる場合、 所有するチェックボックスは不定状態になります


この例では、レシピのために収集した材料を記録します。材料のチェックボックスをオンまたはオフにすると、JavaScript 関数によってチェックされた材料の合計数がチェックされます。

  • 何もチェックされていない場合は、レシピ名のチェックボックスがオフになります。

  • レシピ名のチェックボックスは、どちらかまたは両方がチェックされている場合は不定(中間)状態となります。

  • 3 つすべてがチェックされている場合は、レシピ名のチェック ボックスがオンになります。

const overall = document.querySelector('#enchantment'); // 选择 id 为 enchantment 的元素
const ingredients = document.querySelectorAll('ul input'); // 选择所有 ul 元素下的 input 元素

overall.addEventListener('click', (e) => {
  e.preventDefault(); // 阻止默认事件
});

for (const ingredient of ingredients) {
  ingredient.addEventListener('click', updateDisplay); // 向每个 ingredient 元素附加 click 事件
}

function updateDisplay() {
  let checkedCount = 0; // 定义变量 checkedCount 并初始化为0
  for (const ingredient of ingredients) {
    if (ingredient.checked) { // 如果 ingredient 元素被选中
      checkedCount++; // checkedCount 加1
    }
  }

  if (checkedCount === 0) { // 如果 checkedCount 等于0
    overall.checked = false; // overall 元素未选中
    overall.indeterminate = false; // overall 元素不处于不确定状态
  } else if (checkedCount === ingredients.length) { // 如果 checkedCount 等于 ingredients 的长度
    overall.checked = true; // overall 元素被选中
    overall.indeterminate = false; // overall 元素不处于不确定状态
  } else { // 否则
    overall.checked = false; // overall 元素未选中
    overall.indeterminate = true; // overall 元素处于不确定状态
  }
}

8.7 その他の疑似クラス

これらの疑似クラスは、最新のブラウザでかなりよくサポートされています

  • :focus-within 擬似クラスは、すでにフォーカスを持つ要素、またはすでにフォーカスを持つ要素を含む要素と一致しますこれは、フォーム内の入力がフォーカスされているときにフォーム全体を何らかの方法で強調表示したい場合に便利です。

  • :focus-visible 疑似クラスは、 (タッチやマウスではなく) キーボード操作を介してフォーカスを取得する ** 要素と一致します。** これは、キーボード フォーカスとマウス (またはその他) フォーカスの異なるスタイルを表示する場合に便利です。

  • :placeholder-shon 疑似クラスは、要素の値が空であるため、プレースホルダー (つまり、placeholder 属性のコンテンツ) が表示されている <input> 要素と <textarea> 要素に一致します。

以下も興味深いものですが、ブラウザではまだ十分にサポートされていません

  • :blank 疑似クラスは、空のフォーム コントロールを選択できます:empty は、 <input> などの子のない要素にも一致しますが、より一般的であり、<br> や <hr> などの他の空の要素にも一致します:empty は適切なブラウザ サポートを備えていますが、:blank 疑似クラスの仕様はまだ完成していないため、どのブラウザでもまだサポートされていません。

  • :user-invalid 疑似クラス(サポートされている場合) は、 :invalid に似ていますが、より優れたユーザー エクスペリエンスを備えています。入力がフォーカスを受け取ったときに値が有効であった場合、ユーザーがデータを入力したときに値が一時的に無効であれば、要素は :invalid と一致する可能性がありますが、要素がフォーカスを失った場合にのみ : user -invalid と一致します値が最初に無効である場合、フォーカスが継続している間、その値は :invalid と :user-invalid の両方に一致します。:invalid と同様に、値が有効になった場合は :user-invalid との一致を停止します。

9. フォームデータの検証

フォーム検証は、ユーザーがフォーム データを正しい形式で入力していることを確認し、送信されたデータによってアプリケーションが適切に動作することを確認するのに役立ちます。

9.1 フォームデータの検証とは何ですか?

予期される形式で情報を入力せずにフォームを送信すると、登録ページにフィードバックが表示されます。

  • 「このフィールドは必須です」(このフィールドを空白のままにすることはできません)

  • 「電話番号を入力してください。その形式は: xxx-xxxx」 (データ形式を 3 つの数字、その後にダッシュ、その後に 4 つの数字を入力する必要があります)

  • 「正式な電子メール アドレスを入力してください」 (入力したデータが「[email protected]」の電子メール形式に準拠していない場合)

  • 「パスワードは 8 ~ 30 文字で、少なくとも 1 つの大文字、1 つの記号、1 つの数字を含む必要があります。」

これはフォーム検証ですフォーム検証はさまざまな方法で実装できますフォームというのは面倒なものです。

フォームのデータ検証には主に 3 つの理由があります。

  • 適切なデータを適切な形式で取得する - ユーザー データが間違った形式で保存されている場合、またはユーザーが正しい情報を入力していない場合や情報を完全に省略している場合、アプリは正しく機能しません。

  • ユーザーの保護 - ユーザーに安全なパスワードの入力を強制することで、アカウント情報の保護に役立ちます。

  • 自分自身を守る - 悪意のあるユーザーが保護されていないフォームを悪用してアプリケーションを侵害する方法は数多くあります。

警告: クライアントからサーバーに渡されるデータを決して信頼しないでください。フォームが適切に検証され、不正な入力が防止されていても、悪意のあるユーザーがネットワーク リクエストを変更する可能性があります。

さまざまな種類のフォームデータ検証

クライアントサイド検証は
、フォームデータがサーバーに送信される前にブラウザ側で行われるため、サーバーサイド検証に比べてユーザーエクスペリエンスが向上し、ユーザーの入力検証結果をリアルタイムにフィードバックすることができます。検証はさらに次の方法に細分化できます。

  • JavaScript 検証。完全にカスタマイズ可能な実装です。

  • HTML5 には検証が組み込まれており、JavaScript を必要とせず、パフォーマンスが向上しますが、JavaScript ほどカスタマイズ可能ではありません。

サーバー側の検証は、ブラウザがデータを送信し、サーバー側のプログラムによって受信された後に
行われます 。通常、サーバー側の検証は、データがデータベースに書き込まれる前に行われます。データが検証に失敗した場合、エラーが返されます。サーバー メッセージから直接、エラーが発生した場所と理由をブラウザに正確に伝えます。サーバー側の検証は、フォーム全体が送信されるまでエラー メッセージを返せないため、クライアント側の検証ほど優れたユーザー エクスペリエンスはありませんただし、サーバー側の検証は、誤ったデータや悪意のあるデータに対するアプリの最後の防御線であり、その後、データはデータベースに保存されます。現在、すべてのサーバー側フレームワークは、 (データをより安全にするために) データの検証とサニタイズを提供します。

実際のプロジェクト開発プロセスでは、開発者は一般に、データの正確性とセキュリティをより確実にするために、クライアント側の検証とサーバー側の検証を組み合わせて使用​​する傾向があります。

9.2 組み込みフォームデータ検証の使用

HTML5 の特に便利な新機能は、スクリプト コードを 1 行も記述することなく、ユーザー入力に対してデータ検証を実行できることであり、これはform 要素の validation 属性 (en-US)によって 実現されます。入力ボックスに入力する必要があるかどうか、入力ボックス内の文字列の最小長と最大長、または入力ボックスに数値や電子メール アドレスなどを入力する必要があるかどうかなど、ユーザー入力を制限するいくつかのルールを定義しますこれらは、データが一致する必要があるパターンでもあります。

要素チェックに合格した場合:

  • この要素は、CSS 疑似クラスを通じて特別にスタイル設定できます。

  • ユーザーがフォームを送信しようとした場合、アクションを阻止する他のコントロール (送信を阻止する JavaScript など) がない場合、フォームのデータが送信されます。

要素が検証に失敗した場合:

  • この要素は、CSS 疑似クラスを介して 特別にスタイル設定できます 

  • ユーザーがフォームを送信しようとすると、ブラウザはエラー メッセージを表示し、フォームの送信を停止します。

入力要素の検証制約 — 簡単な開始
<input> 要素の検証に必要なHTML5 固有のいくつかの
属性:

342eaeafe88078873e74d1c3775d548b.png

<form>
  <label for="choose">Would you prefer a banana or cherry?</label>
  <input id="choose" name="i_like" required />
  <button>Submit</button>
</form>
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

正規表現を使用した検証
もう 1 つの一般的に使用される検証関数は、値として正規表現を使用するパターン属性です。
これらがどのように機能するかについての基本的な考え方を示すために、いくつかの例を示します。

  • a — 文字 a に一致します (b、aa などには一致しません)

  • abc — a に一致し、次に b、最後に c に一致します。

  • a* — 0 個以上の文字 a に一致します (+ は少なくとも 1 つ以上に一致することを意味します)。

  • [^a] — 文字と一致しますが、a にすることはできません。

  • a|b — 文字 a または b に一致します。

  • [abc] — a、b、または c の文字に一致します。

  • [^abc] — 文字と一致しますが、a、b、c にすることはできません。

  • [az] — 文字範囲 a ~ z とすべて小文字に一致します ([A-Za-z] を使用して大文字をカバーすることも、[AZ] を使用して大文字に制限することもできます)。

  • ac—文字 a と一致し、中間の任意の文字と一致し、最後の文字 c と一致します。

  • a{5} — 文字 a と 5 回一致します。

  • a{5,7} — 文字 a と 5 ~ 7 回一致します (それ以上でもそれ以下でもありません)。

  • [ -] — スペースまたはダッシュと一致します。

  • [0-9] — 0 ~ 9 の範囲の数値と一致します。

これらを任意に組み合わせたり、異なる部分を任意に指定したりできます。

  • [Ll].*k — 大文字の L または小文字の l に一致し、次に任意のタイプの 0 個以上の文字に一致し、最後に小文字の k に一致します。

  • [AZ][A-Za-z' -]+ — 大文字の後に 1 つ以上の大文字、小文字、ダッシュ、アポストロフィ、またはスペースが続きますこれは英会話で都市名や町名を検証するために使用できますが、最初の文字が大文字で始まる必要があり、英国の Manchester、Ashton-under-lyne、Bishop's Stortford などの他の文字は含まれません。 。

  • [0-9]{3}[ -][0-9]{3}[ -][0-9]{4} —米国の電話番号に単純に一致します —  3 桁の 0 ~ 9の後にスペースが続くか、ダッシュ、その後に3 桁の 0-9、その後にスペースまたはダッシュ、その後に4 桁の 0-9が続きますが、実際の状況はさらに複雑になる可能性があります。数字に括弧を付ける人もいるからです。簡単なデモンストレーション用に。

これらの例を実装するには、次のように HTML ドキュメント フォームを更新してパターン属性を追加します。

4a8ce5d4e73ca2e4b61f35f031a79083.png

<form>
  <label for="choose">Would you prefer a banana or a cherry?</label>
  <input id="choose" name="i_like" required pattern="banana|cherry" />
  <button>Submit</button>
</form>

<input> 要素は、文字列 "banana" または文字列 "cherry" の 2 つの値のいずれかを受け入れます

注: 一部の <input> 要素タイプでは、検証に pattern 属性が必要ありません。特定の電子メール タイプを指定すると、電子メール形式に一致する正規表現が使用されて検証されます (属性が複数ある場合は、複数の電子メール アドレスをカンマで区切ってください)。さらに、フィールドの URL タイプによって、入力が正当なリンクであるかどうかが自動的に検証されます。

注:  <textarea> 要素は、パターン属性をサポートしません

入力の長さの制限
すべてのテキスト ボックス (<input> または <textarea>) は、minlength 属性と maxlength 属性を使用して長さを制限できます。

数値エントリ (つまり、<input type="number">) では、min 属性と max 属性も検証制約を提供します。フィールドの値が min 属性の値より小さいか、max 属性の値より大きい場合、フィールドは無効です。

別の例を確認してください。

4b0239f7042f42a8e3b4d3e6f6950a07.png

<form>
  <div>
    <label for="choose">Would you prefer a banana or a cherry?</label>
    <input id="choose" name="i_like" required minlength="6" maxlength="6" />
  </div>
  <div>
    <label for="number">How many would you like?</label>
    <input type="number" id="number" name="amount" value="1" min="1" max="10" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

備考: <input type="number"> (または range などの他の型) は、増加または減少の過程で値が変化する値 (増加と減少のボタンなど) を指定する step 属性も取得できます。

完全な例
HTML での検証属性の使用を示す完全な例:

a338e74a578178a580f15c09fec0238c.png

<form>
  <p>
    <fieldset>
      <legend>Title<abbr title="This field is mandatory">*</abbr></legend> <!-- 标题字段的标题 -->
      <input type="radio" required name="title" id="r1" value="Mr"><label for="r1">Mr.</label> <!-- 单选按钮,选择 "Mr." -->
      <input type="radio" required name="title" id="r2" value="Ms"><label for="r2">Ms.</label> <!-- 单选按钮,选择 "Ms." -->
    </fieldset>
  </p>
  <p>
    <label for="n1">How old are you?</label> <!-- 年龄字段的标题 -->
    <!-- 这里的 pattern 属性可以用作不支持 number 类 input 浏览器的备用方法
         请注意当与数字输入框一起使用时,支持 pattern 属性的浏览器会使它默认失效。
         它仅仅是在这里用作备用 -->
    <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">*</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?</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>
body {
  font: 1em sans-serif; /* 字体大小为1em,字体族为sans-serif */
  padding: 0; /* 内边距为0 */
  margin: 0; /* 外边距为0 */
}

form {
  max-width: 200px; /* 最大宽度为200像素 */
  margin: 0; /* 外边距为0 */
  padding: 0 5px; /* 内边距为5像素左右,0像素上下 */
}
/* 选择了所有作为 p 元素直接子元素的 label 元素 */
p > label {
  display: block; /* 显示为块级元素 */
}

input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
  /* 需要在基于 WebKit 的浏览器上对表单元素进行恰当的样式设置 */
  -webkit-appearance: none; /* 取消默认外观 */

  width: 100%; /* 宽度为100% */
  border: 1px solid #333; /* 边框为1像素宽,实线,颜色为深灰色 */
  margin: 0; /* 外边距为0 */

  font-family: inherit; /* 继承父元素的字体族 */
  font-size: 90%; /* 字体大小为90% */

  -moz-box-sizing: border-box; /* 在 Firefox 浏览器中,盒模型为border-box */
  box-sizing: border-box; /* 盒模型为border-box */
}

input:invalid {
  box-shadow: 0 0 5px 1px red; /* 阴影为红色,向右下偏移5像素,模糊半径为1像素 */
}

input:focus:invalid {
  outline: none; /* 取消轮廓线 */
}

カスタム エラー メッセージ
無効なフォーム データを送信するたびに、ブラウザには常にエラー メッセージが表示されます。ただし、表示される情報は使用しているブラウザによって異なります。

これらの自動的に生成されるエラーには、次の 2 つの欠点があります

  • CSS には、インターフェイスの外観を変更するための標準はありません。

  • これは、使用しているブラウザ環境によって異なります。つまり、この言語のページで別の言語のエラー メッセージが表示される可能性があります。

    521e1aa26e1c2b5e022076ad6debdb1d.png

これらのメッセージの外観とテキストをカスタマイズするには、JavaScript を使用する必要があります。HTML と CSS を使用して変更することはできません。

HTML5 は、 フォーム要素の状態を検出してカスタマイズする ための制約検証 APIを提供します。エラー メッセージのテキストを変更できます。例を参照してください。

d448a790c2b828cdad6bbeb05ace9e62.png

var email = document.getElementById("mail"); // 获取 id 为 "mail" 的元素

email.addEventListener("input", function (event) { // 为 email 元素添加 input 事件监听器
  if (email.validity.typeMismatch) { // 如果 email 元素的值不符合类型要求
    email.setCustomValidity("I expect an e-mail, darling!"); // 设置自定义验证信息
  } else { // 否则
    email.setCustomValidity(""); // 清除自定义验证信息
  }
});

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

ネイティブ エラー メッセージの外観を制御したい場合、または HTML の組み込みフォーム検証をサポートしていないブラウザに対処したい場合は、JavaScript を使用する必要があります。

制約検証 API
制約検証 API をサポートするブラウザが増えており、この API の信頼性は徐々に高まっています。これらの API は、特定のフォーム要素インターフェイス
で呼び出すことができるメソッドとプロパティのグループで構成されます

  • HTMLボタン要素

  • HTMLフィールドセット要素

  • HTML入力要素

  • HTMLOutputElement (en-US)

  • HTML選択要素

  • HTMLTextArea要素

制約検証 API とプロパティ

属性 説明する
検証メッセージ 要素が検証条件 (存在する場合) に失敗した場合のテキスト情報を説明するローカライズされたメッセージ要素を検証する必要がない場合 (willValidate が false)、または要素の値が検証条件を満たす場合、要素は空の文字列になります。
有効 要素の検証状態を記述するValidityState オブジェクト詳細については、考えられる検証状態に関する記事を参照してください。
validity.customError 要素にカスタム エラー セットがある場合はtrue を返し、それ以外の場合は false を返します。
有効性.パターンの不一致 要素の値が設定された正規表現と一致しない場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
validity.rangeOverflow 要素の値が設定された最大値より大きい場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
validity.rangeUnderflow 要素の値が設定された最小値を下回る場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
validity.step不一致 要素の値が step 属性のルールに準拠していない場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
有効性が長すぎます 要素の値が設定された最大長を超える場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
validity.typeMismatch 要素の値に構文エラーがある場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
有効性.有効性 要素の値に検証の問題がない場合は true を返し、そうでない場合は false を返します。このプロパティが true の場合、要素は :valid CSS 疑似クラスにヒットします。それ以外の場合、要素は :invalid CSS 疑似クラスにヒットします。
validity.valueMissing 要素に必須の属性が設定されており、値が empty の場合は true を返し、それ以外の場合は false を返します。このプロパティが true の場合、要素は :invalid CSS 疑似クラスにヒットします。
検証します 要素がフォーム送信時に検証される場合は true を返し、それ以外の場合は false を返します。

制約検証 API のメソッド

方法 説明する
checkValidity() 要素の値に検証の問題がない場合は true を返し、そうでない場合は false を返します。このメソッドは、要素の検証が失敗した場合に無効なイベントを発生させます
HTMLFormElement.reportValidity() 要素またはそのサブ要素コントロールが検証制約を満たす場合は true を返します。false の場合、無効な要素ごとに元に戻せる無効なイベントが発生し、検証エラーがユーザーに報告されます。
setCustomValidity(メッセージ) カスタム エラー メッセージを要素に追加します。カスタム エラー メッセージが設定されており、要素が無効であると見なされる場合は、指定されたエラーが表示されます。これにより、標準の制約検証 API が提供するものではなく、JavaScript コードを使用して検証失敗を作成できるようになります。これらのカスタム メッセージは、エラーがユーザーに報告されたときに表示されます。パラメーターが空の場合は、カスタム エラーを空にします。

古いブラウザの場合は、ポリフィル (ハイパーフォームなど)を使用して、制約検証 API のサポートの欠如を補うことができますすでに JavaScript を使用しているので、Web サイトまたは Web アプリケーションの設計と実装でポリフィルを使用するのは負担ではありません。

制約検証 API の使用例
この API を使用して、カスタム エラー メッセージを作成します。

<form novalidate> <!-- 表单,禁用浏览器默认验证 -->
  <p>
    <label for="mail"> <!-- 标签,与 id 为 "mail" 的元素关联 -->
      <span>Please enter an email address:</span> <!-- 提示文本 -->
      <input type="email" id="mail" name="mail" /> <!-- 电子邮件输入框 -->
      <span class="error" aria-live="polite"></span> <!-- 错误信息显示区域 -->
    </label>
  </p>
  <button>Submit</button> <!-- 提交按钮 -->
</form>

フォームは novalidate 属性を使用してブラウザの自動検証をオフにし、これによりスクリプトを使用してフォーム検証を制御できるようになります。
これは、制約検証 API のサポート、または次の CSS 疑似クラスの使用を禁止するものではありません: valid、:invalid、:in-range、:out-of-range  。

aria-live (en-US) 属性により、カスタム エラー メッセージがスクリーン リーダーなどの支援技術を使用している人を含むすべての人に表示されるようになります。

次の CSS スタイルにより、フォームとそのエラー出力がより魅力的に見えます。

/* 仅为了使示例更好看 */
body {
  font: 1em sans-serif; /* 字体大小为1em,字体族为sans-serif */
  padding: 0; /* 内边距为0 */
  margin: 0; /* 外边距为0 */
}

form {
  max-width: 200px; /* 最大宽度为200像素 */
}

p * {
  display: block; /* 显示为块级元素 */
}

input[type="email"] {
  -webkit-appearance: none; /* 取消默认外观 */

  width: 100%; /* 宽度为100% */
  border: 1px solid #333; /* 边框为1像素宽,实线,颜色为深灰色 */
  margin: 0; /* 外边距为0 */

  font-family: inherit; /* 继承父元素的字体族 */
  font-size: 90%; /* 字体大小为90% */

  -moz-box-sizing: border-box; /* 在 Firefox 浏览器中,盒模型为border-box */
  box-sizing: border-box; /* 盒模型为border-box */
}
/* 校验失败的元素样式 */
input:invalid {
  border-color: #900; /* 边框颜色为深红色 */
  background-color: #fdd; /* 背景颜色为浅红色 */
}

input:focus:invalid {
  outline: none; /* 取消轮廓线 */
}
/* 错误消息的样式 */
.error {
  width: 100%; /* 宽度为100% */
  padding: 0; /* 内边距为0 */

  font-size: 80%; /* 字体大小为80% */
  color: white; /* 文本颜色为白色 */
  background-color: #900; /* 背景颜色为深红色 */
  border-radius: 0 0 5px 5px; /* 边框圆角半径,左上角和右上角为0,左下角和右下角为5像素 */

  -moz-box-sizing: border-box; /* 在 Firefox 浏览器中,盒模型为border-box */
  box-sizing: border-box; /* 盒模型为border-box */
}

.error.active {
  padding: 0.3em; /* 内边距为0.3em */
}

次の JavaScript コードは、カスタム エラー チェックを設定する方法を示しています。

// 有许多方式可以获取 DOM 节点;在此我们获取表单本身和
// email 输入框,以及我们将放置错误信息的 span 元素。

var form = document.getElementsByTagName("form")[0];
var email = document.getElementById("mail");
var error = document.querySelector(".error");

email.addEventListener(
  "input",
  function (event) {
    // 当用户输入信息时,校验 email 字段
    if (email.validity.valid) {
      // 如果校验通过,清除已显示的错误消息
      error.innerHTML = ""; // 重置消息的内容
      error.className = "error"; // 重置消息的显示状态
    }
  },
  false,
);
form.addEventListener(
  "submit",
  function (event) {
    // 当用户提交表单时,校验 email 字段
    if (!email.validity.valid) {
      // 如果校验失败,显示一个自定义错误
      error.innerHTML = "I expect an e-mail, darling!";
      error.className = "error active";
      // 还需要阻止表单提交事件,以取消数据传送
      event.preventDefault();
    }
  },
  false,
);

0c1e6cd9c7feb2a5eac76f46fec9a79f.png

Constraint Validation API は、フォーム検証を処理するための強力なツールを提供し、HTML と CSS だけで実現できるよりもはるかに詳細にユーザー インターフェイスを制御できるようにします。

組み込み API を使用しない場合のフォーム検証
古いブラウザーやカスタム ウィジェット (en-US) などでは、制約検証 API を使用できない (または使用したくない) 場合があります。
JavaScript を使用してフォームを検証できます。フォームの検証は、実際のデータの検証よりもユーザー インターフェイスの問題です。
どのような検証を実行すればよいですか?
データを検証する方法 (文字列操作、型変換、正規表現など) を決定する必要があります。
フォームの検証が失敗した場合はどうすればよいですか?
フォームがどのように動作するかを決定する必要があります。フォームはデータを送信しますか? 間違ったフィールドが強調表示されていますか? エラーメッセージが表示されていませんか?
ユーザーが無効なデータを修正できるようにするにはどうすればよいでしょうか?
予想される入力内容をユーザーが把握し、エラー メッセージをクリアできるように、事前にアドバイスを提供する必要があります。

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

<!--我们只是关闭了 HTML 校验功能。-->
<form> 
  <p>
    <label for="mail"> <!-- 标签,与 id 为 "mail" 的元素关联 -->
      <span>Please enter an email address:</span> <!-- 提示文本 -->
      <input type="text" class="mail" id="mail" name="mail" /> <!-- 文本输入框,输入电子邮件地址 -->
      <span class="error" aria-live="polite"></span> <!-- 错误信息显示区域 -->
    </label>
  </p>

  <p>
    <button type="submit">Submit</button> <!-- 提交按钮  某些旧版浏览器需要将“button”元素上的“type”属性显式设置为“submit” -->
  </p>
</form>

CSS も大きな変更は必要ありません。:invalid 疑似クラスを実際のクラスに変更し、Internet Explorer 6 で機能しない属性セレクターの使用を避けるだけです。

/* 校验失败的元素样式 */
input.invalid {
  border-color: #900;
  background-color: #fdd;
}

input:focus.invalid {
  outline: none;
}

JavaScript コードが大幅に変更されたため、より多くの作業が必要です。

// 使用旧版浏览器选择 DOM 节点的方法较少
var form = document.getElementsByTagName("form")[0]; // 获取第一个 form 元素
var email = document.getElementById("mail"); // 获取 id 为 "mail" 的元素
// 以下是在 DOM 中访问下一个兄弟元素的技巧
// 这比较危险,很容易引起无限循环
// 在现代浏览器中,应该使用 element.nextElementSibling
var error = email;
while ((error = error.nextSibling).nodeType != 1); // 获取 email 元素后面的第一个元素节点

var emailRegExp =
  /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; // 定义电子邮件正则表达式
// 许多旧版浏览器不支持 addEventListener 方法
// 这只是其中一种简单的处理方法
function addEvent(element, event, callback) { // 定义 addEvent 函数,用于添加事件监听器
  var previousEventCallBack = element["on" + event]; // 获取元素原有的事件监听器
  element["on" + event] = function (e) { // 设置元素的新事件监听器
    var output = callback(e); // 调用回调函数
    
    if (output === false) return false; // 如果回调函数返回 false,则阻止默认事件和冒泡// 返回 `false` 来停止回调链,并中断事件的执行

    if (typeof previousEventCallBack === "function") { // 如果原有的事件监听器是函数
      output = previousEventCallBack(e); // 调用原有的事件监听器
      if (output === false) return false; // 如果原有的事件监听器返回 false,则阻止默认事件和冒泡
    }
  };
}
// 现在我们可以重构字段的约束校验了
// 由于不使用 CSS 伪类,我们必须明确地设置 valid 或 invalid 类到 email 字段上
addEvent(window, "load", function () { // 页面加载完成后执行以下代码
  var test = email.value.length === 0 || emailRegExp.test(email.value); // 检查 email 元素的值是否为空或符合正则表达式

  email.className = test ? "valid" : "invalid"; // 根据检查结果设置 email 元素的类名
});
// 处理用户输入事件
addEvent(email, "input", function () { // 当 email 元素的值改变时执行以下代码
  var test = email.value.length === 0 || emailRegExp.test(email.value); // 检查 email 元素的值是否为空或符合正则表达式
  if (test) { // 如果检查结果为真
    email.className = "valid"; // 设置 email 元素的类名为 "valid"
    error.innerHTML = ""; // 清空错误信息显示区域的内容
    error.className = "error"; // 设置错误信息显示区域的类名为 "error"
  } else { // 否则
    email.className = "invalid"; // 设置 email 元素的类名为 "invalid"
  }
});
// 处理表单提交事件
addEvent(form, "submit", function () { // 当表单提交时执行以下代码
  var test = email.value.length === 0 || emailRegExp.test(email.value); // 检查 email 元素的值是否为空或符合正则表达式

  if (!test) { // 如果检查结果为假
    email.className = "invalid"; // 设置 email 元素的类名为 "invalid"
    error.innerHTML = "I expect an e-mail, darling!"; // 设置错误信息显示区域的内容
    error.className = "error active"; // 设置错误信息显示区域的类名为 "error active"
    return false; // 阻止表单提交
  } else { // 否则
    email.className = "valid"; // 设置 email 元素的类名为 "valid"
    error.innerHTML = ""; // 清空错误信息显示区域的内容
    error.className = "error"; // 设置错误信息显示区域的类名为 "error"
  }
});

ddf61b3a7df1a4acf1ffbb906137c629.png

独自の検証システムを構築することは難しくありません。難しいのは、プラットフォーム間で、作成可能なあらゆる形式で使用できるように、汎用性を持たせることです。フォーム検証の実行に利用できるライブラリが
多数あるので、ためらわずに使用してください。ここではいくつかの例を示します。

  • スタンドアロン ライブラリ (ネイティブ JavaScript 実装):

    • .js を検証する

  • jQueryプラグイン:

    • 検証

    • 有効8

リモート検証
このタイプの検証は、ユーザーが入力したデータがアプリケーション サーバー側に保存されている追加データとバインドされている場合に必要です。
アプリケーションの例としては、ユーザー名が必要な登録フォームがあります。重複を避けるには、ユーザーにデータを送信させてフォームが重複しているためにエラー メッセージを返すよりも、AJAX リクエストを実行してユーザー名が利用できるかどうかを確認する方がはるかに効果的です。
このようなチェックを実行するには、いくつかの予防措置が必要です。

  • API と一部のデータを公開する必要があります機密データではないことを確認する必要があります。

  • ネットワークラグでは、非同期検証を実行する必要があり 、検証が適切に実行されない場合にユーザーがブロックされないようにするための UI 作業が必要になります。

10. フォームデータの送信

ユーザーがフォームを送信すると何が起こるのでしょうか。データはどこに送られ、到着したらどうすればよいのでしょうか? また、フォーム データの送信に関連するいくつかのセキュリティ問題も検討しました。

10.1 データはどこに行くのですか?

クライアント/サーバー アーキテクチャ
クライアント (通常は Web ブラウザ) は、HTTP プロトコルを使用してサーバー (主に Apache、Nginx、IIS、Tomcat などの Web サーバー) にリクエストを送信します。サーバーは同じプロトコルを使用してリクエストに応答します。

c855783211cd2318ac831bd7ce3d40f7.png

クライアント側では、HTML フォームは、サーバーにデータを送信するように HTTP リクエストを構成する便利でユーザーフレンドリーな方法にすぎませんこれにより、ユーザーは HTTP リクエストで渡される情報を提供できるようになります。

クライアント側: データの送信方法の定義
<form> 要素はデータの送信方法を定義します。そのすべてのプロパティは、ユーザーが送信ボタンをクリックしたときに送信されるリクエストを構成するために使用されます。最も重要な 2 つの属性は、action と methodです。

action 属性
この属性は、データの送信先を定義します。その値は有効な URL である必要があります。この属性が指定されていない場合、データはこのフォームを含むページの URL に送信されます。
この例では、データは絶対 URL http://foo.com に送信されます。

<form action="http://foo.com">…</form>

ここでは相対 URLを使用します。データはサーバー上の別の URL に送信されます。

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

属性がない場合、次のように <form> データはフォームが表示される同じページに送信されます。

<form>…</form>

多くの古いページでは、フォームを含む同じページにデータを送信する必要があることを示すために次の表記法が使用されています。これは、HTML5 アクション属性が登場するまではこの表記法が必須であったためです。現在、これは必要ありません。

<form action="#">…</form>

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

Method 属性
この属性はデータの送信方法を定義します。
HTTP プロトコルには、リクエストを実行するためのメソッドがいくつか用意されています。最も一般的なのは GET メソッドと POST メソッドです。

GET メソッド- サーバーに指定されたリソースを返すように要求します。
次の形式を検討してください。

<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が表示されます。

bfb8fdbb73c4c2d49923b50bdf477399.png

データは、一連の名前と値のペアとして URL に追加されます。
URL Web アドレスが終わると、疑問符 (?) が表示され、その後にアンパサンド (&) で区切られた名前と値のペアが表示されます。

HTTP リクエストは次のとおりです。

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

POST メソッド- 応答を要求するときにブラウザがサーバーと通信するために使用するメソッド。応答では、HTTP リクエストの本文で提供されたデータが考慮されます。このメソッドを使用してフォームが送信される場合、データ
はHTTPリクエストの本文。

<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/2.0
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

de3d3b77f42fc17eb20daa4fd1af34ee.png

Content-Length データ ヘッダーは本文のサイズを示し、Content-Typeデータ ヘッダーはサーバーに送信されるリソース タイプを示します。これらのヘッダーについては後で説明します。

HTTP リクエストの表示
HTTP リクエストはユーザーに表示されません。
フォーム データは Chrome ネットワーク タブに表示されます。

  1. F12を押します

  2. 「ネットワーク」を選択します

  3. すべて選択"

  4. 「名前」タブで「foo.com」を選択します

  5. 「ヘッダー」を選択します

e1a5029da6f6607243251b9f189b2cd9.png

ユーザーに表示されるのは、呼び出された URL だけです。上で述べたように、GET でリクエストしたユーザーには URL バーにデータが表示されますが、POST でリクエストしたユーザーには表示されません。

  • パスワード (またはその他の機密データ) を送信する必要がある場合は、GET メソッドを決して使用しないでください。使用しないと、データが URL バーに表示され、非常に安全ではありません。

  • 一部のブラウザでは URL のサイズが制限されているため、大量のデータを送信する必要がある場合は、POST メソッドが推奨されます。また、多くのサーバーは、受け入れる URL の長さを制限しています。

サーバー側: データの取得
選択した HTTP メソッドに関係なく、サーバーは文字列を受信し、それを解析して一連のキーと値のペアとしてデータを取得します

例: Python は、
送信されたデータを Web ページに表示します。これは、Flask フレームワークを使用して、テンプレートのレンダリング、フォーム データの送信の処理などを行います。
これは、/ と /hello の 2 つのルートを定義する単純な Flask アプリケーションです。ユーザーが / ルートにアクセスすると、アプリケーションは form.html テンプレートをレンダリングして返します。ユーザーが /hello ルートにアクセスすると、アプリケーションはフォーム データからsay フィールドと to フィールドの値を取得し、それらをgreeting.html テンプレートに渡してレンダリングし、レンダリングされたテンプレートを返します。

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST']) # 定义 / 路由
def form():
    return render_template('form.html') # 渲染并返回 form.html 模板

@app.route('/hello', methods=['GET', 'POST']) # 定义 /hello 路由
def hello():
    return render_template('greeting.html', say=request.form['say'], to=request.form['to']) # 获取表单数据并渲染 greeting.html 模板

if __name__ == "__main__":
    app.run() # 运行应用程序

上記のコードで参照されている 2 つのテンプレートは次のとおりです。

  • form.html : POST メソッドのセクションで見たものと同じフォームですが、アクションが { { url_for('hello') }}に設定されています (これは Jinja2 テンプレートであり、基本的には HTML ですが、中かっこで囲まれた Web サーバーを実行する Python コードの呼び出しを含めることができます。 url_for('hello') は基本的に、フォームが送信されると / にリダイレクトされます hello  "

  • greeting.html : このテンプレートには、レンダリング時に渡される 2 つのデータ チャンクをレンダリングする 1 行が含まれています。これは、前に示した hello() 関数を使用して行われます。この関数は、/helloURL が指定されたときに実行されます。

注: 繰り返しになりますが、このコードはブラウザに直接ロードしようとしているだけでは機能しません。Python の動作は PHP とは少し異なります。このコードをローカルで実行するには、Python/pip をインストールしてから、 pip3 install flask を使用して Flask をインストールする必要があります。この時点で、python3 python-example.py を使用してこの例を実行し、ブラウザで localhost:5000 に移動できるようになります。

他の言語とフレームワーク
Perl、Java、.Net、Ruby など、フォーム処理に使用できるサーバー側テクノロジは他にも多数あります。お気に入りを選んで使ってください。
これらのテクニックを直接使用するのは難しい場合があるため、一般的ではありません。フォームの操作を容易にする、次のような優れたフレームワークを多数使用することが一般的です。

  • Python 用の Django (Flask よりわずかに重いですが、より多くのツールとオプションがあります。)

  • Node.js 用の Express

  • PHP 用 Laravel

  • Ruby のための Ruby On Rails

  • エリクサーのフェニックス

10.2 特殊なケース: ファイルの送信

HTML フォームを使用したファイルの送信は特殊なケースです。
ファイルはバイナリ データ (またはそう思われています) ですが、他のすべてのデータはテキスト データです。
HTTP はテキスト プロトコルであるため、バイナリ データを処理するには特別な要件があります。

enctype 属性
この属性を使用すると、フォームの送信時に生成されるリクエスト内のContent-Type HTTP ヘッダーの値を指定できます。デフォルトでは、その値は application/x-www-form-urlencoded です。これは、「これは URL パラメータとしてエンコードされたフォーム データです」を意味します。

ファイルを送信する場合は、次の 3 つの追加手順が必要です

  • ファイルの内容を URL パラメータに入れることができないため、メソッド属性を POST に設定します。

  • enctype の値を multipart/form-data に設定します。これは、データが複数の部分に分割され、各ファイルが個別の部分を占め、フォームの本文に含まれるテキスト データ (テキストもフォームに入力されている場合) が 1 つの部分を占めるためです。一部。

  • ユーザーがアップロードするファイルを選択できるようにする1 つ以上のファイル ピッカー ウィジェットが含まれています。

<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>

注: 一部のブラウザは <input> の multiple 属性をサポートしており、これにより、1 つの <input> 要素だけで複数のファイルを選択してアップロードできます。サーバーがこれらのファイルをどのように処理するかは、サーバーで使用されているテクノロジによって異なります。前述したように、フレームワークを使用すると作業が容易になります。

悪用を防ぐために、多くのサーバーではファイルと HTTP リクエストのサイズ制限が設定されています。ファイルを送信する前に、サーバー管理者の権限を確認することが重要です。

10.3 一般的なセキュリティ問題

サーバーにデータを送信するたびに、セキュリティを考慮する必要があります。
HTML フォームは最も一般的な攻撃経路です
これらの問題は HTML フォーム自体から発生するものではなく、サーバーがデータを処理する方法から発生します。

XSS と CSRF
クロスサイト スクリプティング (XSS) と クロスサイト リクエスト フォージェリ (CSRF) は、ユーザーからこのユーザーまたは別のユーザーに送信されたデータを表示する ときに発生する一般的な種類の攻撃です 

XSS を使用すると、攻撃者は他のユーザーが閲覧する Web ページにクライアント側のスクリプトを挿入できます。
CSRF 攻撃は、クライアント側のスクリプトを Web ページに挿入するという同じ方法で攻撃を開始するという点で XSS 攻撃に似ていますが、その目的は異なります。CSRF 攻撃者は、権限を特権ユーザー (サイト管理者など) のレベルに昇格させて、実行すべきではないアクション (信頼できないユーザーへのデータ送信など) を実行しようとします。
XSS 攻撃は Web サイトに対するユーザーの信頼を悪用するのに対し、CSRF 攻撃は Web サイトのユーザーに対する信頼を悪用します。

これらの攻撃を防ぐには、ユーザーがサーバーに送信するデータを常にチェックし (表示する必要がある場合)、ユーザーが指定した HTML コンテンツを表示しないようにする必要があります。
現在市場にあるほぼすべてのフレームワークは、ユーザーが送信したデータから HTML の <script>、<iframe>、および <object> 要素を削除する最小限のフィルターを実装しています。これはリスクの軽減に役立ちますが、必ずしもリスクを排除できるわけではありません。

SQL インジェクション
SQL インジェクションは、標的の Web サイトで使用されるデータベースに対して操作を実行しようとする攻撃の一種です。
これには通常、SQL リクエストを送信し、サーバーがそれを実行することを期待することが含まれます (これは通常、アプリケーション サーバーがユーザーから送信されたデータを保存しようとするときに発生します)。これは実際、Web サイトを攻撃する主な方法の 1 つです。
データの損失から、特権昇格を利用して Web サイトのインフラストラクチャ全体を制御する攻撃に至るまで、悲惨な結果が生じる可能性があります。これは非常に深刻な脅威であるため、クリーンアップを実行せずにユーザーが送信したデータを保存してはなりません

HTTP ヘッダー インジェクションと電子メール インジェクション
このタイプの攻撃は、アプリケーションがフォームに入力されたユーザー データに基づいて HTTP ヘッダーまたは電子メールを構築するときに発生します。
これらはサーバーに直接損害を与えたり、ユーザーに影響を与えたりすることはありませんが、セッションハイジャックやフィッシング攻撃などのより深刻な問題を引き起こす可能性があります。
これらの攻撃はほとんどサイレントで行われ、サーバーをゾンビに変える可能性があります。

パラノイア: ユーザーを決して信用しない
これらの脅威にどのように対処しますか?
これはこのガイドの範疇を超えたトピックですが、留意すべきルールがいくつかあります。最も重要な原則は、自分自身を含め、ユーザーを決して信頼しないことです。信頼されているユーザーであってもハイジャックされる可能性があります。

サーバーに到着するすべてのデータはチェックされ、サニタイズされる必要があります。常にこのような。例外なく。

  • 潜在的に危険な文字のエスケープには近づかないでください注意して使用する必要がある特定の文字は、使用されているデータのコンテキストと使用されているサーバー プラットフォームによって異なりますが、すべてのサーバー側言語には対応する機能があります。

  • 入力するデータ量を制限し、必要なデータのみを許可します。

  • ファイルのサンドボックス アップロード (ファイルを別のサーバーに保存し、別のサブドメイン経由、または完全に別のドメイン名経由でのみファイルへのアクセスを許可します)。

10.4 結論

フォーム データを送信するのは簡単ですが、アプリケーションを保護するのは困難です。
データ セキュリティ モデルを定義するのはフロントエンド開発者ではありません。
後で説明するように、クライアント側のデータ検証 (en-US) を実行することは可能ですが、サーバーはクライアントに何が起こったのかを実際には知ることができないため、この検証を信頼できません。

終わり

おすすめ

転載: blog.csdn.net/cxyhjl/article/details/132267925