Buổi 6: CSS (1): cú pháp, cách nhúng và bộ chọn
Bạn đã dựng được "bộ xương" của một trang web bằng HTML: tiêu đề, đoạn văn, bảng, danh sách, biểu mẫu. Nhưng nếu mở trang đó ra, bạn sẽ thấy nó khá "trần trụi" — chữ đen trên nền trắng, mọi thứ xếp dọc theo kiểu mặc định của trình duyệt. Hãy hình dung trang web của một cửa hàng bán sách: chủ cửa hàng muốn tiêu đề màu xanh ngọc, nút "Đặt mua" nổi bật, dòng sách đang giảm giá có nền vàng nhạt, và khi khách rê chuột vào menu thì mục đó đổi màu. Tất cả những việc đó là nhiệm vụ của CSS. Buổi này bạn học "ngữ pháp" của CSS: một quy tắc được viết ra sao, có ba cách đưa CSS vào trang, khi các quy tắc "cãi nhau" thì ai thắng, và quan trọng nhất là bộ chọn (selector) — công cụ giúp bạn chỉ đúng phần tử cần tạo kiểu.
Mục tiêu buổi học
- Sau buổi này bạn có thể giải thích CSS là gì, cơ chế phân tầng (Cascading) và lợi ích của việc tách kiểu dáng khỏi nội dung.
- Sau buổi này bạn có thể viết đúng cú pháp một quy tắc CSS gồm bộ chọn và khối khai báo, ghi chú thích
/* … */và đọc được một đoạn CSS. - Sau buổi này bạn có thể chèn CSS vào tài liệu HTML bằng ba cách (bên ngoài, nội bộ, nội tuyến), biết cách chúng kết hợp, khi nào dùng
!importantvà thuộc tínhall. - Sau buổi này bạn có thể dùng thành thạo bộ chọn đơn giản (thẻ, id, class,
*, nhóm) và bộ chọn thuộc tính ([attr],=,~=,|=,^=,$=,*=). - Sau buổi này bạn có thể dùng bộ chọn tổ hợp (con cháu, con
>, anh em liền kề+, anh em~), lớp giả (:hover,:first-child,:nth-child(),:not()…) và phần tử giả (::first-line,::first-letter,::before,::after,::selection).
Bản đồ buổi học
| Phần | Nội dung | Thời lượng gợi ý |
|---|---|---|
| 1. Giới thiệu về CSS | Khái niệm, cú pháp, chú thích, ba cách chèn CSS, kết hợp CSS, !important, all |
50 phút |
| 2.1–2.2 Bộ chọn đơn giản | Các loại bộ chọn; thẻ, id, class, *, nhóm |
30 phút |
| 2.3 Bộ chọn thuộc tính | [attr], =, ~=, |=, ^=, $=, *= |
25 phút |
| 2.4 Bộ chọn tổ hợp | Con cháu, con, anh em liền kề, anh em | 25 phút |
| 2.5–2.6 Lớp giả và phần tử giả | :hover, :nth-child(), bảng lớp giả, ::before… |
30 phút |
| Thực hành & ôn tập | Thực hành bộ chọn, câu hỏi ôn tập, bài tập trên Codelab | 20 phút |
Khoảng một nửa thời gian là thực hành: sau mỗi phần lý thuyết bạn chạy thử ví dụ ngay trong bài rồi làm nhóm bài tập tương ứng trên Codelab (các bài web được chấm bằng giao diện).
1. Giới thiệu về CSS
Giáo trình: Chương 3 — mục "Giới thiệu về CSS" (Khái niệm về CSS; Cú pháp CSS; Ghi chú thích cho CSS; Chèn CSS vào tài liệu HTML; Kết hợp CSS; Khai báo !important; Thuộc tính all).
1.1. Khái niệm về CSS
CSS là viết tắt của Cascading Style Sheets (tạm dịch: các tờ định kiểu phân tầng), được dùng để mô tả cách các phần tử HTML được hiển thị. CSS được giới thiệu vào năm 1996, giúp bố trí và định dạng các phần tử HTML linh hoạt và hiệu quả hơn nhiều so với việc đặt thuộc tính style trực tiếp trong từng phần tử. CSS có thể kiểm soát bố cục của cả trang web bằng cách khai báo các style (định dạng), nhờ đó tiết kiệm đáng kể công sức định dạng các phần tử HTML.
Giáo trình tóm tắt ba nguyên lý "CSS hoạt động như thế nào":
- Phân tầng (Cascading): CSS dùng cơ chế phân tầng để áp dụng các style vào các phần tử HTML — nhiều quy tắc có thể cùng tác động lên một phần tử và trình duyệt có luật để chọn quy tắc thắng.
- Tách rời kiểu: CSS cho phép tách rời phần kiểu dáng (style) của trang web với nội dung (content) của nó.
- Đơn giản hóa mã HTML: dùng CSS giúp giảm bớt mã HTML vì không cần lặp lại nhiều thuộc tính
styletrong từng phần tử.
<h4>Khóa học HTML</h4><p>Học dựng trang Web…</p><button>Đăng ký</button>
Học dựng trang Web từ đầu.
.card { border-radius: 12px; }h4 { color: #4f46e5; }button { background: #4f46e5; }
Khóa học HTML
Học dựng trang Web từ đầu.
const btn = document.querySelector('button')btn.onclick = () => {btn.textContent = 'Đã đăng ký ✓'}
Khóa học HTML
Học dựng trang Web từ đầu.
HTML cho biết trang có gì, CSS quyết định trang trông thế nào, JavaScript quyết định trang làm gì khi người dùng thao tác.
Sơ đồ nhắc lại phân công của ba ngôn ngữ: HTML lo cấu trúc, CSS lo trình bày, JavaScript lo hành vi. Hôm nay ta chỉ quan tâm cột giữa. Hãy xem sức mạnh của "tách rời kiểu": chỉ một quy tắc CSS nhưng định dạng được cả ba đoạn văn.
<style>
p { color: crimson; font-style: italic; }
</style>
<h3>Tin nhanh</h3>
<p>Bản tin thứ nhất.</p>
<p>Bản tin thứ hai.</p>
<p>Bản tin thứ ba.</p>
<script>
console.log("Số thẻ p cùng bị một quy tắc định dạng:", document.querySelectorAll("p").length);
</script>Nếu sau này muốn đổi màu cho mọi bản tin, bạn chỉ sửa một chỗ thay vì sửa ba thẻ. Với một website hàng trăm trang, lợi ích này là rất lớn.
Ví dụ thực tế: Website của một trường đại học có hàng nghìn trang, tất cả dùng chung một tệp CSS. Khi nhà trường đổi màu chủ đạo, người quản trị sửa vài dòng trong tệp CSS đó là toàn bộ website đổi giao diện theo.
1.2. Cú pháp CSS
Cú pháp của CSS gồm hai phần:
- Bộ chọn (selector): trỏ đến phần tử HTML cần tạo kiểu.
- Khối khai báo (declaration block): chứa một hoặc nhiều cặp "thuộc tính" (property) / "giá trị" (value), đặt trong cặp ngoặc nhọn
{}.
/* Định dạng đoạn văn */p {color: red;text-align: center;}
<p>Xin chào Web!</p>Xin chào Web!
Xin chào Web!
pBộ chọn{ }Khối khai báocolorThuộc tínhredGiá trị;Dấu chấm phẩy<link rel="stylesheet" href="style.css"><style> p { color: red } </style><p style="color: red">/* nội dung ghi chú */ được trình duyệt bỏ qua, dùng để giải thích hoặc tạm vô hiệu hóa mã.Mỗi quy tắc CSS gồm bộ chọn và khối khai báo; mỗi khai báo là một cặp thuộc tính/giá trị và luôn kết thúc bằng dấu chấm phẩy.
Hình trên tách một quy tắc thành các phần: bộ chọn đứng trước, khối khai báo trong {}, mỗi khai báo gồm thuộc tính, dấu hai chấm, giá trị và dấu chấm phẩy. Ví dụ trong giáo trình:
p {
color: red;
text-align: center;
}Trong ví dụ trên, p là bộ chọn dùng để chọn ra các phần tử <p>; khối khai báo đặt trong cặp ngoặc nhọn quy định các phần tử <p> này có chữ màu đỏ và được căn giữa. Một khai báo CSS luôn kết thúc bằng dấu chấm phẩy (;) và các khối khai báo được bao quanh bởi các dấu ngoặc nhọn {}.
Hãy chạy thử và để ý: đoạn CSS ngay sau đây được trình duyệt "đọc" thành một quy tắc gồm một bộ chọn và hai khai báo.
<style>
p {
color: red;
text-align: center;
}
</style>
<p>Đoạn này chữ đỏ và căn giữa.</p>
<script>
const rule = document.styleSheets[0].cssRules[0];
console.log("Bộ chọn:", rule.selectorText);
console.log("Số khai báo:", rule.style.length);
for (let i = 0; i < rule.style.length; i++) {
const prop = rule.style[i];
console.log(prop + " -> " + rule.style.getPropertyValue(prop));
}
</script>Lưu ý: Quên dấu
;giữa hai khai báo là lỗi rất hay gặp: trình duyệt sẽ bỏ qua khai báo bị hỏng và đôi khi cả khai báo đứng liền sau nó. Hãy tập thói quen luôn kết thúc mọi khai báo bằng dấu chấm phẩy, kể cả khai báo cuối cùng.
1.3. Ghi chú thích cho CSS
Chú thích (comment) trong CSS:
- được dùng để giải thích mã hoặc tạm "tắt" một đoạn mã khi chỉnh sửa;
- bị trình duyệt bỏ qua;
- bắt đầu bằng
/*và kết thúc bằng*/; - có thể viết trên một dòng hoặc nhiều dòng.
/* Chú thích trên một dòng */
p {
color: red;
text-align: center;
}
/* Chú thích
trên nhiều dòng */Hãy kiểm chứng rằng trình duyệt bỏ qua chú thích: ví dụ dưới có ba chú thích nhưng chỉ có hai quy tắc thật, và khai báo font-size bị "tắt" bằng chú thích nên không có tác dụng.
<style>
/* Định dạng đoạn văn */
p {
color: teal;
/* font-size: 30px; <- tạm tắt */
}
/* Định dạng tiêu đề
(chú thích nhiều dòng) */
h3 { color: purple; }
</style>
<h3>Tiêu đề màu tím</h3>
<p>Đoạn văn màu xanh mòng két, cỡ chữ mặc định.</p>
<script>
const sheet = document.styleSheets[0];
console.log("Số quy tắc thật:", sheet.cssRules.length);
console.log("Khai báo trong quy tắc p:", sheet.cssRules[0].style.length);
</script>Cảnh báo: CSS không có chú thích kiểu
//như JavaScript hay<!-- -->như HTML. Viết// ghi chútrong CSS sẽ làm hỏng quy tắc đứng ngay sau nó. Đây cũng là đáp án của câu 3.5 ở cuối bài.
1.4. Chèn CSS vào tài liệu HTML
Có ba cách để chèn CSS vào tài liệu HTML:
- CSS bên ngoài (External);
- CSS nội bộ (Internal);
- CSS nội tuyến (Inline).
CSS bên ngoài
CSS bên ngoài được lưu trong một tệp có phần mở rộng .css. Trang HTML tham chiếu đến tệp này bằng phần tử <link>, đặt trong phần tử <head>. Bạn có thể đổi giao diện cả trang web chỉ bằng cách sửa tệp .css, và một tệp CSS ngoài có thể áp dụng cho nhiều tệp HTML.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<header>
<h1>Minh's Page</h1>
<p>Lập kế hoạch - Ghi chú thích - <b>Viết mã lệnh</b> - Kiểm thử</p>
</header>
</body>
</html>Nội dung tệp style.css đi kèm như sau:
body {
font-family: Arial;
}
header {
background: #1abc9c;
color: white;
padding: 60px;
text-align: center;
}Các khai báo trên làm cho nội dung trong <body> dùng phông chữ Arial; còn <header> có màu nền #1abc9c, chữ trắng, padding (phần đệm bên trong) 60 px và chữ căn giữa. Vì web không đọc được tệp ngoài trong ô chạy thử, bên dưới là bản chạy được: hai quy tắc đó đặt trong <style> (cách thứ hai).
CSS nội bộ
CSS nội bộ được khai báo ngay trong tệp HTML nên chỉ có tác dụng với duy nhất tệp HTML đó. CSS nội bộ được đặt trong phần tử <style>. Những khai báo của style.css ở trên có thể chuyển vào <style> như sau:
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial;
}
header {
background: #1abc9c;
color: white;
padding: 60px;
text-align: center;
}
</style>
</head>
<body>
<header>
<h1>Minh's Page</h1>
<p>Lập kế hoạch - Ghi chú thích - <b>Viết mã lệnh</b> - Kiểm thử</p>
</header>
</body>
</html>Mẹo: Khi làm nhanh một trang thử nghiệm, CSS nội bộ rất tiện vì chỉ cần một tệp. Nhưng khi website có nhiều trang, hãy chuyển CSS ra tệp
.cssriêng để mọi trang dùng chung.
CSS nội tuyến
CSS nội tuyến dùng để áp dụng kiểu cho một phần tử duy nhất: bạn thêm thuộc tính style trực tiếp vào phần tử HTML.
<div>
Đây là bảng tóm tắt tất cả các <span style="font-weight: bold">thuộc tính được sử dụng trong
<em style="color: #1abc9c">CSS</em></span>. Sẽ rất cần thiết cho bạn tham khảo, tra cứu khi sử dụng CSS...
</div>Giáo trình lưu ý: nên hạn chế dùng CSS nội tuyến, vì những cấu hình này không thể dùng lại cho các phần tử HTML khác, và việc chỉnh sửa thiết kế sau này rất mất thời gian, công sức. Bảng so sánh nhanh ba cách:
| Cách chèn | Đặt ở đâu | Phạm vi tác dụng | Khi nào dùng |
|---|---|---|---|
| Bên ngoài (External) | Tệp .css, nối bằng <link> trong <head> |
Nhiều trang HTML | Website thật — cách khuyến nghị |
| Nội bộ (Internal) | Phần tử <style> trong tệp HTML |
Một tệp HTML | Trang đơn, thử nghiệm nhanh |
| Nội tuyến (Inline) | Thuộc tính style của phần tử |
Đúng một phần tử | Ngoại lệ hiếm; ghi đè tạm thời |
1.5. Kết hợp CSS
Trong một tài liệu HTML bạn có thể dùng nhiều loại CSS cùng lúc. Khi các khai báo có cùng độ ưu tiên (specificity), giá trị của CSS được đọc sau cùng sẽ được dùng và CSS đọc trước bị ghi đè (mất tác dụng).
Giả sử một CSS ngoài (mystyle.css) có h1 { color: navy; }, và một CSS nội bộ có h1 { color: orange; }.
- Nếu CSS ngoài được khai báo trước thì nó không có tác dụng, vì bị CSS nội bộ ghi đè:
<head>
<link rel="stylesheet" type="text/css" href="mystyle.css">
<style>
h1 { color: orange; }
</style>
</head>- Ngược lại, nếu CSS ngoài được khai báo sau, nó ghi đè lên CSS nội bộ:
<head>
<style>
h1 { color: orange; }
</style>
<link rel="stylesheet" type="text/css" href="mystyle.css">
</head>Để tự quan sát mà không cần tệp ngoài, ta dùng hai phần tử <style> thay cho "CSS ngoài" và "CSS nội bộ". Dòng console.log in màu mà trình duyệt thực sự áp dụng. Hãy đoán trước: tiêu đề nào màu cam, tiêu đề nào màu xanh navy?
<h1 id="a">Tiêu đề A (navy rồi orange)</h1>
<style> h1 { color: navy; } </style>
<style> h1 { color: orange; } </style>
<h1 id="b" class="x">Tiêu đề B (orange rồi navy)</h1>
<script>
// thêm hai quy tắc theo thứ tự ngược lại cho tiêu đề B bằng cách dùng lớp riêng
const s1 = document.createElement("style");
s1.textContent = ".x { color: orange; }";
const s2 = document.createElement("style");
s2.textContent = ".x { color: navy; }";
document.head.append(s1, s2);
console.log("A:", getComputedStyle(document.getElementById("a")).color);
console.log("B:", getComputedStyle(document.getElementById("b")).color);
</script>Kết quả: A có orange (rgb(255, 165, 0)) vì quy tắc cam đọc sau; B có navy (rgb(0, 0, 128)) vì quy tắc navy đọc sau. Chú ý điều kiện "cùng độ ưu tiên": ở ví dụ B ta dùng lớp .x cho cả hai quy tắc nên chúng ngang nhau, và thứ tự quyết định.
!importantcao nhất, bất kể thứ tựstyle="…"CSS nội tuyến#idbộ chọn id.lớp [thuộc tính] :lớp-giảthẻ ::phần-tử-giả* và giá trị kế thừathấp nhất<p id="myid" class="myclass">Khi nhiều quy tắc cùng tác động một phần tử, trình duyệt chọn quy tắc có độ ưu tiên cao nhất; nếu bằng nhau thì CSS đọc sau cùng thắng.
Sơ đồ cho thấy thứ tự ưu tiên: CSS nội tuyến mạnh hơn các bộ chọn id, rồi đến class/thuộc tính/lớp giả, rồi thẻ/phần tử giả; còn khi ngang nhau thì quy tắc đọc sau thắng.
Mở rộng: "Độ ưu tiên" ở đây chính là độ đặc hiệu (specificity) của bộ chọn. Có thể ghi nhớ bằng bộ ba (a, b, c): a = số bộ chọn id, b = số class + thuộc tính + lớp giả, c = số thẻ + phần tử giả; so sánh từ trái sang phải. Ví dụ
#nav alà (1,0,1),.menu a:hoverlà (0,2,1),alà (0,0,1). Ký hiệu*và các tổ hợp (>,+,~, khoảng trắng) không cộng điểm. Bạn sẽ gặp lại kiến thức này nhiều trong các bài tập.
Hãy gõ các bộ chọn vào máy tính độ đặc hiệu để thấy bộ ba (a, b, c) thay đổi ra sao:
🧮 Máy tính độ đặc hiệu
Sửa bộ chọn, bật !important, đổi thứ tự → xem quy tắc nào thắngp.note#intro🏆 Thắng<main class="page"> <h2>Chào mừng</h2> <p id="intro" class="note hot">…</p> </main>
| Hạng | Quy tắc | Độ đặc hiệu | Kết quả |
|---|---|---|---|
| 1 | #intro | (1, 0, 0) | ✅ thắng |
| 2 | .note | (0, 1, 0) | ❌ thua vì độ đặc hiệu thấp hơn (0,1,0 < 1,0,0) |
| 3 | p | (0, 0, 1) | ❌ thua vì độ đặc hiệu thấp hơn (0,0,1 < 1,0,0) |
Hai bộ chọn ngang điểm thì quy tắc viết sau thắng; bộ chọn điểm cao hơn thắng bất kể thứ tự. Kiểm chứng bằng mã (quy tắc id viết trước vẫn thắng quy tắc thẻ viết sau):
<style>
#title { color: green; }
h2 { color: red; }
</style>
<h2 id="title">Id thắng thẻ dù viết trước</h2>
<script>
console.log(getComputedStyle(document.getElementById("title")).color);
</script>1.6. Khai báo !important
Khai báo !important đánh dấu một khai báo là "quan trọng": nó được ưu tiên cao nhất và không bị các khai báo thông thường khác ghi đè, bất kể thứ tự hay độ ưu tiên của bộ chọn. Ví dụ của giáo trình:
#myid {
background-color: blue !important;
}
p {
background-color: red;
}
.myclass {
background-color: gray;
padding: 20px;
color: white;
}Nếu có một thẻ <p id="myid" class="myclass">Nội dung thẻ p</p> thì nền của nó là blue. Hãy chạy thử:
<style>
#myid { background-color: blue !important; }
p { background-color: red; }
.myclass { background-color: gray; padding: 20px; color: white; }
</style>
<p id="myid" class="myclass">Nội dung thẻ p</p>
<script>
console.log(getComputedStyle(document.getElementById("myid")).backgroundColor);
</script>Tuy vậy, giáo trình nhắc: nên hạn chế dùng !important, chỉ dùng khi không thể thay đổi thứ tự của các CSS — ví dụ các khai báo nằm trong những tệp CSS khác nhau mà bạn không sửa được. Ở ví dụ trên, !important là không cần thiết, vì bộ chọn #myid vốn đã có độ ưu tiên cao hơn p và .myclass nên khai báo của nó vẫn được áp dụng dù đặt ở vị trí nào.
Cảnh báo: Lạm dụng
!importantbiến CSS thành "cuộc chạy đua vũ trang": muốn thắng một khai báo!important, bạn lại phải viết!importantkhác, mã ngày càng khó bảo trì. Hãy ưu tiên chỉnh lại bộ chọn hoặc thứ tự trước khi nghĩ tới!important. Lưu ý:!importantthắng cả CSS nội tuyến thông thường; còn hai khai báo!importantđụng nhau thì lại tính độ đặc hiệu như bình thường.
1.7. Thuộc tính all
Khi một phần tử nằm bên trong phần tử khác, nó là phần tử con và sẽ kế thừa những thuộc tính có tính kế thừa của phần tử cha. Tuy nhiên, bạn có thể dùng thuộc tính all để đặt lại toàn bộ giá trị thuộc tính của phần tử con, quyết định phần tử đó có dùng thuộc tính của phần tử cha hay không. Các giá trị có thể thiết lập cho all:
| Giá trị | Ý nghĩa |
|---|---|
initial |
Đặt toàn bộ thuộc tính về giá trị mặc định (khởi tạo) |
inherit |
Đặt toàn bộ thuộc tính kế thừa từ phần tử cha |
unset |
Với mỗi thuộc tính: nếu có tính kế thừa thì lấy từ cha, ngược lại trở về giá trị khởi tạo (initial) |
Ví dụ trong giáo trình thiết lập để các phần tử <div> đặt trong <body> không kế thừa định dạng của phần tử cha: cho dù <div> nằm trong <body>, nó vẫn không có chữ màu đỏ.
<style>
body {
font-size: small;
color: red;
}
div {
all: initial;
}
</style>
<p>Đoạn p kế thừa chữ đỏ, cỡ nhỏ từ body.</p>
<div id="d">Thẻ div sau all: initial — không còn đỏ, không còn cỡ nhỏ.</div>
<script>
console.log("Màu chữ của p (kế thừa từ body):", getComputedStyle(document.querySelector("p")).color);
</script>Lưu ý:
all: initialđặt cảdisplayvềinlinenên<div>mất tính chất "khối". Nếu bạn chỉ muốn bỏ kế thừa nhưng giữ phần còn lại, dùngunsethoặc đặt riêng từng thuộc tính.
Vừa học xong cú pháp, cách nhúng, thứ tự ưu tiên và !important, hãy làm các bài sau để luyện ghi đè kiểu và đọc độ ưu tiên:
2. Bộ chọn CSS
Giáo trình: Chương 3 — mục "Bộ chọn CSS" (Các loại bộ chọn; Bộ chọn đơn giản; Bộ chọn thuộc tính; Bộ chọn tổ hợp; Bộ chọn lớp giả; Bộ chọn phần tử giả).
2.1. Các loại bộ chọn
Bộ chọn (Selector) CSS được dùng để "tìm" (hoặc chọn) các phần tử HTML cần tạo kiểu. Có thể chia thành năm loại chính:
- Bộ chọn đơn giản (Simple): chọn dựa trên tên thẻ, id, lớp.
- Bộ chọn thuộc tính (Attribute): chọn dựa trên thuộc tính của phần tử.
- Bộ chọn tổ hợp (Combinator): chọn dựa trên mối quan hệ giữa các phần tử.
- Bộ chọn lớp giả (Pseudo-class): chọn dựa trên trạng thái của phần tử.
- Bộ chọn phần tử giả (Pseudo-element): chọn một phần nào đó của một phần tử HTML.
- 1Đơn giảnchọn theo tên thẻ, id, lớp
p.note#title* - 2Thuộc tínhchọn theo thuộc tính của phần tử
a[target]input[type="text"] - 3Tổ hợpchọn theo quan hệ giữa các phần tử
div pul > lih2 + ph2 ~ p - 4Lớp giảchọn theo trạng thái của phần tử
button:hoverli:first-child - 5Phần tử giảchọn theo một phần của phần tử
p::first-letterp::after
Khóa học Web
h2#titleLớp A, học kỳ 1
p.noteChào mừng bạn★
pa[target]input[type="text"]- • Học HTML
- • Học CSS
- • Học JS
ul > libuttonCùng một trang mẫu: mỗi loại bộ chọn chọn ra những phần tử theo một cách khác nhau (khung màu sáng lần lượt theo từng loại).
Hình tóm tắt năm loại cùng ký hiệu đặc trưng của chúng: tên thẻ, #id, .class (đơn giản); [attr] (thuộc tính); khoảng trắng, >, +, ~ (tổ hợp); một dấu hai chấm : (lớp giả); hai dấu hai chấm :: (phần tử giả). Hãy nhớ bảng này vì mọi bộ chọn bạn gặp đều thuộc một trong năm nhóm đó.
Để luyện tập xuyên suốt phần này, bạn có thể dùng công cụ "Bộ chọn CSS sáng đèn": gõ một bộ chọn, các phần tử khớp sẽ được tô sáng.
🎯 Phòng thí nghiệm bộ chọn CSS
Gõ bộ chọn → phần tử khớp được tô sáng ngayli.course.hotli.course.hot- HTML & CSS
- JavaScript
- Bootstrap
- Git & GitHub
2.2. Bộ chọn đơn giản
Bộ chọn đơn giản là loại phổ biến nhất, gồm bốn dạng:
- bộ chọn phần tử (theo tên thẻ);
- bộ chọn theo id;
- bộ chọn theo class;
- bộ chọn
*(mọi phần tử).
Bộ chọn phần tử
Bộ chọn phần tử chọn các phần tử HTML dựa trên tên thẻ — một trong những bộ chọn cơ bản và dễ dùng nhất. Nó áp dụng kiểu cho tất cả phần tử cùng loại trong tài liệu. Ví dụ chọn phần tử <body>:
body {
font-family: Arial;
margin: 0;
}Bộ chọn theo id
Bộ chọn theo id dùng thuộc tính id của phần tử HTML để chọn đúng một phần tử cụ thể (id là duy nhất trong một tài liệu). Trong CSS, viết dấu # kèm theo id. Khi đặt id, không được bắt đầu bằng số và không chứa khoảng trắng. Ví dụ áp dụng cho phần tử có id="avatar":
<style>
body {
background-color: #f1f1f1;
text-align: center;
}
#avatar {
border-radius: 50%;
}
</style>
<h1>Phạm Dương Minh</h1>
<svg id="avatar" width="200" height="200" viewBox="0 0 200 200">
<rect width="200" height="200" fill="#1abc9c"/>
<circle cx="100" cy="80" r="35" fill="#fff"/>
<rect x="55" y="125" width="90" height="75" rx="30" fill="#fff"/>
</svg>Giáo trình dùng thẻ <img id="avatar" src="images/me.jpg" height="200">; ở đây ta thay bằng hình SVG để chạy được ngay, nhưng quy tắc #avatar { border-radius: 50%; } giữ nguyên và biến khung vuông thành hình tròn.
Bộ chọn theo class
Bộ chọn theo class chọn các phần tử có thuộc tính class cụ thể: dùng dấu chấm . trước tên lớp. Ở ví dụ sau, mọi phần tử có class="readmore" đều được định dạng:
<style>
.readmore {
background-color: #1abc9c !important;
border-radius: 5px;
color: white;
padding: 6px 18px;
text-decoration: none;
}
</style>
<p>HTML là ngôn ngữ đánh dấu dùng để xây dựng và cấu trúc lại các thành phần có trong Website...</p>
<a class="readmore">Đọc thêm</a>Bạn cũng có thể giới hạn để chỉ phần tử thuộc một loại cụ thể mới chịu ảnh hưởng của lớp, bằng cách viết tên thẻ liền trước dấu chấm. Chỉ các thẻ <a> có class="readmore" mới nhận quy tắc a.readmore; một thẻ <p class="readmore"> thì không. Đây là bộ chọn kết hợp (compound) thẻ + lớp, không có khoảng trắng giữa hai phần.
Một phần tử có thể mang nhiều lớp (cách nhau bằng khoảng trắng) và chịu tác động của tất cả các quy tắc lớp đó. Ở ví dụ giáo trình, thẻ <a> được tạo kiểu theo cả readmore và dblock:
<style>
a.readmore {
background-color: #1abc9c !important;
border-radius: 5px;
color: white;
padding: 6px 18px;
text-decoration: none;
}
a.dblock {
text-align: center;
display: block;
}
</style>
<p>HTML là ngôn ngữ đánh dấu dùng để xây dựng và cấu trúc lại các thành phần có trong Website...</p>
<a class="readmore dblock">Đọc thêm</a>
<p class="readmore">Thẻ p này mang class readmore nhưng KHÔNG khớp a.readmore.</p>
<script>
console.log("a.readmore khớp:", document.querySelectorAll("a.readmore").length, "phần tử");
console.log(".readmore khớp:", document.querySelectorAll(".readmore").length, "phần tử");
</script>Lưu ý:
.a.b(không khoảng trắng) nghĩa là phần tử có cả hai lớpavàb. Còn.a .b(có khoảng trắng) lại là bộ chọn con cháu — một chi tiết nhỏ nhưng khác hoàn toàn (xem mục 2.4).
Bộ chọn *
Bộ chọn * (universal selector) chọn tất cả phần tử HTML trên trang. Ví dụ dưới đây ảnh hưởng đến mọi phần tử:
* {
text-align: center;
color: blue;
}Mẹo: Bộ chọn
*thường dùng để "reset" nhanh, chẳng hạn* { box-sizing: border-box; margin: 0; }. Nó có độ đặc hiệu bằng 0 nên rất dễ bị mọi quy tắc khác ghi đè.
Nhóm các bộ chọn
Ta có thể nhóm nhiều bộ chọn, phân tách bằng dấu phẩy, để áp dụng cùng một kiểu CSS và đỡ lặp mã. Ở ví dụ dưới đây, h1, #title và .center có cùng một kiểu:
h1, #title, .center {
text-align: center;
color: red;
}Trong ví dụ chạy được sau, mỗi dòng console.log cho biết bộ chọn khớp bao nhiêu phần tử. Hãy đoán trước con số rồi bấm chạy:
<style>
h1, #title, .center { text-align: center; color: crimson; }
* { font-family: sans-serif; }
</style>
<h1>Tiêu đề h1</h1>
<p id="title">Đoạn có id title</p>
<p class="center">Đoạn có class center</p>
<p>Đoạn bình thường</p>
<script>
for (const sel of ["p", "#title", ".center", "h1, #title, .center", "*"]) {
console.log(sel.padEnd(22), "→", document.querySelectorAll(sel).length);
}
</script>Đáp án: p → 3; #title → 1; .center → 1; nhóm → 3; * → 9 vì tính cả html, head, style, body, script cùng h1 và ba thẻ p. Bảng tổng kết nhóm bộ chọn đơn giản:
| Bộ chọn | Cú pháp | Chọn gì |
|---|---|---|
| Phần tử | p |
Mọi thẻ <p> |
| Id | #avatar |
Phần tử có id="avatar" (duy nhất) |
| Class | .readmore |
Mọi phần tử có lớp readmore |
| Thẻ + class | a.readmore |
Chỉ thẻ <a> có lớp readmore |
| Mọi phần tử | * |
Tất cả phần tử trên trang |
| Nhóm | h1, #title, .center |
Hợp của các bộ chọn, cùng một kiểu |
Vừa học xong bộ chọn đơn giản, hãy làm các bài sau để luyện chọn theo thẻ, class và kết hợp thẻ với lớp:
2.3. Bộ chọn thuộc tính
Bộ chọn thuộc tính chọn phần tử dựa vào thuộc tính (và giá trị thuộc tính) của chúng. Có bảy dạng, giáo trình lần lượt trình bày như sau.
[attribute]— chọn các phần tử có thuộc tính đó. Ví dụ chọn mọi<a>có thuộc tínhtarget:
a[target] {
background-color: yellow;
}[attribute="value"]— chọn các phần tử có thuộc tính mang đúng giá trị cụ thể. Ví dụ chọn<a>cótarget="_blank":
a[target="_blank"] {
background-color: yellow;
}[attribute~="value"]— chọn các phần tử có giá trị thuộc tính chứa một từ nhất định (từ đứng riêng, các từ cách nhau bằng khoảng trắng). Ví dụ chọn mọi phần tử cótitlechứa từ "box":
[title~="box"] {
border: 5px solid yellow;
}[attribute|="value"]— chọn các phần tử có giá trị thuộc tính bắt đầu bằng giá trị đó. Lưu ý giá trị phải là một từ đứng riêng, nhưclass="yellow", hoặc là phần đầu của giá trị và được nối tiếp bằng dấu gạch ngang-, nhưclass="yellow-text":
[class|="yellow"] {
background: yellow;
}[attribute^="value"]— chọn các phần tử có giá trị thuộc tính bắt đầu với chuỗi xác định (không cần là từ riêng). Ví dụ lớp bắt đầu với "top":
[class^="top"] {
background: yellow;
}[attribute$="value"]— chọn các phần tử có giá trị thuộc tính kết thúc bằng chuỗi xác định. Ví dụ lớp kết thúc bằng "test":
[class$="test"] {
background: yellow;
}[attribute*="value"]— chọn các phần tử có giá trị thuộc tính chứa chuỗi xác định ở bất kỳ vị trí nào. Ví dụ lớp chứa "red":
[class*="red"] {
background: red;
}Sự khác biệt tinh tế giữa ~=, |=, ^=, $=, *= rất dễ nhầm, nên ta kiểm tra ngay bằng mã. Mỗi phần tử có một class khác nhau; hãy đoán xem mỗi bộ chọn khớp những phần tử nào:
<p class="yellow">1. yellow</p>
<p class="yellow-text">2. yellow-text</p>
<p class="top-box">3. top-box</p>
<p class="my top">4. my top</p>
<p class="unit-test">5. unit-test</p>
<p class="dark-red-bg">6. dark-red-bg</p>
<script>
const sels = ['[class|="yellow"]', '[class^="top"]', '[class$="test"]', '[class*="red"]', '[class~="top"]'];
for (const s of sels) {
const ids = [...document.querySelectorAll(s)].map((e) => e.textContent.split(".")[0]);
console.log(s.padEnd(18), "→ phần tử số", ids.join(", ") || "(không có)");
}
</script>Đối chiếu: [class|="yellow"] khớp 1 và 2; [class^="top"] khớp 3 (còn 4 có top nhưng không đứng đầu); [class$="test"] khớp 5; [class*="red"] khớp 6; [class~="top"] khớp 4 (từ top đứng riêng, còn top-box thì không).
Một ứng dụng rất thực tế là tạo kiểu cho biểu mẫu theo type của <input>. Ví dụ giáo trình chọn mọi nút type=submit:
<style>
input[type=submit] {
padding: 10px;
font-size: 18px;
border: 1px solid #1abc9c;
background-color: #1abc9c;
color: white;
width: 100%;
}
input[type=text], input[type=email] {
width: 100%;
padding: 8px;
margin-bottom: 8px;
box-sizing: border-box;
}
</style>
<input type="text" placeholder="Họ tên">
<input type="email" placeholder="Email">
<input type="submit" value="Đăng ký">Ví dụ thực tế: Trang web thường tự đánh dấu liên kết ra ngoài:
a[href^="http"]thêm biểu tượng "mở tab mới",a[href$=".pdf"]thêm biểu tượng tệp PDF,img[src$=".png"]viền riêng cho ảnh PNG. Toàn bộ chỉ nhờ bộ chọn thuộc tính, không cần thêm class vào từng thẻ.
Cảnh báo: Giá trị trong
~=,|=,^=,$=,*=mặc định phân biệt hoa/thường và^=,$=,*=với chuỗi rỗng sẽ không khớp gì. Hãy đặt giá trị trong dấu nháy kép cho chắc.
Vừa học xong bộ chọn thuộc tính, hãy làm các bài sau để chọn theo sự tồn tại, giá trị và phần đầu/cuối của thuộc tính:
2.4. Bộ chọn tổ hợp
Bộ chọn tổ hợp (Combinator) thể hiện mối quan hệ giữa các bộ chọn, cho phép ghép nhiều bộ chọn lại với nhau. Có bốn bộ chọn tổ hợp quen thuộc:
| Bộ chọn | Ký hiệu | Chọn những phần tử… |
|---|---|---|
| Con cháu (descendant) | dấu khoảng trắng A B |
là con cháu (ở bất kỳ cấp nào) của phần tử khác |
| Con (child) | A > B |
là con trực tiếp của phần tử khác |
| Anh em liền kề (adjacent sibling) | A + B |
nằm liền kề và ngang hàng với phần tử khác |
| Anh em (general sibling) | A ~ B |
ngang hàng và đứng sau phần tử khác |
Giáo trình lưu ý về từ "con" và "con cháu": "con cháu" chỉ phần tử nằm trong phần tử khác ở bất kỳ cấp nào, còn "con" chỉ phần tử nằm trực tiếp ngay bên trong. Ví dụ với <div><span><p>example</p></span></div> thì chỉ <span> là "con" của <div>; còn cả <span> lẫn <p> đều là "con cháu" của <div>.
Để dễ quan sát, ta dùng chung một đoạn HTML cho cả bốn bộ chọn, đúng như ví dụ trong giáo trình:
<div>
<p>Phần tử p nằm trong div</p>
<p>Phần tử p nằm trong div</p>
<span>
<p>Phần tử p nằm trong div > span</p>
</span>
</div>
<p>Phần tử p nằm ngay sau div</p>
<p>Phần tử p ngang hàng, đứng sau</p>Bộ chọn con cháu (khoảng trắng)
Chọn mọi <p> nằm trong <div>, ở bất kỳ cấp nào:
div p {
background-color: yellow;
border: 1px solid;
}Bộ chọn con (>)
Chỉ những <p> là con trực tiếp của <div> mới có nền vàng; các <p> nằm sâu hơn (ví dụ bên trong <span>) thì không:
div > p {
background-color: yellow;
border: 1px solid;
}Bộ chọn anh em liền kề (+)
Chọn <p> nằm liền kề và ngang hàng với <div> (đứng ngay sau nó). Các <p> nằm trong <div> hoặc <p> ngang hàng nhưng không liền kề thì không được chọn:
div + p {
background-color: yellow;
border: 1px solid;
}Bộ chọn anh em (~)
Chọn mọi <p> ngang hàng và đứng sau <div>; các <p> bên trong <div> hoặc đứng trước <div> thì không:
div ~ p {
background-color: yellow;
}Hãy thử cả bốn bộ chọn cùng lúc. Mỗi dòng in ra số thứ tự (theo văn bản trong ngoặc) của các <p> được chọn; hãy đoán trước rồi chạy:
<div>
<p>[1] p nằm trong div</p>
<p>[2] p nằm trong div</p>
<span>
<p>[3] p nằm trong div > span</p>
</span>
</div>
<p>[4] p nằm ngay sau div</p>
<p>[5] p ngang hàng, đứng sau</p>
<style>
div { border: 2px dashed #888; padding: 6px; }
div p { background: #fff3a3; }
</style>
<script>
const show = (sel) => {
const nums = [...document.querySelectorAll(sel)].map((p) => p.textContent.match(/\[(\d)\]/)[1]);
console.log(sel.padEnd(8), "→", nums.join(", "));
};
["div p", "div > p", "div + p", "div ~ p"].forEach(show);
</script>Đáp án: div p → 1, 2, 3; div > p → 1, 2; div + p → 4; div ~ p → 4, 5. Nhận xét: + chỉ chọn một phần tử (liền ngay sau), còn ~ chọn tất cả các anh em đứng sau.
Mẹo: Hãy "đọc" bộ chọn từ phải sang trái:
div > plà "một thẻpmà cha trực tiếp làdiv". Phần tử được định dạng luôn là phần bên phải cùng của bộ chọn.
Một ứng dụng quen thuộc của + là tạo khoảng cách giữa các phần tử liên tiếp mà không "thừa" khoảng trước phần tử đầu:
<style>
.card { border: 1px solid #cbd5e1; padding: 8px; border-radius: 6px; }
.card + .card { margin-top: 12px; }
h2 + p { font-weight: bold; color: #0f766e; }
</style>
<h2>Danh sách thẻ</h2>
<p>Đoạn ngay sau h2 được in đậm (h2 + p).</p>
<p>Đoạn này không liền kề h2 nên bình thường.</p>
<div class="card">Thẻ 1</div>
<div class="card">Thẻ 2 (cách thẻ 1 một khoảng nhờ .card + .card)</div>
<div class="card">Thẻ 3</div>Vừa học xong các bộ chọn tổ hợp, hãy làm các bài sau để phân biệt con cháu, con trực tiếp và anh em:
2.5. Bộ chọn lớp giả
Bộ chọn lớp giả (pseudo-class) dùng để xác định trạng thái đặc biệt của một phần tử. Nó có thể dùng để:
- định dạng phần tử khi người dùng lướt chuột qua nó (
:hover); - định dạng liên kết khi người dùng đã truy cập (
:visited); - định dạng phần tử tại thời điểm nó đang được kích hoạt, chẳng hạn khi đang nhấn giữ chuột (
:active).
Cú pháp (một dấu hai chấm):
selector:pseudo-class {
property: value;
}Bốn lớp giả :link, :visited, :hover, :active thường dùng để cho liên kết hiển thị khác nhau ở mỗi trạng thái:
a:link { color: #FF0000; }
a:visited { color: #00FF00; }
a:hover { color: #FF00FF; }
a:active { color: #0000FF; }Lưu ý: Thứ tự quan trọng:
a:hoverphải đứng saua:linkvàa:visited;a:activephải đứng saua:hover. Lý do: các lớp giả này có cùng độ đặc hiệu nên quy tắc đọc sau thắng. Cách nhớ vui của giới lập trình là LoVe HAte: Link – Visited – Hover – Active.
Bạn hãy rê chuột vào từng mục của thanh điều hướng dưới đây — đây chính là ví dụ "thanh điều hướng" của giáo trình, dùng nav a:hover để đổi màu chữ và màu nền:
<style>
nav {
display: flex;
background-color: #333;
padding: 0px;
margin: 0px;
}
nav a {
color: white;
padding: 14px 20px;
text-decoration: none;
text-align: center;
}
nav a:hover {
background-color: #ddd;
color: black;
}
</style>
<nav>
<a href="#">Trang chủ</a>
<a href="#">Bài viết</a>
<a href="#">Dự án</a>
<a href="#">Liên hệ</a>
</nav>:hover là lớp giả mô tả trạng thái con trỏ chuột đang nằm trên phần tử; làm nổi bật phần tử khiến người dùng dễ quan sát hơn.
Lớp giả cũng chọn theo vị trí. :first-child so khớp với phần tử là con đầu tiên của phần tử chứa nó: p:first-child chọn mọi <p> là con đầu tiên của cha (không chọn các <p> còn lại); p i:first-child chọn <i> là con đầu tiên nằm trong <p>.
p:first-child {
color: blue;
}
p i:first-child {
color: blue;
}Ngoài ra còn nhiều lớp giả khác. Bảng 3‑1 của giáo trình liệt kê các lớp giả thường dùng:
| Bộ chọn | Ví dụ | Mô tả ví dụ |
|---|---|---|
:checked |
input:checked |
Chọn <input> đã được chọn (checked) |
:disabled |
input:disabled |
Chọn <input> bị vô hiệu hóa (disabled) |
:empty |
p:empty |
Chọn <p> không có con |
:enabled |
input:enabled |
Chọn <input> không bị vô hiệu hóa |
:invalid |
input:invalid |
<input> có giá trị không hợp lệ |
:lang(language) |
p:lang(it) |
<p> có thuộc tính lang bắt đầu với "it" |
:last-child |
p:last-child |
<p> là con cuối cùng |
:not(selector) |
:not(p) |
Không phải là <p> |
:nth-child(n) |
p:nth-child(2) |
<p> là con thứ hai |
:nth-last-child(n) |
p:nth-last-child(2) |
<p> là con thứ hai của cha, tính từ con cuối cùng |
:only-child |
p:only-child |
<p> là con duy nhất |
:out-of-range |
input:out-of-range |
<input> có giá trị không nằm trong khoảng cho phép |
:read-only |
input:read-only |
<input> có thuộc tính "readonly" |
:read-write |
input:read-write |
<input> không có thuộc tính "readonly" |
:required |
input:required |
<input> có thuộc tính "required" |
:valid |
input:valid |
<input> có giá trị hợp lệ |
Kiểm chứng các lớp giả theo vị trí và trạng thái bằng mã. Danh sách có năm mục, ô đầu bị vô hiệu hóa, và có một thẻ <p> rỗng:
<style>
li:nth-child(even) { background: #e0f2fe; }
li:first-child { font-weight: bold; }
li:last-child { color: crimson; }
input:disabled { background: #e5e7eb; }
input:required { border: 2px solid #f59e0b; }
p:empty { height: 20px; background: #fecaca; }
</style>
<ul>
<li>Mục 1</li><li>Mục 2</li><li>Mục 3</li><li>Mục 4</li><li>Mục 5</li>
</ul>
<input id="i1" disabled value="Bị khóa">
<input id="i2" required placeholder="Bắt buộc">
<input id="i3" type="checkbox" checked>
<p></p>
<p>Đoạn có nội dung</p>
<script>
const q = (s) => document.querySelectorAll(s).length;
console.log("li:nth-child(even) →", q("li:nth-child(even)"));
console.log("li:nth-child(2n+1) →", q("li:nth-child(2n+1)"));
console.log("li:nth-last-child(2) →", document.querySelector("li:nth-last-child(2)").textContent);
console.log("input:disabled →", q("input:disabled"), "| input:enabled →", q("input:enabled"));
console.log("input:required →", q("input:required"), "| input:checked →", q("input:checked"));
console.log("p:empty →", q("p:empty"), "| li:not(:first-child) →", q("li:not(:first-child)"));
</script>Đối chiếu: :nth-child(even) chọn mục 2, 4 (2 phần tử); 2n+1 chọn 1, 3, 5 (3 phần tử); :nth-last-child(2) là "Mục 4"; ba <input> gồm 1 bị khóa nên :enabled có 2 (ô bắt buộc và checkbox); :not(:first-child) loại mục đầu nên còn 4.
Mở rộng: Trong
:nth-child(n),ncó thể là số (2), từ khóaodd(lẻ) /even(chẵn) hoặc công thứcan+b(ví dụ3nchọn mục 3, 6, 9…). Lớp giả:hovercần con trỏ chuột thật nên không thể in ra bằngconsole.log; hãy rê chuột trực tiếp vào khung xem trước để thấy hiệu ứng.
2.6. Bộ chọn phần tử giả
Bộ chọn phần tử giả (pseudo-element) dùng để tạo kiểu cho một phần của phần tử HTML — phần đó không tồn tại như một phần tử thật trong tài liệu. Viết với hai dấu hai chấm ::. Chúng hữu ích để:
-
tạo kiểu cho chữ cái đầu tiên hoặc dòng đầu tiên của một phần tử;
-
chèn nội dung trước hoặc sau nội dung của một phần tử.
-
::first-line— thêm phong cách riêng cho dòng đầu tiên của văn bản. Ví dụ định dạng dòng đầu của mọi<p>(chỉ áp dụng được cho phần tử mức khối):
p::first-line {
color: #ff0000;
font-variant: small-caps;
}::first-letter— thêm phong cách riêng cho chữ cái đầu tiên của văn bản (cũng chỉ áp dụng cho phần tử mức khối, như<p>):
p::first-letter {
color: #ff0000;
font-size: xx-large;
}Bạn có thể giới hạn bằng lớp, ví dụ chỉ đoạn dẫn class="intro":
p.intro::first-letter {
color: #ff0000;
font-size: 200%;
}::before— chèn nội dung trước nội dung của một phần tử;::after— chèn nội dung sau nội dung. Giáo trình chèn hình ảnh bằngcontent: url(smiley.gif):
h1::before {
content: url(smiley.gif);
}
h1::after {
content: url(smiley.gif);
}::selection— xác định phần nội dung mà người dùng bôi chọn. Chỉ một số thuộc tính áp dụng được:color,background,cursor,outline.
::selection {
color: red;
background: yellow;
}CSS trên làm cho vùng văn bản người dùng chọn có chữ đỏ trên nền vàng. Hãy chạy ví dụ tổng hợp bốn phần tử giả; thử bôi chọn một đoạn văn trong khung xem trước để thấy ::selection:
<style>
p::first-line { color: #b91c1c; font-variant: small-caps; }
p.intro::first-letter { color: #b91c1c; font-size: 200%; }
h1::before { content: "★ "; color: #f59e0b; }
h1::after { content: " ★"; color: #f59e0b; }
::selection { color: red; background: yellow; }
</style>
<h1>Tin mới</h1>
<p class="intro">Phần tử giả cho phép tạo kiểu cho một phần của phần tử mà không cần thêm thẻ HTML nào. Dòng đầu tiên của đoạn này được định dạng riêng, còn chữ cái đầu to hơn.</p>
<p>Thử bôi chọn đoạn này bằng chuột để thấy màu nền vàng của ::selection.</p>
<script>
console.log("h1 trong DOM chỉ có", document.querySelector("h1").childNodes.length, "nút con (::before/::after không phải nút thật)");
console.log("Nội dung h1:", document.querySelector("h1").textContent);
</script>Dòng console.log cho thấy ::before/::after không tạo ra nút DOM nào: textContent của <h1> vẫn chỉ là "Tin mới". Vì thế bạn không thể bôi chọn hay tìm chúng bằng JavaScript.
Cảnh báo:
::beforevà::afterkhông hiển thị nếu thiếu thuộc tínhcontent(kể cảcontent: ""để chèn "hộp trống"). Đây là lỗi hay gặp nhất khi mới dùng phần tử giả. Ngoài ra,::first-linevà::first-letterchỉ hoạt động trên phần tử khối.
Lưu ý: Phiên bản CSS3 quy định phần tử giả viết với hai dấu hai chấm (
::before) để phân biệt với lớp giả một dấu hai chấm (:hover). Trình duyệt vẫn chấp nhận:beforekiểu cũ, nhưng bạn nên viết::.
Bảng phân biệt lớp giả và phần tử giả:
| Lớp giả (pseudo-class) | Phần tử giả (pseudo-element) | |
|---|---|---|
| Ký hiệu | Một dấu hai chấm : |
Hai dấu hai chấm :: |
| Chọn theo | Trạng thái hoặc vị trí của phần tử có thật | Một phần của phần tử (hoặc nội dung chèn thêm) |
| Ví dụ | :hover, :checked, :nth-child(2) |
::first-letter, ::before, ::selection |
| Độ đặc hiệu | Cộng vào cột b (như class) | Cộng vào cột c (như thẻ) |
Vừa học xong lớp giả và phần tử giả — cũng là toàn bộ các loại bộ chọn — hãy làm các bài sau, gồm cả bài tổng hợp nhiều loại bộ chọn và độ ưu tiên:
Thực hành theo giáo trình
Giáo trình: Chương 3 — cuối mục "Bộ chọn CSS" có công cụ thực hành "Bộ chọn CSS sáng đèn"; các bài thực hành 3.1, 3.2, 3.3 của chương (trang chào mừng, trang giới thiệu bản thân, trang đăng ký khóa học) cần thêm kiến thức màu sắc, văn bản, viền, biểu mẫu nên được làm ở buổi 7.
Mục tiêu: luyện phản xạ "nhìn HTML → viết đúng bộ chọn" bằng cách thử và quan sát phần tử nào "sáng đèn".
Các bước thực hiện (theo tinh thần công cụ "Bộ chọn CSS sáng đèn"):
- Dùng ô thử bộ chọn ở mục 2.1 (
selector-lab), gõ lần lượtp,#title,.center,h1, #title, .centervà quan sát phần tử được tô sáng. - Thử các bộ chọn thuộc tính:
a[href],a[target="_blank"],[class^="top"],[class*="red"]. - Thử các bộ chọn tổ hợp:
div p,div > p,div + p,div ~ pvà so sánh kết quả với dự đoán của bạn. - Thử lớp giả:
li:first-child,li:nth-child(odd),input:not([type="checkbox"]). - Mở trình soạn thảo trực tiếp, viết một trang gồm một thanh điều hướng (
nav a) và ba thẻ tin tức, rồi tô kiểu bằng đủ cả năm loại bộ chọn.
(Chương trình không in gì.)
Chạy trong khung cách ly ngay trên trình duyệt của bạn; không có mạng.
Bạn cũng có thể chạy "bài chấm điểm tự động" của chính mình bằng mã. Ví dụ dưới in bảng "bộ chọn → số phần tử khớp" cho một đoạn HTML mẫu — hãy sửa các bộ chọn trong mảng và xem con số thay đổi:
<article class="post">
<h2 id="main-title">Học CSS</h2>
<p class="lead">Đoạn dẫn.</p>
<p>Đoạn thường.</p>
<ul class="menu">
<li class="active">Trang chủ</li>
<li>Bài viết</li>
<li><a href="https://neu.edu.vn" target="_blank">NEU</a></li>
</ul>
</article>
<script>
const tests = [
"p", ".lead", "#main-title", "article > p", "article p", "h2 + p",
"h2 ~ p", "li.active", "a[target=_blank]", "a[href^=https]", "li:not(.active)", "li:last-child"
];
for (const t of tests) {
console.log(t.padEnd(18), document.querySelectorAll(t).length);
}
</script>Câu hỏi ôn tập
Các câu 3.1 – 3.6 và 3.10 của giáo trình thuộc nội dung buổi này (các câu 3.7 – 3.9 về thuộc tính định dạng sẽ ôn ở buổi 7).
3.1. Mã HTML chính xác để tham chiếu đến CSS bên ngoài là gì?
A. <style src="mystyle.css"> · B. <stylesheet>mystyle.css</stylesheet> · C. <link rel="stylesheet" type="text/css" href="mystyle.css"> · D. <link rel="stylesheet" type="text/css" url="mystyle.css">
Đáp án: C — dùng phần tử <link> với thuộc tính href trỏ tới tệp .css.
3.2. Vị trí nào trong tài liệu HTML là nơi chính xác để tham chiếu đến CSS bên ngoài?
A. Trong phần <body> · B. Cuối của tài liệu HTML · C. Trong phần <head> · D. Trong phần <footer>
Đáp án: C — phần tử <link> đặt trong phần <head>.
3.3. Thẻ HTML nào được dùng để định nghĩa một CSS nội bộ?
A. <script> · B. <style> · C. <css> · D. <internal>
Đáp án: B — CSS nội bộ nằm trong phần tử <style>.
3.4. Thuộc tính nào được dùng để tạo CSS nội tuyến?
A. style · B. font · C. styles · D. class
Đáp án: A — thuộc tính style đặt trực tiếp trong phần tử HTML.
3.5. Đâu là cách để chèn chú thích vào tệp CSS?
A. //this is a comment · B. //this is a comment // · C. /* this is a comment */ · D. ' this is a comment
Đáp án: C — chú thích CSS bắt đầu bằng /* và kết thúc bằng */.
3.6. Đâu là cách để thêm màu nền cho tất cả các phần tử <h1>?
A. all.h1 {background-color:#FFFFFF;} · B. h1.all {background-color:#FFFFFF;} · C. h1 {background-color:#FFFFFF;} · D. h1: all {background-color:#FFFFFF;}
Đáp án: C — bộ chọn phần tử h1 chọn mọi thẻ <h1>.
3.10. Đâu là cú pháp CSS chính xác?
A. body {color: black;} · B. {body;color:black;} · C. body:color=black; · D. {body:color=black;}
Đáp án: A — bộ chọn đứng trước, khối khai báo { thuộc tính: giá trị; } đứng sau.
Ba câu tự luyện thêm về bộ chọn:
T1. Phân biệt div p, div > p, div + p và div ~ p.
Đáp án: div p chọn mọi <p> là con cháu của <div>; div > p chỉ chọn <p> là con trực tiếp; div + p chọn <p> đứng liền ngay sau <div> (cùng cấp); div ~ p chọn mọi <p> cùng cấp đứng sau <div>.
T2. Bộ chọn [class|="yellow"] khớp được class="yellow-text" không? Còn class="light-yellow"?
Đáp án: Khớp yellow-text (và yellow), vì giá trị bắt đầu bằng "yellow" theo sau là dấu gạch ngang hoặc đứng riêng; không khớp light-yellow (muốn khớp phải dùng [class*="yellow"]).
T3. Một thẻ <p id="myid" class="myclass"> có quy tắc p { color: red; }, .myclass { color: gray; }, #myid { color: blue; }. Chữ có màu gì? Nếu thêm !important vào quy tắc .myclass thì sao?
Đáp án: Màu blue, vì bộ chọn id có độ đặc hiệu cao nhất. Khi .myclass { color: gray !important; } thì thành gray, vì khai báo !important thắng mọi khai báo thường.
Sai lầm thường gặp
| Sai lầm | Hậu quả | Cách đúng |
|---|---|---|
Quên dấu ; giữa các khai báo |
Trình duyệt bỏ qua khai báo (và đôi khi cả khai báo kế tiếp) | Kết thúc mọi khai báo bằng ; |
Viết chú thích // … hoặc <!-- --> trong CSS |
Quy tắc đứng sau chú thích bị hỏng | Dùng /* … */ |
Đặt <link> hoặc <style> trong <body> hoặc quên rel="stylesheet" |
CSS không được áp dụng hoặc giật hình lúc tải | Đặt trong <head>, đủ rel="stylesheet" và href |
Lạm dụng CSS nội tuyến và !important |
Khó bảo trì, khó ghi đè, mã rối | Dùng CSS ngoài/nội bộ; chỉnh bộ chọn trước khi dùng !important |
Nhầm .a.b (cùng một phần tử, hai lớp) với .a .b (con cháu) |
Chọn sai hoặc không chọn được phần tử | Không có khoảng trắng = cùng phần tử; có khoảng trắng = con cháu |
Viết # cho class hoặc . cho id |
Bộ chọn không khớp gì | #id cho id, .class cho class |
Nhầm div p với div > p |
Định dạng lan xuống cả các thẻ lồng sâu | Dùng > khi chỉ muốn con trực tiếp |
Đặt a:hover trước a:link/a:visited |
Hiệu ứng rê chuột không hiện | Theo thứ tự LoVe HAte: link, visited, hover, active |
Dùng ::before/::after mà thiếu content |
Không hiện gì | Luôn khai báo content (có thể content: "") |
| Đặt id trùng nhau trong một trang, hoặc id bắt đầu bằng số | Bộ chọn id chọn sai hoặc không hoạt động | Mỗi id duy nhất, bắt đầu bằng chữ cái, không có khoảng trắng |
Tóm tắt
- CSS (Cascading Style Sheets) tách kiểu dáng khỏi nội dung; một quy tắc gồm bộ chọn và khối khai báo
{ thuộc tính: giá trị; }, mỗi khai báo kết thúc bằng;; chú thích là/* … */. - Ba cách chèn CSS: bên ngoài (
<link>trong<head>, tệp.css, dùng cho nhiều trang), nội bộ (<style>, một trang), nội tuyến (thuộc tínhstyle, một phần tử — hạn chế dùng). - Khi các quy tắc cùng độ ưu tiên thì quy tắc đọc sau thắng; bộ chọn id mạnh hơn class/thuộc tính/lớp giả, mạnh hơn thẻ/phần tử giả;
!importantthắng tất cả nhưng nên hạn chế;all: initial | inherit | unsetđặt lại toàn bộ thuộc tính. - Năm loại bộ chọn: đơn giản (thẻ,
#id,.class,*, nhóm bằng dấu phẩy), thuộc tính ([a],=,~=,|=,^=,$=,*=), tổ hợp (khoảng trắng,>,+,~), lớp giả (:hover,:first-child,:nth-child(),:not()…), phần tử giả (::first-line,::first-letter,::before,::after,::selection). - Buổi sau (buổi 7) bạn sẽ dùng các bộ chọn này để định dạng màu sắc, nền, viền, văn bản, bảng, danh sách, liên kết và biểu mẫu.