Lớp giả của CSS :scope dùng để làm gì?

:scope được xác định trong Bộ chọn CSS 4 như sau:

Một lớp giả đại diện cho bất kỳ phần tử nào trong nhóm phần tử tham chiếu theo ngữ cảnh. Đây là một nhóm phần tử (có thể trống) được chỉ định rõ ràng, chẳng hạn như nhóm phần tử do querySelector() chỉ định hoặc phần tử mẹ của một phần tử <style scoped>, dùng để "xác định phạm vi" cho một bộ chọn sao cho bộ chọn này chỉ khớp trong một cây con.

Ví dụ về cách sử dụng thuộc tính này nằm trong <style scoped> (xem thêm thông tin):

<style>
    li {
    color: blue;
    }
</style>

<ul>
    <style scoped>
    li {
        color: red;
    }
    :scope {
        border: 1px solid red;
    }
    </style>
    <li>abc</li>
    <li>def</li>
    <li>efg</li>
</ul>

<ul>
    <li>hij</li>
    <li>klm</li>
    <li>nop</li>
</ul>

Thao tác này sẽ tô màu các phần tử li trong màu đỏ đầu tiên của ul và đặt đường viền xung quanh ul theo quy tắc :scope. Đó là vì trong ngữ cảnh của <style scoped> này, ul khớp với :scope. Vấn đề là bối cảnh địa phương. Nếu chúng ta thêm quy tắc :scope vào <style> bên ngoài, quy tắc này sẽ khớp với toàn bộ tài liệu. Về cơ bản, tương đương với :root.

Phần tử theo ngữ cảnh

Có thể bạn đã biết phiên bản Element của querySelector()querySelectorAll(). Thay vì truy vấn toàn bộ tài liệu, bạn có thể giới hạn kết quả được đặt ở một phần tử theo ngữ cảnh:

<ul>
    <li id="scope"><a>abc</a></li>
    <li>def</li>
    <li><a>efg</a></li>
</ul>
<script>
    document.querySelectorAll('ul a').length; // 2

    var scope = document.querySelector('#scope');
    scope.querySelectorAll('a').length; // 1
</script>

Khi các nút này được gọi, trình duyệt sẽ trả về một NodeList được lọc để chỉ bao gồm tập hợp các nút mà a.) khớp với bộ chọn và b.) cũng là thành phần con của phần tử ngữ cảnh. Vì vậy, trong ví dụ thứ hai, trình duyệt sẽ tìm mọi phần tử a, sau đó lọc ra những phần tử không có trong phần tử scope. Việc này hiệu quả, nhưng có thể gây ra một số hành vi kỳ quái nếu bạn không cẩn thận. Hãy đọc thêm.

Khi querySelector gặp sự cố

Có một điểm thực sự quan trọng trong thông số Trình chọn mà mọi người thường bỏ qua. Ngay cả khi querySelector[All]() được gọi trên một phần tử, các bộ chọn vẫn đánh giá trong bối cảnh của toàn bộ tài liệu. Điều này có nghĩa là những điều không lường trước có thể xảy ra:

    scope.querySelectorAll('ul a').length); // 1
    scope.querySelectorAll('body ul a').length); // 1

Tuyệt vời! Trong ví dụ đầu tiên, ul phần tử của tôi, nhưng tôi vẫn có thể sử dụng phần tử này và so khớp các nút. Trong phần thứ hai, body thậm chí không phải là thành phần con của phần tử, nhưng "body ul a" vẫn khớp. Cả hai câu hỏi này đều gây nhầm lẫn và không phải là điều bạn mong đợi.

Ở đây, bạn nên so sánh với jQuery. Phương pháp này sử dụng phương pháp phù hợp và thực hiện những điều bạn mong đợi:

    $(scope).find('ul a').length // 0
    $(scope).find('body ul a').length // 0

...nhập :scope để giải quyết các vấn đề về ngữ nghĩa này.

Sửa querySelector bằng :scope

Hỗ trợ WebKit vừa mới ra mắt để sử dụng lớp giả :scope trong querySelector[All](). Bạn có thể kiểm thử tính năng này trong Chrome Canary 27.

Bạn có thể sử dụng tuỳ chọn này để hạn chế bộ chọn đối với một phần tử ngữ cảnh. Hãy xem một ví dụ. Trong ví dụ sau, :scope được dùng để "đặt phạm vi" cho bộ chọn đến cây con của phần tử phạm vi. Đúng vậy, tôi đã nói phạm vi 3 lần!

    scope.querySelectorAll(':scope ul a').length); // 0
    scope.querySelectorAll(':scope body ul a').length); // 0
    scope.querySelectorAll(':scope a').length); // 1

Việc sử dụng :scope giúp ngữ nghĩa của các phương thức querySelector() dễ dự đoán hơn một chút và phù hợp với những gì các phương thức khác như jQuery đang làm.

Hiệu suất chiến thắng?

Chưa :(

Tôi tò mò muốn biết liệu việc sử dụng :scope trong qS/qSA có giúp tăng hiệu suất hay không. Vì vậy,... giống như một kỹ sư giỏi, tôi đã tập hợp một bài kiểm tra. Lý do của tôi: diện tích bề mặt ít hơn để trình duyệt so khớp bộ chọn có nghĩa là tra cứu nhanh hơn.

Trong thử nghiệm của tôi, WebKit hiện mất hơn ~1,5-2 lần so với việc không sử dụng :scope. Chết tiệt! Khi crbug.com/222028 được khắc phục, về mặt lý thuyết, việc sử dụng mã này sẽ giúp hiệu suất của bạn tăng nhẹ so với việc không sử dụng.