
Web Development Essentials 035.3: Khái niệm cơ bản về SQL
Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Đọc bộ chọn CSS theo phần tử, class, id và quan hệ; giải thích cascade, specificity, kế thừa, important và kiểm tra focus, hover trong lab có hoàn tác.
Một trang tiếp nhận yêu cầu có thanh điều hướng, thẻ hướng dẫn và liên kết hỗ trợ. Khi đổi màu liên kết trong thẻ, cả thanh điều hướng cũng đổi theo. Thêm !important có thể che triệu chứng, nhưng người bảo trì vẫn chưa biết quy tắc nào đang áp dụng. Bài này xây dựng cách đọc bộ chọn và lần theo giá trị CSS trước khi sửa.
Mục tiêu tham chiếu là 033.2, Bộ chọn CSS và Ứng dụng kiểu trong Web Development Essentials 030-100. Nội dung được biên soạn độc lập, cập nhật ngày 23/10/2026. Bạn cần biết cấu trúc HTML, thuộc tính class/id và ba cách thêm CSS ở bài trước. Các ví dụ dùng dữ liệu DEMO cục bộ, không yêu cầu framework.
Sau bài học, bạn có thể chỉ ra phần tử mà một bộ chọn khớp, phân biệt danh sách với quan hệ tổ tiên, giải thích một xung đột có điều kiện cụ thể và tạo trạng thái tương tác dùng được bằng bàn phím. Tiêu chí hoàn thành là dự đoán được kết quả trước khi mở trang, sau đó đối chiếu với DOM và giá trị computed.
p chọn phần tử có tên thẻ p. .notice chọn phần tử có token class notice; một phần tử có thể mang nhiều class cách nhau bằng khoảng trắng. #ticket chọn phần tử có id ticket. HTML yêu cầu id duy nhất trong tài liệu, vì liên kết phân đoạn, label và script còn dùng định danh này. Trình duyệt vẫn có thể dựng một tài liệu sai có id trùng; khả năng hiển thị không chứng minh tài liệu đúng.
Class phù hợp với kiểu được dùng lại, chẳng hạn thẻ hướng dẫn hoặc nút hành động. Đừng dùng id cho mọi quy tắc chỉ để tăng độ ưu tiên. Khi giao diện thay đổi, các bộ chọn quá mạnh khiến người sau phải viết quy tắc mạnh hơn nữa. Tên class nên diễn đạt vai trò hoặc trạng thái ổn định như .help-card, thay vì mô tả màu hiện tại như .blue.
| Cú pháp | Phần tử khớp |
|---|---|
h2, .notice |
Mọi h2 và mọi phần tử có class notice |
a.notice |
Liên kết a đồng thời mang class notice |
.help-card.urgent |
Một phần tử có cả hai class |
.help-card a |
Liên kết nằm bên trong tổ tiên help-card, ở bất kỳ độ sâu nào |
.help-card > a |
Liên kết là con trực tiếp của help-card |
* |
Mọi phần tử, với độ đặc hiệu bằng không |
Dấu phẩy tạo danh sách bộ chọn riêng. Khoảng trắng tạo quan hệ hậu duệ. Hai class liền nhau cùng kiểm tra một phần tử. Ba trường hợp này khác nhau dù tên class giống hệt. Bộ chọn body chỉ khớp body; việc màu chữ truyền xuống con là kế thừa. Trong khi đó, * { color: ... } trực tiếp khai báo màu cho từng phần tử khớp, nên có thể thay đổi kết quả so với chỉ đặt màu trên body.
a:hover khớp liên kết khi thiết bị trỏ đang ở trên nó. a:focus khớp khi liên kết có focus, chẳng hạn sau khi người dùng nhấn Tab. Focus không chỉ dành cho trường nhập liệu. Liên kết có href và nhiều điều khiển HTML cũng có thể nhận focus.
Phần mở rộng thực tế là :focus-visible: trình duyệt quyết định khi nào cần hiển thị chỉ báo focus theo cách tương tác và loại điều khiển. Nó giúp thiết kế vòng focus rõ ràng mà vẫn giữ ngữ nghĩa HTML. Không xóa outline rồi chỉ thêm màu hover. Trên thiết bị cảm ứng, hover có thể không tồn tại hoặc có hành vi khác với chuột; chức năng chính phải dùng được mà không phụ thuộc hover.
Trong bài này, hover đổi gạch chân, còn focus-visible thêm outline. Đây là hai phản hồi bổ sung. Trạng thái không làm liên kết tự hoạt động như nút, không thay đổi đích href và không xác thực quyền truy cập. Muốn thao tác trên dữ liệu, vẫn phải có logic phù hợp và kiểm tra phía server.

