The CSS Podcast – 015: Pseudo-classes
Giả sử bạn có một biểu mẫu đăng ký email và bạn muốn trường biểu mẫu email có đường viền màu đỏ nếu chứa địa chỉ email không hợp lệ.
Làm cách nào để thực hiện điều đó?
Bạn có thể sử dụng một :invalid
CSS pseudo-class, đây là một trong nhiều pseudo-class do trình duyệt cung cấp.
Lớp giả cho phép bạn áp dụng kiểu dựa trên các thay đổi về trạng thái và các yếu tố bên ngoài. Điều này có nghĩa là thiết kế của bạn có thể phản ứng với thông tin đầu vào của người dùng, chẳng hạn như địa chỉ email không hợp lệ. Các nội dung này được đề cập trong mô-đun bộ chọn và mô-đun này sẽ hướng dẫn bạn chi tiết hơn về các nội dung đó.
Không giống như các phần tử giả (bạn có thể tìm hiểu thêm về các phần tử này trong mô-đun trước), các lớp giả sẽ liên kết với các trạng thái cụ thể mà một phần tử có thể ở trong, thay vì tạo kiểu chung cho các phần của phần tử đó.
Trạng thái tương tác
Các lớp giả sau đây sẽ áp dụng do một lượt tương tác của người dùng với trang của bạn.
:hover
Nếu người dùng có một thiết bị trỏ như chuột hoặc bàn di chuột và họ đặt thiết bị đó lên một phần tử, bạn có thể liên kết với trạng thái đó bằng :hover
để áp dụng các kiểu.
Đây là một cách hữu ích để gợi ý rằng người dùng có thể tương tác với một phần tử.
:active
Trạng thái này được kích hoạt khi người dùng đang tương tác với một phần tử (chẳng hạn như nhấp chuột) trước khi nhả chuột. Nếu bạn dùng một thiết bị trỏ như chuột, thì trạng thái này là khi thao tác nhấp bắt đầu và chưa được nhả ra.
:focus
, :focus-within
và :focus-visible
Nếu một phần tử có thể nhận tiêu điểm (chẳng hạn như <button>
), bạn có thể phản ứng với trạng thái đó bằng lớp giả :focus
.
Bạn cũng có thể phản ứng nếu một phần tử con của phần tử nhận được tiêu điểm bằng :focus-within
.
Các phần tử có thể lấy tiêu điểm, chẳng hạn như nút, sẽ hiển thị một vòng tiêu điểm khi chúng đang ở trạng thái tiêu điểm, ngay cả khi được nhấp vào. Trong trường hợp này, nhà phát triển sẽ áp dụng CSS sau:
button:focus {
outline: none;
}
CSS này sẽ xoá vòng tâm điểm mặc định của trình duyệt khi một phần tử nhận được tâm điểm. Điều này gây ra vấn đề về khả năng tiếp cận cho những người dùng điều hướng một trang web bằng bàn phím.
Nếu không có kiểu tiêu điểm, họ sẽ không thể theo dõi vị trí hiện tại của tiêu điểm khi sử dụng phím tab.
Với :focus-visible
, bạn có thể trình bày một kiểu tiêu điểm khi một phần tử nhận tiêu điểm bằng bàn phím, đồng thời sử dụng quy tắc outline: none
để ngăn kiểu tiêu điểm đó khi một thiết bị con trỏ tương tác với phần tử.
button:focus {
outline: none;
}
button:focus-visible {
outline: 1px solid black;
}
:target
Lớp giả :target
chọn một phần tử có id
khớp với một đoạn URL.
Giả sử bạn có mã HTML sau:
<article id="content">
<!-- ... -->
</article>
Bạn có thể đính kèm kiểu vào phần tử đó khi URL chứa #content
.
#content:target {
background: yellow;
}
Điều này hữu ích khi bạn muốn làm nổi bật những khu vực có thể đã được liên kết cụ thể, chẳng hạn như nội dung chính trên một trang web, bằng cách sử dụng một đường liên kết bỏ qua.
Các tiểu bang trong lịch sử
:link
Bạn có thể áp dụng lớp giả :link
cho mọi phần tử <a>
có giá trị href
mà chưa được truy cập.
:visited
Bạn có thể tạo kiểu cho một đường liên kết mà người dùng đã truy cập bằng cách sử dụng lớp giả :visited
.
Đây là trạng thái ngược lại với :link
nhưng bạn có ít thuộc tính CSS hơn để sử dụng vì lý do bảo mật.
Bạn chỉ có thể tạo kiểu cho color
, background-color
, border-color
, outline-color
và màu sắc của SVG fill
và stroke
.
Thứ tự rất quan trọng
Nếu bạn xác định một kiểu :visited
, thì kiểu này có thể bị ghi đè bởi một lớp giả liên kết có độ đặc hiệu ít nhất là bằng nhau.
Do đó, bạn nên sử dụng quy tắc LVHA để tạo kiểu cho các đường liên kết bằng các lớp giả theo một thứ tự cụ thể: :link
, :visited
, :hover
, :active
.
a:link {}
a:visited {}
a:hover {}
a:active {}
Trạng thái biểu mẫu
Các lớp giả sau đây có thể chọn các phần tử biểu mẫu, ở nhiều trạng thái mà các phần tử này có thể ở trong quá trình tương tác với chúng.
:disabled
và :enabled
Nếu một phần tử biểu mẫu, chẳng hạn như <button>
bị trình duyệt vô hiệu hoá, bạn có thể móc vào trạng thái đó bằng lớp giả :disabled
.
Lớp giả :enabled
có sẵn cho trạng thái ngược lại, mặc dù các phần tử biểu mẫu cũng là :enabled
theo mặc định, do đó, bạn có thể không cần dùng đến lớp giả này.
:checked
và :indeterminate
Lớp giả :checked
có sẵn khi một phần tử biểu mẫu hỗ trợ, chẳng hạn như hộp đánh dấu hoặc nút chọn ở trạng thái đã đánh dấu.
Trạng thái :checked
là trạng thái nhị phân (true hoặc false), nhưng hộp kiểm có trạng thái ở giữa khi không được đánh dấu hoặc bỏ đánh dấu.
Đây được gọi là trạng thái :indeterminate
.
Ví dụ về trạng thái này là khi bạn có một chế độ điều khiển "chọn tất cả" để đánh dấu vào tất cả các hộp đánh dấu trong một nhóm. Nếu người dùng xoá một trong các hộp đánh dấu này, thì hộp đánh dấu gốc sẽ không còn đại diện cho "tất cả" được đánh dấu nữa, vì vậy, hộp này sẽ chuyển sang trạng thái không xác định.
Phần tử <progress>
cũng có một trạng thái không xác định mà bạn có thể tạo kiểu.
Một trường hợp sử dụng phổ biến là tạo cho nó một giao diện có sọc để cho biết không rõ cần bao nhiêu nữa.
:placeholder-shown
Nếu một trường biểu mẫu có thuộc tính placeholder
và không có giá trị, bạn có thể dùng lớp giả :placeholder-shown
để đính kèm các kiểu vào trạng thái đó.
Ngay khi có nội dung trong trường này, cho dù có biểu tượng placeholder
hay không, trạng thái này sẽ không còn áp dụng nữa.
Trạng thái xác thực
Bạn có thể phản hồi quá trình xác thực biểu mẫu HTML bằng các lớp giả như :valid
, :invalid
và :in-range
.
Các giả lớp :valid
và :invalid
rất hữu ích cho các bối cảnh như trường email có pattern
cần được so khớp để trở thành một trường hợp lệ.
Trạng thái giá trị hợp lệ này có thể được hiển thị cho người dùng, giúp họ hiểu rằng họ có thể chuyển sang trường tiếp theo một cách an toàn.
Lớp giả :in-range
có sẵn nếu một dữ liệu đầu vào có min
và max
, chẳng hạn như dữ liệu đầu vào dạng số và giá trị nằm trong các giới hạn đó.
Với biểu mẫu HTML, bạn có thể xác định rằng một trường là bắt buộc bằng thuộc tính required
.
Lớp giả :required
sẽ có sẵn cho các trường bắt buộc.
Bạn có thể chọn các trường không bắt buộc bằng lớp giả :optional
.
Chọn các phần tử theo chỉ mục, thứ tự và số lần xuất hiện
Có một nhóm các lớp giả chọn các mục dựa trên vị trí của chúng trong tài liệu.
:first-child
và :last-child
Nếu muốn tìm mục đầu tiên hoặc mục cuối cùng, bạn có thể sử dụng :first-child
và :last-child
.
Các lớp giả này sẽ trả về phần tử đầu tiên hoặc phần tử cuối cùng trong một nhóm các phần tử cùng cấp.
:only-child
Bạn cũng có thể chọn các phần tử không có phần tử anh chị em bằng lớp giả :only-child
.
:first-of-type
và :last-of-type
Bạn có thể chọn :first-of-type
và :last-of-type
. Thoạt nhìn, chúng có vẻ giống như :first-child
và :last-child
, nhưng hãy xem xét đoạn mã HTML này:
<div class="my-parent">
<p>A paragraph</p>
<div>A div</div>
<div>Another div</div>
</div>
Và CSS này:
.my-parent div:first-child {
color: red;
}
Không có phần tử nào có màu đỏ vì phần tử con đầu tiên là một đoạn văn chứ không phải là một div. Lớp giả :first-of-type
rất hữu ích trong trường hợp này.
.my-parent div:first-of-type {
color: red;
}
Mặc dù <div>
đầu tiên là phần tử con thứ hai, nhưng vẫn là phần tử đầu tiên thuộc loại bên trong phần tử .my-parent
, vì vậy, theo quy tắc này, phần tử đó sẽ có màu đỏ.
:nth-child
và :nth-of-type
Bạn cũng không bị giới hạn ở trẻ đầu và trẻ cuối cũng như các loại trẻ.
Các lớp giả :nth-child
và :nth-of-type
cho phép bạn chỉ định một phần tử ở một chỉ mục nhất định.
Chỉ mục trong bộ chọn CSS bắt đầu từ 1.
Các giả lớp :nth-last-child()
và :nth-last-of-type()
đếm từ cuối, chứ không phải từ đầu.
Bạn cũng có thể truyền nhiều chỉ mục vào các lớp giả này.
Nếu muốn chọn tất cả các phần tử chẵn, bạn có thể dùng :nth-child(even)
.
Bạn cũng có thể tạo các bộ chọn phức tạp hơn để tìm các mục theo khoảng thời gian đều đặn bằng cách sử dụng cú pháp vi mô An+B.
li:nth-child(3n+3) {
background: yellow;
}
Bộ chọn này sẽ chọn mọi mục thứ ba, bắt đầu từ mục 3.
n
trong biểu thức này là chỉ mục, bắt đầu từ 0 và 3 (3n
) là số mà bạn nhân chỉ mục đó với.
Giả sử bạn có 7 mặt hàng <li>
.
Mục đầu tiên được chọn là 3 vì 3n+3
dịch thành (3 * 0) + 3
.
Lần lặp lại tiếp theo sẽ chọn mục 6 vì n
hiện đã tăng lên 1
, nên (3 * 1) + 3)
.
Biểu thức này áp dụng cho cả :nth-child
và :nth-of-type
.
:nth-child()
và :nth-last-child()
cũng hỗ trợ cú pháp "of S" cho phép bạn lọc các kết quả trùng khớp bằng một bộ chọn, tương tự như :nth-of-type()
. li:nth-of-type(even)
tương đương với :nth-child(even of li)
. Mặc dù :nth-of-type
chỉ cho phép bạn lọc dựa trên loại phần tử (chẳng hạn như li
hoặc p
), nhưng cú pháp "of S" cho phép bạn lọc trên mọi bộ chọn.
Nếu có bảng, bạn có thể muốn thêm sọc vào mỗi hàng. Mặc dù bạn có thể nhắm đến mọi hàng khác bằng tr:nth-child(even)
, nhưng cách này sẽ không hiệu quả nếu bạn đang lọc bỏ một số hàng. Nếu triển khai tính năng lọc bằng cách áp dụng thuộc tính hidden
, bạn có thể thêm of :not([hidden])
vào bộ chọn để lọc trước các mục bị ẩn trước khi chọn các hàng chẵn.
tr:nth-child(even of :not([hidden])){
background: lightgrey;
}
Bạn có thể thử dùng loại bộ chọn này trên trình kiểm thử nth-child hoặc công cụ bộ chọn số lượng này.
:only-of-type
Cuối cùng, bạn có thể tìm thấy phần tử duy nhất thuộc một loại nhất định trong một nhóm các phần tử cùng cấp bằng :only-of-type
.
Điều này sẽ hữu ích nếu bạn muốn chọn danh sách chỉ có một mục hoặc nếu bạn muốn tìm phần tử duy nhất được in đậm trong một đoạn văn.
Tìm các phần tử trống
Đôi khi, bạn nên xác định các phần tử hoàn toàn trống và cũng có một lớp giả cho việc đó.
:empty
Nếu một phần tử không có phần tử con, thì lớp giả :empty
sẽ áp dụng cho phần tử đó.
Tuy nhiên, trẻ em không chỉ là các phần tử HTML hoặc nút văn bản: chúng cũng có thể là khoảng trắng. Điều này có thể gây nhầm lẫn khi bạn gỡ lỗi HTML sau đây và thắc mắc tại sao HTML này không hoạt động với :empty
:
<div>
</div>
Lý do là vì có một số khoảng trắng giữa thẻ mở và thẻ đóng <div>
, nên :empty
sẽ không hoạt động.
Lớp giả :empty
có thể hữu ích nếu bạn có ít quyền kiểm soát đối với HTML và muốn ẩn các phần tử trống, chẳng hạn như trình chỉnh sửa nội dung WYSIWYG.
Ở đây, một người chỉnh sửa đã thêm một đoạn văn bản trống không cần thiết.
<article class="post">
<p>Donec ullamcorper nulla non metus auctor fringilla.</p>
<p></p>
<p>Curabitur blandit tempus porttitor.</p>
</article>
Với :empty
, bạn có thể tìm thấy và ẩn thông tin đó.
.post :empty {
display: none;
}
Tìm và loại trừ nhiều phần tử
Một số lớp giả giúp bạn viết CSS gọn gàng hơn.
:is()
Nếu muốn tìm tất cả các phần tử con h2
, li
và img
trong một phần tử .post
, bạn có thể nghĩ đến việc viết một danh sách bộ chọn như sau:
.post h2,
.post li,
.post img {
…
}
Với lớp giả :is()
, bạn có thể viết một phiên bản gọn gàng hơn:
.post :is(h2, li, img) {
/* ... */
}
Lớp giả :is
không chỉ gọn gàng hơn danh sách bộ chọn mà còn dễ sử dụng hơn.
Trong hầu hết các trường hợp, nếu có lỗi hoặc bộ chọn không được hỗ trợ trong danh sách bộ chọn, thì toàn bộ danh sách bộ chọn sẽ không hoạt động nữa.
Nếu có lỗi trong các bộ chọn được truyền trong một lớp giả :is
, thì lớp giả đó sẽ bỏ qua bộ chọn không hợp lệ nhưng sử dụng những bộ chọn hợp lệ.
:not()
Bạn cũng có thể loại trừ các mục bằng lớp giả :not()
.
Ví dụ: bạn có thể dùng bộ chọn này để tạo kiểu cho tất cả các đường liên kết không có thuộc tính class
.
a:not([class]) {
color: blue;
}
Lớp giả :not
cũng có thể giúp bạn cải thiện khả năng tiếp cận.
Ví dụ: <img>
phải có alt
, ngay cả khi đó là một giá trị trống, vì vậy, bạn có thể viết một quy tắc CSS để thêm đường viền màu đỏ dày cho những hình ảnh không hợp lệ:
img:not([alt]) {
outline: 10px red;
}
:has()
Điều gì sẽ xảy ra nếu bạn muốn tạo kiểu cho các phần tử dựa trên nội dung bên trong chúng? Bạn có thể sử dụng lớp giả :has()
để làm việc đó. Ví dụ: bạn có thể muốn áp dụng kiểu cho các nút có biểu tượng.
button:has(svg) {
/* ... */
}
Trong cấu hình cơ bản nhất, chẳng hạn như trong ví dụ trước, bạn có thể coi :has()
là bộ chọn mẹ. Bạn cũng có thể sử dụng bộ chọn gốc trùng khớp kết hợp với các bộ chọn khác để nhắm đến các phần tử khác.
form:has(input:valid) label {
font-weight: bold;
}
form:has(input:valid) label::after {
content: "✅";
}
Trong ví dụ này, chúng ta đang áp dụng các kiểu cho phần tử nhãn và phần tử giả label::after
khi đầu vào biểu mẫu có một phần tử giả valid
.
Bạn không thể lồng lớp giả :has()
bên trong một :has()
khác, nhưng bạn có thể kết hợp lớp giả này với các lớp giả khác.
:is(h1, h2, h3):has(a) {
/* ... */
}
Danh sách bộ chọn không cho phép sai sót, vì vậy, nếu có bất kỳ bộ chọn nào không hợp lệ trong danh sách, thì tất cả các quy tắc kiểu sẽ bị bỏ qua.
.my-element:has(img, ::before) {
/* any styles here will be discarded since pseudo elements can't be included in the :has() selector list */
}
Kiểm tra mức độ hiểu biết của bạn
Kiểm tra kiến thức của bạn về các lớp giả
Lớp giả hoạt động như thể một lớp đã được áp dụng động cho một phần tử, trong khi phần tử giả hoạt động trên chính phần tử đó.
Đâu là một giả lớp chức năng?
:is()
:target
:empty
:not()
Giả lớp nào sau đây là do người dùng tương tác?
:target
:hover
:squeeze
:focus-within
:press
Đâu là các lớp giả trạng thái <form>
?
:checked
:loading
:valid
:indeterminate
:enabled
:fresh
:in-range