Lớp giả giao diện người dùng

Trong các bài viết trước, chúng ta đã đề cập đến việc tạo kiểu cho nhiều loại điều khiển biểu mẫu theo cách chung chung. Điều đó bao gồm một số cách sử dụng lớp giả, ví dụ, sử dụng :checked để nhắm mục tiêu một hộp kiểm chỉ khi nó được chọn. Trong bài viết này, chúng ta khám phá các lớp giả giao diện người dùng khác nhau có sẵn để tạo kiểu cho biểu mẫu ở các trạng thái khác nhau.

Điều kiện tiên quyết: Hiểu biết cơ bản về HTMLCSS, bao gồm kiến thức chung về lớp giả và phần tử giả.
Mục tiêu: Hiểu những phần nào của biểu mẫu khó tạo kiểu và tại sao; học được những gì có thể làm để tùy chỉnh chúng.

Những lớp giả nào có sẵn cho chúng ta?

Bạn có thể đã quen với các lớp giả sau:

  • :hover: Chọn một phần tử chỉ khi nó đang được di chuột qua.
  • :focus: Chọn một phần tử chỉ khi nó được lấy focus (tức là, bằng cách nhấn Tab từ bàn phím để di chuyển đến).
  • :active: Chọn một phần tử chỉ khi nó đang được kích hoạt (tức là, trong khi nó đang được nhấp, hoặc khi phím Return / Enter đang được nhấn xuống trong trường hợp kích hoạt bằng bàn phím).

Bộ chọn CSS cung cấp một số lớp giả khác liên quan đến biểu mẫu HTML. Chúng cung cấp một số điều kiện nhắm mục tiêu hữu ích mà bạn có thể tận dụng. Chúng ta sẽ thảo luận về những điều này chi tiết hơn trong các phần bên dưới, nhưng tóm lại, các lớp giả chính mà chúng ta sẽ xem xét là:

  • :required:optional: Nhắm mục tiêu các phần tử có thể bắt buộc (ví dụ: các phần tử hỗ trợ thuộc tính required HTML)), dựa trên việc chúng bắt buộc hay tùy chọn.
  • :valid:invalid, và :in-range:out-of-range: Nhắm mục tiêu các điều khiển biểu mẫu hợp lệ/không hợp lệ theo các ràng buộc kiểm tra hợp lệ biểu mẫu được đặt trên chúng, hoặc trong phạm vi/ngoài phạm vi.
  • :enabled:disabled, và :read-only:read-write: Nhắm mục tiêu các phần tử có thể bị vô hiệu hóa (ví dụ: các phần tử hỗ trợ thuộc tính disabled HTML), dựa trên việc chúng hiện đang được bật hay tắt, và các điều khiển biểu mẫu chỉ đọc hoặc đọc-ghi (ví dụ: các phần tử có thuộc tính readonly HTML được đặt).
  • :checked, :indeterminate, và :default: Lần lượt nhắm mục tiêu các hộp kiểm và nút chọn đã được chọn, ở trạng thái không xác định (không được chọn cũng không phải không được chọn), và tùy chọn được chọn mặc định khi trang tải (ví dụ: một <input type="checkbox"> với thuộc tính checked được đặt, hoặc phần tử <option> với thuộc tính selected được đặt).

Có nhiều lớp giả khác, nhưng những lớp được liệt kê ở trên là những lớp hữu ích nhất. Một số trong số chúng nhằm giải quyết các vấn đề rất cụ thể. Các lớp giả giao diện người dùng được liệt kê ở trên có hỗ trợ trình duyệt tuyệt vời, nhưng tất nhiên, bạn nên kiểm tra kỹ các triển khai biểu mẫu của mình để đảm bảo chúng hoạt động cho đối tượng mục tiêu của bạn.

Note: Một số lớp giả được thảo luận ở đây liên quan đến việc tạo kiểu cho điều khiển biểu mẫu dựa trên trạng thái kiểm tra hợp lệ của chúng (dữ liệu của chúng có hợp lệ không?). Bạn sẽ học nhiều hơn về việc thiết lập và kiểm soát các ràng buộc kiểm tra hợp lệ trong bài viết tiếp theo của chúng ta — Kiểm tra hợp lệ biểu mẫu phía máy khách — nhưng hiện tại chúng ta sẽ giữ mọi thứ đơn giản về kiểm tra hợp lệ biểu mẫu, để nó không gây nhầm lẫn.