Khi nhiều khai báo cạnh tranh cho cùng thuộc tính trên cùng phần tử, trình duyệt dùng cascade. Trước hết phải xét khai báo có liên quan hay không, nguồn gốc và mức quan trọng, cùng các tầng cascade nếu có. Sau đó mới so độ đặc hiệu giữa các ứng viên còn lại; khoảng cách scope và thứ tự nguồn có thể phân xử tiếp. Bài nhập môn này dùng CSS tác giả, không có layer hoặc @scope, nên dễ quan sát từng yếu tố riêng.
Độ đặc hiệu thường được đọc thành ba nhóm: số id; số class, thuộc tính và pseudo-class; số tên thẻ và pseudo-element. So từ trái sang phải, không cộng chúng thành một số thập phân. Một id vượt nhiều class trong phép so này. Bộ chọn phổ quát và dấu quan hệ không tự cộng trọng số. Với các bộ chọn thông thường trong lab, #priority là 1-0-0, .help-card a là 0-1-1, còn a là 0-0-1.
Nếu hai khai báo cùng nguồn, mức quan trọng, layer và độ đặc hiệu, đồng thời không có khác biệt scope, khai báo đứng sau thắng. Vì vậy, “dòng cuối luôn thắng” chỉ đúng khi các yếu tố trước đã ngang nhau. Một stylesheet tải sau không mặc nhiên ghi đè mọi quy tắc tải trước.
!important đánh dấu mức quan trọng của khai báo, không tăng độ đặc hiệu của selector. Nó không phải quyền ưu tiên tuyệt đối trước mọi nguồn và mọi trạng thái của hệ thống cascade. Trong phạm vi lab, khai báo author important thắng author normal cho cùng thuộc tính, dù selector normal có id. Nếu cần dùng important thật, ghi rõ lý do và phạm vi; trước tiên kiểm tra xem có thể sửa bộ chọn hoặc cấu trúc stylesheet hay không.
Kế thừa được xét khi phần tử không nhận một giá trị được chỉ định thích hợp cho thuộc tính đó. Màu trực tiếp trên con có thể thắng màu được thừa hưởng từ cha, dù bộ chọn cha có id. Không so độ đặc hiệu của tổ tiên với selector khớp trực tiếp con như hai ứng viên bình đẳng. Đây là nguyên nhân thường gặp khi người học tưởng id “không đủ mạnh”.
Tạo thư mục mới chứa hai tệp sau. Mở index.html bằng trình duyệt; không nhập thông tin thật. Chạy từ tệp cục bộ đủ cho ví dụ này vì không có fetch, module hoặc server. Mở DevTools để xem Styles và Computed. Nếu giao diện DevTools khác phiên bản, tìm phần hiển thị quy tắc khớp và giá trị cuối thay vì phụ thuộc vị trí nút.
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DEMO bộ chọn CSS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav aria-label="Chính"><a href="#guide">Hướng dẫn</a></nav>
<main id="guide">
<h1>Tiếp nhận yêu cầu DEMO</h1>
<section class="help-card urgent" aria-labelledby="card-title">
<h2 id="card-title">Trước khi gửi yêu cầu</h2>
<p class="notice">Ghi triệu chứng, không ghi mật khẩu.</p>
<a id="priority" class="action" href="#contact">Mở mục liên hệ</a>
<p><a class="secondary" href="#contact">Xem lựa chọn khác</a></p>
</section>
<section id="contact"><h2>Liên hệ DEMO</h2><p>Chưa gửi dữ liệu đi đâu.</p></section>
</main>
</body>
</html>
body { color: #213641; font-family: sans-serif; line-height: 1.6; }
a { color: #135f75; }
.help-card { border: 2px solid #607d8b; padding: 1rem; }
.help-card.urgent { border-color: #b45309; }
.help-card a { color: #6b21a8; }
#priority { color: #166534; }
.help-card .notice { color: #374151; }
.notice { color: #b91c1c; }
#guide { color: #92400e; }
.help-card a:hover { text-decoration-thickness: 3px; }
a:focus-visible { outline: 3px solid #b45309; outline-offset: 3px; }
Trước khi mở trang, ghi dự đoán cho bốn màu chữ: liên kết điều hướng, liên kết priority, liên kết secondary và đoạn notice. Tiếp theo dự đoán màu viền của thẻ. Đừng lấy màu nhìn trên màn hình làm bằng chứng duy nhất; ánh sáng và màn hình ảnh hưởng cảm nhận. Trong Computed, đối chiếu giá trị rgb và lần về khai báo nguồn.
Kết quả mong đợi: liên kết điều hướng dùng #135f75; priority dùng #166534; secondary dùng #6b21a8; notice dùng #374151. Viền thẻ dùng #b45309. Quy tắc .notice đứng sau vẫn thua .help-card .notice vì độ đặc hiệu thấp hơn trong cùng điều kiện cascade. Màu từ #guide được kế thừa tới những nội dung phù hợp, nhưng không ghi đè màu đã khai báo trực tiếp trên các liên kết và notice.
Thử document.querySelectorAll('.help-card > a').length trong Console rồi đổi sang .help-card a. Kết quả tương ứng là 1 và 2: liên kết trong p là hậu duệ nhưng không phải con trực tiếp. Đổi tiếp sang .help-card.urgent và .help-card .urgent; kết quả là 1 và 0. Các lệnh chỉ đọc DOM của trang DEMO.
Nhấn Tab từ đầu trang để kiểm tra từng liên kết. Khi focus đến priority, phải có vòng outline nhìn thấy. Đưa chuột lên secondary để quan sát độ dày gạch chân. Không dùng hover làm tiêu chí duy nhất cho khả năng tương tác. Trạng thái focus và hover có thể đồng thời tồn tại.
Thêm .notice { color: #0f766e; !important: true; } là cú pháp sai cố ý: trình duyệt không hiểu important dưới dạng tên thuộc tính. Xóa toàn bộ dòng thử đó ngay sau khi quan sát; cách đúng là .notice { color: #0f766e !important; }. Với cách đúng, màu notice đổi sang xanh trong phạm vi author CSS này. Hoàn tác để tránh biến bài tập thành cuộc đua important.
Sau đó thêm .help-card a { color: #7e22ce; } ở cuối. Secondary đổi màu do cùng selector và cùng điều kiện, priority vẫn xanh vì id mạnh hơn. Xóa dòng thêm và xác nhận giá trị trở lại ban đầu. Mỗi lần chỉ sửa một yếu tố để giải thích được nguyên nhân.

Lab đã được chạy ngày 23/10/2026 với Chromium 151.0.7922.34, dùng đúng HTML và CSS trên trong context mới, chặn yêu cầu mạng. Các giá trị màu, số phần tử khớp, Tab, hover, hai cách viết important và hoàn tác đều được đối chiếu bằng assertion. Đây chưa phải kiểm thử trình đọc màn hình, mọi trình duyệt hoặc đánh giá toàn bộ WCAG.
Nếu không thấy quy tắc trong Styles, kiểm tra tệp CSS có tải đúng rồi kiểm tra class trong DOM. Nếu thấy khai báo bị gạch, tìm ứng viên thắng cùng thuộc tính. Nếu computed khác dự đoán, kiểm tra kế thừa, trạng thái tương tác, media và stylesheet của thành phần khác trước khi tăng độ đặc hiệu. Ghi lại selector, thuộc tính, nguồn khai báo và kết quả sau hoàn tác để người hỗ trợ tái hiện được.
Tự giải thích: vì sao .help-card .urgent không khớp chính thẻ? Vì sao id của main không ép mọi liên kết cùng màu? Khi nào quy tắc đặt sau thắng? Important thay đổi trọng số selector hay mức quan trọng? Nếu chỉ có chuột mới thấy phản hồi, cần kiểm tra gì bằng bàn phím? Câu trả lời phải gắn với phần tử và thuộc tính cụ thể trong lab.

Tạo bảng SQLite, hiểu khóa chính và CHECK, dùng CRUD có WHERE và bind tham số từ Node.js; kiểm tra transaction rollback, mở lại dữ liệu và API driver.

Tạo route Express, phục vụ CSS và render EJS; đọc query, params, body, kiểm tra dữ liệu form, escape HTML và phân biệt XSS với CSRF qua lab loopback.

Chạy Node.js LTS, phân biệt ES module và CommonJS, package.json và lockfile; cài package DEMO cục bộ, dùng npm ci và kiểm tra server HTTP trên loopback.

Chọn node DOM, phân biệt collection sống và tĩnh, textContent và innerHTML; cập nhật class, thuộc tính và sự kiện nút bằng chuột, bàn phím trong lab DEMO.