Đây là một trong những ví dụ nổi tiếng nhất của các tác giả hiểu sai về cách thức :first-child
hoạt động. Được giới thiệu trong CSS2 , lớp :first-child
giả đại diện cho đứa con đầu tiên của cha mẹ nó . Đó là nó. Có một quan niệm sai lầm rất phổ biến rằng nó chọn bất kỳ phần tử con nào là phần tử đầu tiên phù hợp với các điều kiện được chỉ định bởi phần còn lại của bộ chọn ghép. Do cách thức hoạt động của bộ chọn (xem ở đây để giải thích), điều đó đơn giản là không đúng.
Bộ chọn cấp 3 giới thiệu một :first-of-type
lớp giả , đại diện cho phần tử đầu tiên trong số các anh chị em thuộc loại phần tử của nó. Câu trả lời này giải thích, với hình minh họa, sự khác biệt giữa :first-child
và :first-of-type
. Tuy nhiên, như với :first-child
, nó không xem xét bất kỳ điều kiện hoặc thuộc tính nào khác. Trong HTML, loại phần tử được thể hiện bằng tên thẻ. Trong câu hỏi, loại đó là p
.
Thật không may, không có tương tự :first-of-class
lớp giả để khớp với phần tử con đầu tiên của một lớp nhất định. Một cách giải quyết mà Lea Verou và tôi đã đưa ra cho điều này (mặc dù hoàn toàn độc lập) là trước tiên áp dụng các kiểu mong muốn của bạn cho tất cả các thành phần của bạn với lớp đó:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
... Sau đó "hoàn tác" các kiểu cho các phần tử với lớp xuất hiện sau lớp đầu tiên , sử dụng bộ kết hợp anh chị em chung~
trong một quy tắc ghi đè:
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
Bây giờ chỉ có phần tử đầu tiên với class="red"
sẽ có đường viền.
Dưới đây là một minh họa về cách áp dụng các quy tắc:
<div class="home">
<span>blah</span> <!-- [1] -->
<p class="red">first</p> <!-- [2] -->
<p class="red">second</p> <!-- [3] -->
<p class="red">third</p> <!-- [3] -->
<p class="red">fourth</p> <!-- [3] -->
</div>
Không có quy tắc được áp dụng; không có đường viền được hiển thị.
Phần tử này không có lớp red
, vì vậy nó bị bỏ qua.
Chỉ quy tắc đầu tiên được áp dụng; một đường viền màu đỏ được hiển thị.
Phần tử này có lớp red
, nhưng nó không có trước bất kỳ phần tử nào có lớp red
trong lớp mẹ của nó. Do đó, quy tắc thứ hai không được áp dụng, chỉ quy tắc thứ nhất và phần tử giữ đường viền của nó.
Cả hai quy tắc đều được áp dụng; không có đường viền được hiển thị.
Phần tử này có lớp red
. Nó cũng đi trước ít nhất một yếu tố khác với lớp red
. Do đó, cả hai quy tắc đều được áp dụng và border
tuyên bố thứ hai ghi đè lên quy tắc thứ nhất, do đó "hoàn tác" nó, có thể nói như vậy.
Như một phần thưởng, mặc dù đã được giới thiệu trong Selector 3, bộ kết hợp anh chị em thực sự được IE7 hỗ trợ khá tốt và mới hơn, không giống như :first-of-type
và :nth-of-type()
chỉ được IE9 hỗ trợ trở đi. Nếu bạn cần hỗ trợ trình duyệt tốt, bạn sẽ gặp may.
Trong thực tế, thực tế là bộ kết hợp anh chị em là thành phần quan trọng duy nhất trong kỹ thuật này, và nó có hỗ trợ trình duyệt tuyệt vời như vậy, làm cho kỹ thuật này rất linh hoạt - bạn có thể điều chỉnh nó để lọc các phần tử bằng những thứ khác, bên cạnh các bộ chọn lớp:
Bạn có thể sử dụng điều này để giải quyết :first-of-type
trong IE7 và IE8, bằng cách cung cấp bộ chọn loại thay vì bộ chọn lớp (một lần nữa, nhiều hơn về cách sử dụng không chính xác của nó ở đây trong phần sau):
article > p {
/* Apply styles to article > p:first-of-type, which may or may not be :first-child */
}
article > p ~ p {
/* Undo the above styles for every subsequent article > p */
}
Bạn có thể lọc theo các bộ chọn thuộc tính hoặc bất kỳ bộ chọn đơn giản nào khác thay vì các lớp.
Bạn cũng có thể kết hợp kỹ thuật ghi đè này với các phần tử giả mặc dù các phần tử giả về mặt kỹ thuật không phải là các bộ chọn đơn giản.
Lưu ý rằng để làm việc này, bạn sẽ cần biết trước các kiểu mặc định sẽ là gì cho các phần tử anh chị em khác của bạn để bạn có thể ghi đè quy tắc đầu tiên. Ngoài ra, vì điều này liên quan đến các quy tắc ghi đè trong CSS, bạn không thể đạt được điều tương tự với một bộ chọn để sử dụng với API Selector hoặc bộ định vị CSS của Selenium .
Điều đáng nói là Selector 4 giới thiệu một phần mở rộng cho :nth-child()
ký hiệu (ban đầu là một lớp giả hoàn toàn mới được gọi :nth-match()
), cho phép bạn sử dụng một cái gì đó như:nth-child(1 of .red)
giả thuyết .red:first-of-class
. Là một đề xuất tương đối gần đây, chưa có triển khai đủ khả năng tương tác để nó có thể sử dụng được trong các trang web sản xuất. Hy vọng điều này sẽ sớm thay đổi. Trong khi đó, cách giải quyết mà tôi đề xuất nên hoạt động trong hầu hết các trường hợp.
Hãy nhớ rằng câu trả lời này giả định rằng câu hỏi đang tìm kiếm mọi phần tử con đầu tiên có một lớp nhất định. Không có lớp giả hay thậm chí là một giải pháp CSS chung cho trận đấu thứ n của một bộ chọn phức tạp trên toàn bộ tài liệu - việc một giải pháp có tồn tại hay không phụ thuộc nhiều vào cấu trúc tài liệu. jQuery cung cấp :eq()
, :first
, :last
và nhiều hơn nữa cho mục đích này, nhưng lưu ý một lần nữa rằng chúng hoạt động rất khác so với :nth-child()
et al . Sử dụng API chọn, bạn có thể sử dụng document.querySelector()
để có được kết quả khớp đầu tiên:
var first = document.querySelector('.home > .red');
Hoặc sử dụng document.querySelectorAll()
với một bộ chỉ mục để chọn bất kỳ trận đấu cụ thể:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
Mặc dù .red:nth-of-type(1)
giải pháp trong câu trả lời ban đầu được chấp nhận bởi Philip Daubmeier (vốn được viết bởi Martyn nhưng đã bị xóa từ đó), nhưng nó không hành xử theo cách bạn mong đợi.
Ví dụ: nếu bạn chỉ muốn chọn p
trong đánh dấu ban đầu của mình:
<p class="red"></p>
<div class="red"></div>
... sau đó bạn không thể sử dụng .red:first-of-type
(tương đương .red:nth-of-type(1)
), vì mỗi phần tử là phần tử đầu tiên (và duy nhất) của một loại ( p
và div
tương ứng), do đó cả hai sẽ được khớp bởi bộ chọn.
Khi phần tử đầu tiên của một lớp nhất định cũng là phần tử đầu tiên của lớp đó, lớp giả sẽ hoạt động, nhưng điều này chỉ xảy ra do sự trùng hợp . Hành vi này được thể hiện trong câu trả lời của Philip. Thời điểm bạn dính vào một phần tử cùng loại trước phần tử này, bộ chọn sẽ thất bại. Lấy đánh dấu cập nhật:
<div class="home">
<span>blah</span>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
Áp dụng quy tắc với .red:first-of-type
sẽ hoạt động, nhưng một khi bạn thêm một quy tắc khác p
mà không có lớp:
<div class="home">
<span>blah</span>
<p>dummy</p>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
... Bộ chọn sẽ ngay lập tức thất bại, vì .red
phần tử đầu tiên bây giờ là phần tử thứ hai p
.