Tạo kiểu trường nhập liệu dựa trên việc chúng bắt buộc hay không

Một trong những khái niệm cơ bản nhất liên quan đến kiểm tra hợp lệ biểu mẫu phía máy khách là liệu trường nhập liệu biểu mẫu có bắt buộc (nó phải được điền vào trước khi biểu mẫu có thể được gửi) hay tùy chọn.

Các phần tử <input>, <select>, và <textarea> có thuộc tính required có sẵn, khi được đặt, có nghĩa là bạn phải điền vào điều khiển đó trước khi biểu mẫu sẽ gửi thành công. Ví dụ, họ và tên là bắt buộc trong biểu mẫu bên dưới, nhưng địa chỉ email là tùy chọn:

html
<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 (if you want a response): </label>
      <input id="email" name="email" type="email" />
    </div>
    <div><button>Submit</button></div>
  </fieldset>
</form>

Bạn có thể khớp hai trạng thái này bằng cách sử dụng lớp giả :required:optional. Ví dụ, nếu chúng ta áp dụng CSS sau đây cho HTML ở trên:

css
input:required {
  border: 2px solid;
}

input:optional {
  border: 2px dashed;
}

Các điều khiển bắt buộc có đường viền liền, còn điều khiển tùy chọn có đường viền đứt đoạn. Bạn cũng có thể thử gửi biểu mẫu mà không điền vào, để xem các thông báo lỗi kiểm tra hợp lệ phía máy khách mặc định mà trình duyệt cung cấp:

Nhìn chung, bạn nên tránh tạo kiểu cho các phần tử 'bắt buộc' so với 'tùy chọn' trong biểu mẫu chỉ bằng màu sắc, vì điều này không tốt cho người bị mù màu:

css
input:required {
  border: 2px solid red;
}

input:optional {
  border: 2px solid green;
}

Quy ước tiêu chuẩn trên web để chỉ trạng thái bắt buộc là dấu hoa thị (*), hoặc từ "required" được liên kết với các điều khiển tương ứng. Trong phần tiếp theo, chúng ta sẽ xem xét một ví dụ tốt hơn về cách chỉ ra các trường bắt buộc bằng :required và nội dung được tạo ra.

Note: Bạn có thể sẽ không thấy mình sử dụng lớp giả :optional thường xuyên. Các điều khiển biểu mẫu theo mặc định là tùy chọn, vì vậy bạn chỉ cần tạo kiểu tùy chọn theo mặc định, và thêm kiểu trên cùng cho các điều khiển bắt buộc.

Note: Nếu một nút chọn trong một nhóm nút chọn cùng tên có thuộc tính required được đặt, tất cả các nút chọn sẽ không hợp lệ cho đến khi một nút được chọn, nhưng chỉ có nút có thuộc tính được gán thực sự khớp với :required.

Sử dụng nội dung được tạo với lớp giả

Trong các bài viết trước, chúng ta đã thấy cách sử dụng nội dung được tạo, nhưng chúng ta nghĩ đây là thời điểm tốt để nói về nó chi tiết hơn một chút.

Ý tưởng là chúng ta có thể sử dụng các phần tử giả ::before::after cùng với thuộc tính content để tạo ra một đoạn nội dung xuất hiện trước hoặc sau phần tử bị ảnh hưởng. Đoạn nội dung không được thêm vào DOM, vì vậy nó có thể không nhìn thấy với một số trình đọc màn hình. Bởi vì nó là một phần tử giả, nó có thể được nhắm mục tiêu bằng kiểu theo cách tương tự như bất kỳ nút DOM thực nào.

Điều này thực sự hữu ích khi bạn muốn thêm chỉ báo trực quan vào một phần tử, chẳng hạn như nhãn hoặc biểu tượng, khi các chỉ báo thay thế cũng có sẵn để đảm bảo khả năng tiếp cận cho tất cả người dùng. Ví dụ, chúng ta có thể sử dụng nội dung được tạo để xử lý vị trí và hoạt ảnh của vòng tròn bên trong nút chọn tùy chỉnh khi một nút chọn được chọn:

css
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);
}

Điều này thực sự hữu ích — trình đọc màn hình đã cho người dùng biết khi một nút chọn hoặc hộp kiểm mà họ gặp đã được chọn/đánh dấu, vì vậy bạn không muốn chúng đọc ra một phần tử DOM khác chỉ ra sự lựa chọn — điều đó có thể gây nhầm lẫn. Có một chỉ báo thuần túy về mặt hình ảnh giải quyết vấn đề này.

Không phải tất cả các loại <input> đều hỗ trợ việc có nội dung được tạo đặt trên chúng. Tất cả các loại trường nhập liệu hiển thị văn bản động trong đó, chẳng hạn như text, password, hoặc button, không hiển thị nội dung được tạo. Những loại khác, bao gồm range, color, checkbox, v.v., hiển thị nội dung được tạo.

Quay lại ví dụ bắt buộc/tùy chọn của chúng ta từ trước, lần này chúng ta sẽ không thay đổi giao diện của chính trường nhập liệu — chúng ta sẽ sử dụng nội dung được tạo để thêm nhãn chỉ báo.

Trước tiên, chúng ta sẽ thêm một đoạn văn vào đầu biểu mẫu để nói những gì bạn đang tìm kiếm:

html
<p>Required fields are labeled with "required".</p>

Người dùng trình đọc màn hình sẽ được đọc "required" ra như một thông tin thêm khi họ đến mỗi trường bắt buộc, trong khi người dùng thị giác sẽ nhận được nhãn của chúng ta.

Như đã đề cập trước đây, trường nhập liệu văn bản không hỗ trợ nội dung được tạo, vì vậy chúng ta thêm một <span> trống để treo nội dung được tạo:

html
<div>
  <label for="fname">First name: </label>
  <input id="fname" name="fname" type="text" required />
  <span></span>
</div>

Vấn đề trước mắt với điều này là span đang đổ xuống dòng mới bên dưới trường nhập liệu vì trường nhập liệu và nhãn đều được đặt với width: 100%. Để khắc phục điều này, chúng ta tạo kiểu cho <div> cha để trở thành container flex, nhưng cũng nói với nó để bọc nội dung của nó xuống dòng mới nếu nội dung trở nên quá dài:

css
fieldset > div {
  margin-bottom: 20px;
  display: flex;
  flex-flow: row wrap;
}

Hiệu ứng của điều này là nhãn và trường nhập liệu nằm trên các dòng riêng biệt vì chúng đều có width: 100%, nhưng <span> có chiều rộng 0 nên nó có thể nằm trên cùng dòng với trường nhập liệu.

Bây giờ đến nội dung được tạo. Chúng ta tạo nó bằng CSS này:

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

Chúng ta đặt <span> thành position: relative để chúng ta có thể đặt nội dung được tạo thành position: absolute và định vị nó tương đối với <span> thay vì <body> (Nội dung được tạo hoạt động như thể nó là một nút con của phần tử mà nó được tạo ra, cho mục đích định vị).

Sau đó chúng ta cho nội dung được tạo nội dung "required", là những gì chúng ta muốn nhãn của mình nói, và tạo kiểu và định vị nó như chúng ta muốn. Kết quả được nhìn thấy bên dưới (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn).

Tạo kiểu điều khiển dựa trên việc dữ liệu của chúng có hợp lệ hay không

Khái niệm quan trọng khác, cơ bản trong kiểm tra hợp lệ biểu mẫu là liệu dữ liệu của điều khiển biểu mẫu có hợp lệ hay không (trong trường hợp dữ liệu số, chúng ta cũng có thể nói về dữ liệu trong phạm vi và ngoài phạm vi). Các điều khiển biểu mẫu với giới hạn ràng buộc có thể được nhắm mục tiêu dựa trên các trạng thái này.

:valid và :invalid

