CSS dùng thang đo số để xác định sức mạnh của selector. Sức mạnh, nghĩa là cái nào mạnh nhất, hoặc nói cách khác, selector nào có specificity cao nhất. Selector có specificity number cao nhất sẽ luôn quy định HTML element trông như thế nào, không quan trọng styles nào được áp dụng bởi các selector yếu hơn.
Nếu bạn có button element, có nhiều cách apply styles cho nó. Trong CSS 3 selector thường dùng là type, class, id.
Bạn có thể style element trực tiếp qua type selector:
button { background-color: red; }
Bạn có thể dùng class attribute:
.button { background-color: red; }
Hoặc id attribute:
#button { background-color: red; }
Mỗi selector có giá trị số là 1.
Vậy nếu tất cả CSS selectors trên (type, class, id) có cùng giá trị (1), rule-set nào thắng, khi dùng trong cùng stylesheet?
ID thắng (#button) vì IDs có precedence (ưu tiên) hơn class và type selectors.
Điều gì nếu không có ID involved?
Thì class selector (.button) thắng vì classes có precedence hơn type selectors.
Đợi đã, CSS "Cascade" (CSS = Cascading Style Sheets) thì sao?
Trong CSS, nếu mọi thứ bằng nhau, selector nào áp dụng cuối cùng trong Style Sheet, thắng.
Giả sử bạn thêm 3 classes, cùng tên (.hero-button,) trong stylesheet. Mỗi class target cùng styling property (background-color), nhưng mỗi class có property values khác nhau: green, blue, red.
.hero-button { background-color: green; } .hero-button { background-color: blue; } .hero-button { background-color: red; }
Rồi áp dụng cả 3 cho cùng HTML element:
<button class="hero-button">Button</button>
Class .hero-button cuối cùng trong stylesheet sẽ precede qua bất kỳ class trước nào cùng tên, làm background color button thành đỏ. Đó là cách CSS cascade hoạt động, order of specificity từ trên xuống dưới, selector dưới cùng thắng. Nhớ, điều này chỉ 100% true khi mọi thứ bằng nhau.
Quan trọng: nếu bạn có multiple selectors cùng tên, selector ở dưới stylesheet sẽ không override tất cả styles áp dụng bởi previous selector cùng tên, nó chỉ override specified styling property.
Bối rối?
Đừng lo, sẽ hiểu ngay.
Đây là 3 class .hero-button từ trước, tất cả cùng background-color properties, nhưng hai class đầu mỗi cái thêm một styling property (padding & font-size):
.hero-button { background-color: green; padding: 2rem; } .hero-button { background-color: blue; font-size: 2rem; } .hero-button { background-color: red; }
Stylesheet giờ có 3 CSS classes, cùng tên, tất cả target cùng background-color property cho HTML button element — nhưng hai class đầu có additional styling properties. Bạn nghĩ gì xảy ra?
Kết quả:
https://codepen.io/StrengthandFreedom/pen/mdyMGPL
Như bạn thấy, button element stack together (kết hợp) tất cả styling properties từ 3 class .hero-button, và button element giờ có tất cả 3 properties:
padding: 2rem; font-size: 2rem; background-color: red;
Nói cách khác, khi bạn có CSS selectors trong stylesheet với tên identical, bạn chỉ get properties overridden nếu selectors target cùng properties.
Trong ví dụ trên, 3 classes đều target background-color property, và ở đây class dưới cùng thắng, vì CSS Cascade rule (Order Specificity). Nhưng vì class .hero-button cuối cùng trong stylesheet target không phải padding cũng không phải font-size property, những properties từ hai class .hero-button đầu được phép apply cho button element.
Lý do tôi đập vào điểm này là bạn sẽ chắc chắn rơi vào tình huống deal với massive, disorganized CSS codebase, nhiều developers đóng góp làm đời bạn miserable.
Một scenario phổ biến nhất: developers khác nhau thêm CSS classes vào stylesheet đã exist ở đâu đó, nhanh chóng gây nhầm lẫn nếu bạn không biết hoặc quên CSS sẽ stack properties together như chúng ta thấy.
Vì vậy bạn có thể thêm CSS class tên: primary-button với properties sau:
.primary-button { background-color: red padding: 1rem; font-size: 1.125rem; }
Mà vì lý do nào đó, button xuất hiện có shadow trên nó, mà bạn không thêm. Có thể do ai đó apply styling trực tiếp trên button element:
button { box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); }
Hoặc vì ai đó trước đó thêm class trong stylesheet cùng tên, thêm box-shadow property, như sau:
.primary-button { box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); }
Để remove unwanted styling, bạn hoặc locate nơi trong codebase unwanted property được apply và remove nó, hoặc trực tiếp target và remove nó trong của bạn class .primary-button, như sau:
.primary-button { background-color: red padding: 1rem; font-size: 1.125rem; box-shadow: 0; /*removes existing box shadow value*/ }
Tôi biết bạn nghĩ gì. Đây là cách messy giải quyết vấn đề. Đúng. Nhưng đôi khi bạn exposed đến massive disorganized project (thường nhiều legacy code), không có access toàn bộ existing CSS code (bất kỳ developer có kinh nghiệm nào cũng relate). Trong tình huống này, bạn không thể modify existing code như ý — bạn phải override existing code, và trong trường hợp đó, tốt nếu biết CSS Specificity và Source Order hoạt động thế nào.
Bạn có thể dùng Specificity Calculator trên website này so sánh specificity number của các CSS selectors khác nhau, bao gồm group selectors (kết hợp selectors).
Mẹo: cách tốt tránh specificity issues trong CSS là dùng chủ yếu classes để style UI thay vì type selectors, và chỉ dùng IDs thận trọng. Cũng tránh group selectors nếu có thể vì chúng dẫn đến unpredictable problems trong growing codebase.