Đây là một vấn đề rất phổ biến phát sinh do sự hiểu lầm về cách thức :nth-child()
và :nth-of-type()
công việc. Thật không may, hiện tại vẫn chưa có giải pháp dựa trên bộ chọn vì Bộ chọn không cung cấp cách đối sánh con thứ n khớp với bộ chọn tùy ý dựa trên một mẫu như số lẻ, số chẵn hoặc bất kỳ an+b
đâu a != 1
và b != 0
. Điều này mở rộng ra ngoài chỉ các bộ chọn lớp, cho các bộ chọn thuộc tính, phủ định và các kết hợp phức tạp hơn của các bộ chọn đơn giản.
Lớp :nth-child()
giả đếm các phần tử trong số tất cả anh chị em của chúng dưới cùng một cha mẹ. Nó không chỉ tính các anh chị em phù hợp với phần còn lại của bộ chọn. Tương tự, lớp :nth-of-type()
giả đếm các anh chị em chia sẻ cùng một loại phần tử, tham chiếu đến tên thẻ trong HTML chứ không phải phần còn lại của bộ chọn.
Điều này cũng có nghĩa là nếu tất cả các phần tử con của cùng một cha mẹ đều thuộc cùng một loại phần tử, ví dụ trong trường hợp nội dung bảng có tr
phần tử con duy nhất là phần tử hoặc một phần tử danh sách có phần tử con duy nhất là li
phần tử, thì:nth-child()
và :nth-of-type()
sẽ hoạt động giống nhau, tức là cho mỗi giá trị của an+b
, :nth-child(an+b)
và :nth-of-type(an+b)
sẽ phù hợp với cùng một tập hợp của các yếu tố.
Trên thực tế, tất cả các bộ chọn đơn giản trong một bộ chọn ghép nhất định, bao gồm các lớp giả như :nth-child()
và:not()
, hoạt động độc lập với nhau, thay vì xem xét tập hợp con các phần tử được khớp với phần còn lại của bộ chọn.
Điều này cũng ngụ ý rằng không có khái niệm thứ tự giữa các bộ chọn đơn giản trong mỗi bộ chọn phức hợp riêng lẻ 1 , có nghĩa là hai bộ chọn sau đây là tương đương nhau:
table.myClass tr.row:nth-child(odd)
table.myClass tr:nth-child(odd).row
Được dịch sang tiếng Anh, cả hai đều có nghĩa là:
Chọn bất kỳ tr
phần tử nào phù hợp với tất cả các điều kiện độc lập sau:
- nó là một con số lẻ của cha mẹ nó;
- nó có lớp "hàng"; và
- nó là con của một
table
phần tử có lớp "myClass".
(bạn sẽ nhận thấy việc tôi sử dụng một danh sách không có thứ tự ở đây, chỉ để lái xe về nhà)
Vì hiện tại không có giải pháp CSS thuần túy, bạn sẽ phải sử dụng tập lệnh để lọc các phần tử và áp dụng các kiểu hoặc tên lớp bổ sung cho phù hợp. Ví dụ: sau đây là một cách giải quyết phổ biến sử dụng jQuery (giả sử chỉ có một nhóm hàng được điền tr
các phần tử trong bảng):
$('table.myClass').each(function() {
// Note that, confusingly, jQuery's filter pseudos are 0-indexed
// while CSS :nth-child() is 1-indexed
$('tr.row:even').addClass('odd');
});
Với CSS tương ứng:
table.myClass tr.row.odd {
...
}
Nếu bạn đang sử dụng các công cụ kiểm tra tự động như Selenium hoặc xử lý HTML bằng các công cụ như lxml, thì nhiều công cụ trong số này cho phép XPath thay thế:
//table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
Các giải pháp khác sử dụng các công nghệ khác nhau được để lại như một bài tập cho người đọc; đây chỉ là một ví dụ ngắn gọn, có nguồn gốc để minh họa.
Đối với những gì nó đáng giá, có một đề xuất mở rộng :nth-child()
ký hiệu để được thêm vào Bộ chọn cấp 4 với mục đích cụ thể là chọn mọi con thứ n phù hợp với một bộ chọn nhất định. 2
Bộ chọn để lọc các kết quả phù hợp được cung cấp như một đối số :nth-child()
, một lần nữa do cách các bộ chọn hoạt động độc lập với nhau theo một trình tự như được quy định bởi cú pháp bộ chọn hiện có. Vì vậy, trong trường hợp của bạn, nó sẽ giống như sau:
table.myClass tr:nth-child(odd of .row)
(Một độc giả thông minh sẽ lưu ý ngay rằng điều này nên được :nth-child(odd of tr.row)
thay thế, vì các bộ chọn đơn giản tr
và :nth-child()
hoạt động độc lập với nhau. Đây là một trong những vấn đề với các lớp giả chức năng chấp nhận bộ chọn, một lon sâu tôi muốn chứ không phải mở ở giữa câu trả lời này. Thay vào đó, tôi sẽ đi đến giả định rằng hầu hết các trang web sẽ không có bất kỳ yếu tố nào khác ngoàitr
các phần tử là anh chị em của nhau trong nội dung bảng, điều này sẽ làm cho một trong hai tùy chọn tương đương về mặt chức năng.)
Tất nhiên, là một đề xuất hoàn toàn mới trong một đặc điểm kỹ thuật hoàn toàn mới, điều này có thể sẽ không được triển khai cho đến khi bắt đầu vài năm. Trong thời gian chờ đợi, bạn sẽ phải sử dụng một script, như trên.
1 Nếu bạn chỉ định một loại hoặc bộ chọn đa năng, nó phải đến trước. Tuy nhiên, điều này không làm thay đổi cách thức hoạt động cơ bản của bộ chọn; nó không gì khác hơn là một câu cú pháp.
2 Điều này ban đầu được đề xuất :nth-match()
, tuy nhiên vì nó vẫn chỉ tính một phần tử liên quan đến anh chị em của nó, chứ không phải cho mọi phần tử khác khớp với bộ chọn đã cho, kể từ năm 2014, nó đã được thay thế như một phần mở rộng cho phần tử hiện có :nth-child()
.