Bạn có thể nhắm mục tiêu các điều khiển biểu mẫu bằng cách sử dụng lớp giả :valid:invalid. Một số điểm cần lưu ý:

  • Các điều khiển không có kiểm tra hợp lệ ràng buộc sẽ luôn hợp lệ, và do đó được khớp với :valid.
  • Các điều khiển có required được đặt trên chúng mà không có giá trị được tính là không hợp lệ — chúng sẽ được khớp với :invalid:required.
  • Các điều khiển có kiểm tra hợp lệ tích hợp, chẳng hạn như <input type="email"> hoặc <input type="url">:invalid khi dữ liệu được nhập vào chúng không khớp với mẫu mà chúng đang tìm kiếm (nhưng chúng hợp lệ khi trống).
  • Các điều khiển có giá trị hiện tại nằm ngoài giới hạn phạm vi được chỉ định bởi các thuộc tính minmax:invalid, nhưng cũng được khớp bởi :out-of-range, như bạn sẽ thấy sau.
  • Có một số cách khác để làm cho một phần tử được khớp bởi :valid/:invalid, như bạn sẽ thấy trong bài viết Kiểm tra hợp lệ biểu mẫu phía máy khách. Nhưng chúng ta sẽ giữ mọi thứ đơn giản bây giờ.

Hãy xem xét một ví dụ về :valid/:invalid.

Như trong ví dụ trước, chúng ta có thêm <span> để tạo nội dung, mà chúng ta sẽ sử dụng để cung cấp chỉ báo dữ liệu hợp lệ/không hợp lệ:

html
<div>
  <label for="fname">First name: </label>
  <input id="fname" name="fname" type="text" required />
  <span></span>
</div>

Để cung cấp các chỉ báo này, chúng ta sử dụng CSS sau:

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

Như trước đây, chúng ta đặt <span> thành position: relative để chúng ta có thể định vị nội dung được tạo tương đối với chúng. Sau đó chúng ta định vị tuyệt đối nội dung được tạo khác nhau tùy thuộc vào dữ liệu của biểu mẫu có hợp lệ hay không hợp lệ — một dấu kiểm màu xanh lá hoặc một dấu chéo đỏ, tương ứng. Để thêm một chút cấp bách hơn cho dữ liệu không hợp lệ, chúng ta cũng đã cho trường nhập liệu một đường viền đỏ dày khi không hợp lệ.

Note: Chúng ta đã sử dụng ::before để thêm các nhãn này, vì chúng ta đã sử dụng ::after cho các nhãn "required".

Bạn có thể thử nó bên dưới (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn):

Lưu ý cách các trường nhập liệu văn bản bắt buộc không hợp lệ khi trống, nhưng hợp lệ khi chúng có gì đó được điền vào. Mặt khác, trường nhập liệu email hợp lệ khi trống, vì nó không bắt buộc, nhưng không hợp lệ khi nó chứa thứ gì đó không phải là địa chỉ email đúng định dạng.

Dữ liệu trong phạm vi và ngoài phạm vi

Như chúng ta đã ám chỉ ở trên, có hai lớp giả liên quan khác cần xem xét — :in-range:out-of-range. Chúng khớp với các trường nhập liệu số nơi giới hạn phạm vi được chỉ định bởi minmax, khi dữ liệu của chúng ở trong hoặc ngoài phạm vi được chỉ định, tương ứng.

Note: Các loại trường nhập liệu số là date, month, week, time, datetime-local, number, và range.

Đáng chú ý là các trường nhập liệu có dữ liệu trong phạm vi cũng sẽ được khớp bởi lớp giả :valid và các trường nhập liệu có dữ liệu ngoài phạm vi cũng sẽ được khớp bởi lớp giả :invalid. Vậy tại sao lại có cả hai? Vấn đề thực sự là về ngữ nghĩa — ngoài phạm vi là một loại thông báo không hợp lệ cụ thể hơn, vì vậy bạn có thể muốn cung cấp thông báo khác nhau cho các trường nhập liệu ngoài phạm vi, điều này sẽ hữu ích hơn cho người dùng so với chỉ nói "không hợp lệ". Bạn thậm chí có thể muốn cung cấp cả hai.

Hãy xem xét một ví dụ làm chính xác điều này, xây dựng dựa trên ví dụ trước để cung cấp thông báo ngoài phạm vi cho các trường nhập liệu số, cũng như nói liệu chúng có bắt buộc không.

Trường nhập liệu số trông như thế này:

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

Và CSS trông như thế này:

css
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;
}

Đây là câu chuyện tương tự như những gì chúng ta đã có trước đây trong ví dụ :required, ngoại trừ ở đây chúng ta đã tách ra các khai báo áp dụng cho bất kỳ nội dung ::after nào vào một quy tắc riêng, và đã cho nội dung ::after riêng cho trạng thái :required:out-of-range nội dung và kiểu riêng của chúng. Bạn có thể thử nó ở đây (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn):

Trường nhập liệu số có thể vừa bắt buộc vừa ngoài phạm vi cùng một lúc, vậy điều gì xảy ra? Bởi vì quy tắc :out-of-range xuất hiện sau trong mã nguồn so với quy tắc :required, các quy tắc xếp tầng áp dụng, và thông báo ngoài phạm vi được hiển thị.

Điều này hoạt động khá tốt — khi trang tải lần đầu, "Required" được hiển thị, cùng với dấu chéo đỏ và đường viền. Khi bạn đã nhập tuổi hợp lệ (tức là, trong phạm vi 12-120), trường nhập liệu trở nên hợp lệ. Tuy nhiên, nếu bạn thay đổi mục nhập tuổi thành một giá trị ngoài phạm vi, thông báo "Outside allowable value range" sẽ xuất hiện thay thế cho "Required".

Note: Để nhập giá trị không hợp lệ/ngoài phạm vi, bạn sẽ phải thực sự lấy focus vào biểu mẫu và nhập nó bằng bàn phím. Các nút spinner sẽ không cho phép bạn tăng/giảm giá trị ra ngoài phạm vi cho phép.

Tạo kiểu trường nhập liệu được bật và bị vô hiệu hóa, và chỉ đọc và đọc-ghi

Phần tử được bật là phần tử có thể được kích hoạt; nó có thể được chọn, nhấp vào, gõ vào, v.v. Mặt khác, phần tử bị vô hiệu hóa không thể tương tác theo bất kỳ cách nào, và dữ liệu của nó thậm chí không được gửi đến máy chủ.

Hai trạng thái này có thể được nhắm mục tiêu bằng :enabled:disabled. Tại sao các trường nhập liệu bị vô hiệu hóa hữu ích? Vâng, đôi khi nếu một số dữ liệu không áp dụng cho một người dùng nhất định, bạn thậm chí có thể không muốn gửi dữ liệu đó khi họ gửi biểu mẫu. Một ví dụ điển hình là biểu mẫu giao hàng — thường bạn sẽ được hỏi có muốn sử dụng cùng địa chỉ cho thanh toán và giao hàng không; nếu có, bạn có thể chỉ gửi một địa chỉ đến máy chủ, và cũng có thể vô hiệu hóa các trường địa chỉ thanh toán.

Hãy xem một ví dụ làm chính xác điều này. Trước tiên, HTML là một biểu mẫu đơn giản chứa các trường nhập liệu văn bản, cùng với hộp kiểm để bật tắt việc vô hiệu hóa địa chỉ thanh toán. Các trường địa chỉ thanh toán bị vô hiệu hóa theo mặc định.

html
<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="zip-code1">Zip/postal code: </label>
      <input id="zip-code1" name="zip-code1" 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="zip-code2" class="billing-label disabled-label">
        Zip/postal code:
      </label>
      <input id="zip-code2" name="zip-code2" type="text" disabled required />
    </div>
  </fieldset>

  <div><button>Submit</button></div>
</form>

Bây giờ đến CSS. Các phần liên quan nhất của ví dụ này như sau:

css
input[type="text"]:disabled {
  background: #eeeeee;
  border: 1px solid #cccccc;
}

label:has(+ :disabled) {
  color: #aaaaaa;
}

Chúng ta đã trực tiếp chọn các trường nhập liệu mà chúng ta muốn vô hiệu hóa bằng input[type="text"]:disabled, nhưng chúng ta cũng muốn làm mờ các nhãn văn bản tương ứng. Vì các nhãn nằm ngay trước các trường nhập liệu của chúng, chúng ta đã chọn những nhãn đó bằng lớp giả :has.

Cuối cùng, chúng ta đã sử dụng một số JavaScript để bật tắt việc vô hiệu hóa các trường địa chỉ thanh toán:

js
function toggleBilling() {
  // Select the billing text fields
  const billingItems = document.querySelectorAll('#billing input[type="text"]');

  // Toggle the billing text fields
  for (const item of billingItems) {
    item.disabled = !item.disabled;
  }
}

// Attach `change` event listener to checkbox
document
  .getElementById("billing-checkbox")
  .addEventListener("change", toggleBilling);

Nó sử dụng sự kiện change để cho phép người dùng bật/tắt các trường thanh toán, và bật tắt kiểu của các nhãn liên quan.

Bạn có thể thấy ví dụ trong thực tế bên dưới (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn):

Chỉ đọc và đọc-ghi

Theo cách tương tự như :disabled:enabled, các lớp giả :read-only:read-write nhắm mục tiêu hai trạng thái mà trường nhập liệu biểu mẫu chuyển đổi giữa. Như với trường nhập liệu bị vô hiệu hóa, người dùng không thể chỉnh sửa trường nhập liệu chỉ đọc. Tuy nhiên, không giống như trường nhập liệu bị vô hiệu hóa, giá trị trường nhập liệu chỉ đọc sẽ được gửi đến máy chủ. Đọc-ghi có nghĩa là chúng có thể được chỉnh sửa — trạng thái mặc định của chúng.

Trường nhập liệu được đặt thành chỉ đọc bằng thuộc tính readonly. Như một ví dụ, hãy tưởng tượng một trang xác nhận nơi nhà phát triển đã gửi thông tin điền ở các trang trước sang trang này, với mục đích để người dùng kiểm tra tất cả chúng ở một nơi, thêm bất kỳ dữ liệu cuối cùng cần thiết, và sau đó xác nhận đơn hàng bằng cách gửi. Tại thời điểm này, tất cả dữ liệu biểu mẫu cuối cùng có thể được gửi đến máy chủ trong một lần.

Hãy xem một biểu mẫu có thể trông như thế nào.

Một đoạn HTML như sau — lưu ý thuộc tính readonly:

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

Nếu bạn thử ví dụ trực tiếp, bạn sẽ thấy rằng bộ phần tử biểu mẫu trên cùng không thể chỉnh sửa, tuy nhiên, các giá trị được gửi khi biểu mẫu được gửi. Chúng ta đã tạo kiểu cho các điều khiển biểu mẫu bằng lớp giả :read-only:read-write, như thế này:

css
input:read-only,
textarea:read-only {
  border: 0;
  box-shadow: none;
  background-color: white;
}

textarea:read-write {
  box-shadow: inset 1px 1px 3px #cccccc;
  border-radius: 5px;
}

Ví dụ đầy đủ trông như thế này (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn):

Kiểu radio được chọn theo mặc định và không xác định

Như đã đề cập ở trên, lớp giả :checked khớp với nút chọn và hộp kiểm khi chúng được chọn, điều này thực sự hữu ích để tạo kiểu.

Ngoài ra còn có lớp giả :default khớp với các nút chọn/hộp kiểm được chọn theo mặc định, khi trang tải, ngay cả khi chúng không được chọn. Điều này có thể hữu ích để thêm chỉ báo vào danh sách các tùy chọn nhắc nhở người dùng về các mặc định (hoặc tùy chọn bắt đầu) là gì, trong trường hợp họ muốn đặt lại các lựa chọn của mình.

Ngoài ra, các nút chọn/hộp kiểm đã đề cập ở trên sẽ được khớp bởi lớp giả :indeterminate khi chúng ở trạng thái không được chọn cũng không phải không được chọn. Nhưng điều này có nghĩa là gì? Các phần tử không xác định bao gồm:

  • Các trường nhập liệu <input/radio>, khi tất cả các nút chọn trong một nhóm cùng tên không được chọn
  • Các trường nhập liệu <input/checkbox> có thuộc tính indeterminate được đặt thành true qua JavaScript
  • Các phần tử <progress> không có giá trị.

Đây không phải là thứ bạn sẽ thường xuyên sử dụng. Một trường hợp sử dụng có thể là chỉ báo để cho người dùng biết rằng họ thực sự cần chọn một nút chọn trước khi tiếp tục.

Hãy xem xét một số phiên bản đã sửa đổi của ví dụ trước nhắc nhở người dùng về tùy chọn mặc định là gì, và tạo kiểu cho các nhãn của nút chọn khi không xác định. Cả hai đều có cấu trúc HTML sau cho các trường nhập liệu:

html
<p>
  <input type="radio" name="fruit" value="cherry" id="cherry" />
  <label for="cherry">Cherry</label>
  <span></span>
</p>

Đối với ví dụ :default, chúng ta đã thêm thuộc tính checked vào trường nhập liệu nút chọn banana giữa, vì vậy nó sẽ được chọn theo mặc định khi tải. Sau đó chúng ta tạo kiểu cho nó với CSS sau:

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

Đối với ví dụ :indeterminate, chúng ta không có nút chọn mặc định nào được chọn — điều này quan trọng — nếu có, thì sẽ không có trạng thái không xác định để tạo kiểu. Chúng ta tạo kiểu cho các nút chọn không xác định với CSS sau:

css
input[type="radio"]:indeterminate {
  outline: 2px solid red;
  animation: 0.4s linear infinite alternate outline-pulse;
}

@keyframes outline-pulse {
  from {
    outline: 2px solid red;
  }

  to {
    outline: 6px solid red;
  }
}

Điều này tạo ra một đường viền hoạt ảnh thú vị trên các nút chọn, hy vọng chỉ ra rằng bạn cần chọn một trong số chúng!

Xem kết quả trực tiếp bên dưới (nhấn nút Play để chạy ví dụ trong MDN Playground và chỉnh sửa mã nguồn):

Note: Bạn có thể tìm thấy một ví dụ thú vị liên quan đến trạng thái indeterminate trên trang tham khảo <input type="checkbox">.

Nhiều lớp giả hơn

Có một số lớp giả thú vị khác, và chúng ta không có không gian để viết về tất cả chúng chi tiết ở đây. Hãy nói về một vài lớp giả khác mà bạn nên dành thời gian điều tra.

  • Lớp giả :focus-within khớp với phần tử đã nhận focus hoặc chứa phần tử đã nhận focus. Điều này hữu ích nếu bạn muốn toàn bộ biểu mẫu làm nổi bật theo một cách nào đó khi một trường nhập liệu bên trong nó được lấy focus.
  • Lớp giả :focus-visible khớp với các phần tử được lấy focus đã nhận focus qua tương tác bàn phím (thay vì chạm hoặc chuột) — hữu ích nếu bạn muốn hiển thị kiểu khác nhau cho focus bàn phím so với focus chuột (hoặc khác).
  • Lớp giả :placeholder-shown khớp với các phần tử <input><textarea> có văn bản gợi ý hiển thị của chúng (tức là, nội dung của thuộc tính placeholder) vì giá trị của phần tử trống.

Những lớp sau cũng thú vị, nhưng chưa được hỗ trợ tốt trong các trình duyệt:

  • Lớp giả :blank chọn các điều khiển biểu mẫu trống. :empty cũng khớp với các phần tử không có con, như <input>, nhưng nó tổng quát hơn — nó cũng khớp với các phần tử rỗng khác như <br><hr>. :empty có hỗ trợ trình duyệt hợp lý; đặc tả của lớp giả :blank chưa hoàn thành, vì vậy nó chưa được hỗ trợ trong bất kỳ trình duyệt nào.
  • Lớp giả :user-invalid, khi được hỗ trợ, sẽ tương tự như :invalid, nhưng với trải nghiệm người dùng tốt hơn. Nếu giá trị hợp lệ khi trường nhập liệu nhận focus, phần tử có thể khớp :invalid khi người dùng nhập dữ liệu nếu giá trị tạm thời không hợp lệ, nhưng sẽ chỉ khớp :user-invalid khi phần tử mất focus. Nếu giá trị ban đầu không hợp lệ, nó sẽ khớp cả :invalid:user-invalid trong suốt thời gian focus. Tương tự như :invalid, nó sẽ ngừng khớp :user-invalid nếu giá trị trở nên hợp lệ.

Tóm tắt

Điều này kết thúc cái nhìn của chúng ta về các lớp giả giao diện người dùng liên quan đến trường nhập liệu biểu mẫu. Hãy tiếp tục chơi với chúng, và tạo ra một số kiểu biểu mẫu thú vị! Tiếp theo, chúng ta sẽ chuyển sang điều gì đó khác — kiểm tra hợp lệ biểu mẫu phía máy khách.