Buổi 8: Thiết kế bố cục (1): mô hình hộp, kích thước và vị trí
Hãy mở trang chủ của một cửa hàng trực tuyến mà bạn hay vào. Dù nội dung khác nhau, gần như trang nào cũng có chung một "dáng": logo và thanh điều hướng ở trên cùng, ảnh quảng cáo rộng hết bề ngang, các thẻ sản phẩm xếp đều tăm tắp, nút "Về đầu trang" luôn nằm ở góc dưới bên phải. Những thứ đó không tự nhiên mà có. Mỗi phần tử trên trang là một chiếc hộp, và người thiết kế phải quyết định hộp đó rộng bao nhiêu, cách các hộp khác bao nhiêu, nằm ở đâu, có đè lên hộp nào không, nội dung dài quá thì xử lý ra sao. Buổi này bạn sẽ học bộ "ngôn ngữ" để ra những quyết định ấy bằng CSS: mô hình hộp, kích thước, khoảng cách, hiển thị, căn chỉnh, vị trí, tràn nội dung và trôi. Hai kỹ thuật bố cục hiện đại là Flexbox và Grid sẽ để dành cho buổi 9; để dùng chúng tốt, trước hết bạn phải hiểu thật chắc những nền tảng của buổi này.
Mục tiêu buổi học
- Sau buổi này bạn có thể mô tả mô hình hộp (box model) gồm bốn lớp content – padding – border – margin và tính đúng kích thước thực của một phần tử, kể cả khi dùng
box-sizing. - Sau buổi này bạn có thể đặt độ rộng, chiều cao và giới hạn tối đa bằng
width,height,max-width,max-height, đồng thời chọn đúng đơn vị tuyệt đối hoặc tương đối (px,%,em,rem,vw,vh…). - Sau buổi này bạn có thể điều khiển khoảng cách bằng
marginvàpadding(viết rút gọn 1–4 giá trị,auto,inherit) và giải thích hiện tượng gom lề (margin collapsing). - Sau buổi này bạn có thể chọn kiểu hiển thị
display(none,inline,block,inline-block) và căn chỉnh phần tử, văn bản theo chiều ngang lẫn chiều dọc. - Sau buổi này bạn có thể định vị phần tử bằng
position(static,relative,absolute,fixed,sticky) vàz-index, xử lý nội dung tràn bằngoverflow, và dàn các phần tử cạnh nhau bằngfloatkèmclear.
Bản đồ buổi học
| Phần | Nội dung | Thời lượng gợi ý |
|---|---|---|
| 1. Giới thiệu về thiết kế bố cục | Layout là gì, bảy vấn đề cần quan tâm | 10 phút |
| 2. Mô hình hộp | content, padding, border, margin; box-sizing |
25 phút |
| 3. Độ rộng và chiều cao | width, height, max-width, max-height |
15 phút |
| 4. Đơn vị kích thước | Tuyệt đối, tương đối, khi nào dùng gì | 20 phút |
| 5–6. Khoảng cách lề và đệm | margin, padding, rút gọn, auto, gom lề |
30 phút |
7–8. display và căn chỉnh |
Khối, nội tuyến, căn giữa, căn trái/phải, căn dọc | 30 phút |
| 9. Thiết lập vị trí | position, z-index |
25 phút |
10–11. overflow và float |
Nội dung tràn, trôi và clear |
15 phút |
| Thực hành & ôn tập | Thực hành theo giáo trình, câu hỏi ôn tập, bài tập trên Codelab | 10 phút |
Khoảng một nửa thời gian của buổi là thực hành: mỗi phần lý thuyết đi kèm các ví dụ chạy thử (bấm nút chạy và quan sát bản xem trước) và một nhóm bài tập trên Codelab. Hãy vừa đọc vừa sửa số trong ví dụ để thấy kết quả đổi theo.
1. Giới thiệu về thiết kế bố cục
Giáo trình: Chương 4 — mục "Giới thiệu về thiết kế bố cục".
Thiết kế bố cục (Layout) là quá trình sắp xếp, tổ chức và định hình các phần tử trên một trang Web hoặc ứng dụng để tạo ra một cấu trúc hợp lý, dễ hiểu và hấp dẫn cho người sử dụng. Việc sắp xếp này liên quan đến vị trí, kích thước và khoảng cách giữa các phần tử như văn bản, hình ảnh, ô nhập liệu, nút bấm và các thành phần khác.
Bố cục không chỉ là chuyện "cho đẹp". Nó ảnh hưởng trực tiếp đến trải nghiệm người dùng (UX): một bố cục tốt giúp người dùng nhanh chóng tìm thấy thông tin cần thiết và tương tác hiệu quả; một bố cục tồi khiến họ rời trang sau vài giây. Khi thiết kế bố cục bằng CSS, giáo trình nêu bảy vấn đề cần quan tâm để trang hoạt động hiệu quả và hiển thị tốt trên nhiều loại thiết bị:
| Vấn đề | Ý nghĩa | Học ở |
|---|---|---|
| Mô hình hộp (Box model) | Kích thước, khoảng cách, viền của mọi phần tử HTML | Buổi này |
| Thuộc tính hiển thị (Display) | Xác định cách phần tử HTML hiển thị trên trang | Buổi này |
| Căn chỉnh (Alignment) | text-align, vertical-align, line-height để căn văn bản và phần tử |
Buổi này |
| Vị trí (Positioning) | position với relative, absolute, fixed, sticky |
Buổi này |
| Thuộc tính tràn (Overflow) | Quản lý nội dung tràn ra khỏi phần tử cha: hidden, scroll, auto |
Buổi này |
| Bố cục linh hoạt (Flexbox) và lưới (Grid) | Flexbox cho một hàng/cột; Grid cho cấu trúc lưới phức tạp | Buổi 9 |
| Thiết kế đáp ứng (Responsive design) | Dùng media queries để thích ứng nhiều kích thước màn hình | Buổi 9 |
Lưu ý: Thứ tự học trong chương 4 đi từ "viên gạch" (mô hình hộp) đến "cách xếp gạch" (vị trí, Flexbox, Grid) rồi tới "xây cho mọi kích cỡ màn hình" (đáp ứng). Nếu bỏ qua mô hình hộp, bạn sẽ gặp những lỗi khó hiểu như hai cột 50% bị đẩy xuống dòng, hay một hộp "rộng hơn mình khai báo".
2. Mô hình hộp
Giáo trình: Chương 4 — mục "Mô hình hộp" (kể cả "Cách tính kích thước hộp (box-sizing)").
Trong CSS, mô hình hộp hay box model là khái niệm cơ bản và rất quan trọng, mô tả cách các phần tử HTML được hiểu và hiển thị trên trang Web. Mọi phần tử đều là một hộp chữ nhật gồm bốn thành phần, từ ngoài vào trong lần lượt là margin, border, padding và content:
- content — vùng nội dung của hộp, nơi chứa văn bản, hình ảnh hoặc các phần tử con.
- padding — không gian xung quanh nội dung (bên trong đường viền).
- border — đường viền bao quanh phần padding và nội dung.
- margin — không gian bên ngoài border, tách hộp này khỏi hộp khác.
div {margin: 20px;border: 10px solid;padding: 20px;width: 500px;}
*, *::before, *::after { box-sizing: border-box; }Mỗi phần tử là một hộp gồm nội dung, đệm (padding), viền (border) và lề (margin); với content-box, width chỉ tính phần nội dung.
Sơ đồ trên vẽ bốn lớp lồng nhau. Hãy đọc từ trong ra ngoài: nội dung được bao bởi padding, padding được bao bởi viền, viền được bao bởi margin. Màu nền (background) tô cả vùng content lẫn padding, còn margin luôn trong suốt.
2.1. Kích thước của một hộp
Để đặt chính xác chiều rộng và chiều cao của một phần tử trong mọi trình duyệt, bạn cần biết hộp được tính như thế nào. Xét ví dụ của giáo trình: một div có width: 500px, padding: 20px, border: 10px solid và margin: 20px. Chiều rộng mà hộp thực sự chiếm chỗ theo chiều ngang là:
500 px (nội dung) + 2 × 20 px (padding) + 2 × 10 px (viền) + 2 × 20 px (margin) = 600 px
Hộp chỉ có 500 px nội dung nhưng chiếm tới 600 px bề ngang! Ví dụ dưới đây tự tính lại con số đó từ các giá trị CSS đã khai báo, để bạn kiểm chứng:
<style>
body { margin: 0; }
div {
width: 500px;
padding: 20px;
margin: 20px;
border: 10px solid;
background-color: #1abc9c;
}
</style>
<div id="hop">Phần tử div</div>
<pre id="kq"></pre>
<script>
const cs = getComputedStyle(document.getElementById("hop"));
const so = (t) => parseFloat(cs[t]) || 0;
const noiDung = so("width");
const padding = so("paddingLeft") + so("paddingRight");
const vien = so("borderLeftWidth") + so("borderRightWidth");
const margin = so("marginLeft") + so("marginRight");
const tong = noiDung + padding + vien + margin;
console.log("content =", noiDung, "| padding =", padding, "| border =", vien, "| margin =", margin);
console.log("Tổng chiều ngang =", tong, "px");
document.getElementById("kq").textContent = noiDung + " + " + padding + " + " + vien + " + " + margin + " = " + tong + " px";
</script>Hãy đổi padding: 20px thành padding: 40px và chạy lại: tổng tăng thêm 40 px chứ không đứng yên. Đó chính là cách tính mặc định của CSS.
Mẹo: Muốn biết một phần tử thực sự có bao nhiêu margin, padding, border, hãy mở DevTools (F12), chọn phần tử và nhìn sơ đồ hộp ở tab Computed/Layout. Đây là cách nhanh nhất để "bắt bệnh" mọi lỗi bố cục.
2.2. Cách tính kích thước hộp (box-sizing)
Theo mặc định, width và height chỉ quy định kích thước của vùng nội dung; padding và border được cộng thêm ra ngoài. Cách tính này gọi là content-box. Vì vậy một phần tử khai báo width: 300px; padding: 20px; border: 5px solid sẽ chiếm thực tế 300 + 20×2 + 5×2 = 350 px theo chiều ngang.
Cách tính này gây khó khăn khi chia bố cục theo tỷ lệ phần trăm: hai cột 50% mà có thêm padding thì tổng vượt quá 100% và cột thứ hai bị đẩy xuống dòng. Để tránh, bạn đặt box-sizing: border-box: khi đó width đã bao gồm cả padding và border, hộp luôn rộng đúng bằng con số bạn khai báo, còn vùng nội dung tự co lại cho vừa.
Mô hình hộp: margin – border – padding – content
kéo thanh trượt, hộp lớn dần theo từng lớpwidth chỉ là vùng nội dung, đệm và viền cộng thêm ra ngoài, hộp thật rộng 360px..hop { width: 300px; height: 80px; padding: 20px; border: 10px solid #b45309; margin: 20px; box-sizing: content-box; }
Trong mô phỏng, hãy kéo thanh trượt padding và border rồi chuyển qua lại giữa hai giá trị box-sizing; để ý khi nào tổng chiều rộng thay đổi và khi nào đứng yên.
Ví dụ sau đặt hai hộp cùng khai báo width: 300px; padding: 20px; border: 5px solid nhưng khác box-sizing, rồi in chiều rộng thực:
<style>
div { width: 300px; padding: 20px; border: 5px solid #333; margin-bottom: 10px; color: white; }
.a { box-sizing: content-box; background: #e74c3c; }
.b { box-sizing: border-box; background: #2980b9; }
</style>
<div class="a">content-box (mặc định)</div>
<div class="b">border-box</div>
<script>
for (const el of document.querySelectorAll("div")) {
const cs = getComputedStyle(el);
const n = (t) => parseFloat(cs[t]);
const ngoai = n("paddingLeft") + n("paddingRight") + n("borderLeftWidth") + n("borderRightWidth");
const thuc = cs.boxSizing === "border-box" ? n("width") : n("width") + ngoai;
console.log(el.className, "→ chiều rộng thực =", thuc, "px");
}
</script>Kết quả: hộp a rộng 350 px, hộp b rộng đúng 300 px. Trong thực tế, hầu hết các dự án đặt border-box cho toàn bộ tài liệu ngay từ đầu tệp CSS. Giáo trình yêu cầu đây là quy ước áp dụng cho mọi bài thực hành trong chương:
*, *::before, *::after {
box-sizing: border-box;
}Ví dụ tiếp theo cho thấy lợi ích: hai cột 50% có padding và viền vẫn nằm vừa trong một hàng khi dùng border-box. Hãy xóa dòng box-sizing để xem cột thứ hai bị đẩy xuống dòng.
<style>
* { box-sizing: border-box; }
.hang { width: 400px; background: #ecf0f1; font-size: 0; }
.cot { display: inline-block; width: 50%; padding: 20px; border: 3px solid #16a085; background: #1abc9c; color: white; font-size: 16px; vertical-align: top; }
</style>
<div class="hang">
<div class="cot">Cột 1</div><div class="cot">Cột 2</div>
</div>
<script>
console.log("Số cột:", document.querySelectorAll(".cot").length, "- mỗi cột 50% của 400px =", 400 * 0.5, "px");
</script>Giáo trình: Công cụ tương tác "Mô hình hộp CSS" của Khoa: https://fit.neu.edu.vn/smartdoc/congcu/web-mo-hinh-hop.html — dùng để quan sát box model khi thay đổi từng thông số.
Vừa học xong mô hình hộp và box-sizing, hãy làm nhóm bài 8.1 để tập tính kích thước thực của hộp và áp dụng border-box cho cả trang.
3. Độ rộng và chiều cao
Giáo trình: Chương 4 — mục "Độ rộng và chiều cao" (Thuộc tính chiều rộng và chiều cao; Chiều rộng và chiều cao tối đa).
3.1. Thuộc tính chiều rộng và chiều cao
Các thuộc tính width và height xác định chiều rộng và chiều cao của một phần tử. Giá trị có thể là một độ dài cụ thể (300px, 20em…) hoặc một trong các từ khóa:
auto— mặc định, trình duyệt tự tính chiều cao và chiều rộng cho phù hợp.%— tính theo phần trăm của khối chứa (phần tử cha).inherit— kế thừa giá trị từ phần tử cha.
Với box-sizing mặc định là content-box, chiều cao và chiều rộng khai báo không bao gồm padding, border và margin. Ví dụ đặt width: 80% và height: 200px cho một div:
<style>
div {
width: 80%;
height: 200px;
background-color: #1abc9c;
text-align: center;
color: white;
padding: 20px;
margin: 30px;
}
</style>
<div>
<h1>Tiêu đề h1</h1>
<h2>Tiêu đề h2</h2>
<p>Đoạn văn 1</p>
<p>Đoạn văn 2</p>
</div>
<script>
const cs = getComputedStyle(document.querySelector("div"));
console.log("width:", cs.width, "| height:", cs.height, "| padding:", cs.paddingTop);
</script>Lưu ý:
width: autocủa phần tử khối nghĩa là "giãn hết khối chứa" (sau khi trừ margin, padding, border), cònwidth: 100%nghĩa là "bằng đúng 100% khối chứa" rồi cộng thêm margin, padding, border nếu làcontent-box. Đó là lý do một khốiwidth: 100%có padding hay bị tràn ngang.
3.2. Chiều rộng và chiều cao tối đa
Chiều rộng và chiều cao tối đa được thiết lập bằng max-width và max-height. Giá trị có thể là độ dài (px, cm…) hoặc phần trăm. Điểm khác biệt quan trọng so với width: nếu thay max-width: 800px bằng width: 800px thì hộp luôn rộng 800 px và trình duyệt sẽ xuất hiện thanh cuộn ngang khi cửa sổ hẹp hơn; còn với max-width, hộp tự thu nhỏ khi không đủ chỗ.
Ví dụ thứ hai trong giáo trình kết hợp cả hai: width: 80%; max-width: 500px; nghĩa là hộp chiếm 80% khối chứa nhưng không quá 500 px. Ví dụ dưới đặt hai hộp trong một khung hẹp 300 px để bạn so sánh:
<style>
.khung { width: 300px; border: 2px dashed #7f8c8d; padding: 6px; }
.co-dinh { width: 500px; height: 60px; background: #e74c3c; color: white; }
.co-gian { max-width: 500px; height: 60px; background: #27ae60; color: white; margin-top: 10px; }
</style>
<div class="khung">
<div class="co-dinh">width: 500px (tràn ra ngoài khung)</div>
<div class="co-gian">max-width: 500px (thu nhỏ vừa khung)</div>
</div>
<script>
const a = getComputedStyle(document.querySelector(".co-dinh"));
const b = getComputedStyle(document.querySelector(".co-gian"));
console.log("co-dinh: width =", a.width, "| co-gian: max-width =", b.maxWidth);
</script>Mẹo: Mẫu "ảnh co giãn" kinh điển là
img { max-width: 100%; height: auto; }: ảnh không bao giờ rộng hơn khung chứa, nhưng cũng không bị phóng to quá kích thước gốc. Cùng vớimax-widthcòn cómin-widthvàmin-heightđể đặt giới hạn dưới.
4. Đơn vị kích thước
Giáo trình: Chương 4 — mục "Đơn vị kích thước" (Các loại đơn vị đo kích thước; Đơn vị tuyệt đối; Đơn vị tương đối).
4.1. Các loại đơn vị đo kích thước
Rất nhiều thuộc tính như margin, padding, font-size, border, width… cần giá trị kích thước như 10px hay 2em. Có hai loại đơn vị: đơn vị tuyệt đối và đơn vị tương đối. Hai quy tắc cần nhớ: (1) khi viết kích thước phải kèm đơn vị, trừ số 0 thì không cần; (2) với một số thuộc tính CSS (như margin), giá trị âm cũng hợp lệ.
1 in = 96 px = 2.54 cm1 pt = 1/72 in1 pc = 12 ptcm · mmhtml { font-size: 16px → 26px }px là đơn vị tuyệt đối; các đơn vị còn lại đều tương đối nên giúp bố cục co giãn theo màn hình và cỡ chữ. Trong hình, khung nhìn được mô phỏng bằng khung co giãn.
Sơ đồ phân nhóm các đơn vị: bên trái là nhóm tuyệt đối (độ dài cố định), bên phải là nhóm tương đối (phụ thuộc phông chữ, khung nhìn hoặc phần tử cha). Hãy dùng nó làm "bảng tra" khi chọn đơn vị.
4.2. Đơn vị tuyệt đối
Đơn vị tuyệt đối chỉ một độ dài cố định:
| Đơn vị | Tên | Quy đổi |
|---|---|---|
cm |
centimeters | 1 in = 2,54 cm |
mm |
millimeters | 10 mm = 1 cm |
in |
inches | 1 in = 96 px = 2,54 cm |
px |
pixels | 1 px = 1/96 in |
pt |
points | 1 pt = 1/72 in |
pc |
picas | 1 pc = 12 pt |
px (pixel) phụ thuộc thiết bị hiển thị: với màn hình độ phân giải thấp, 1 px tương đương một điểm ảnh. Điều cần chú ý là, trừ thiết bị in cho kết quả chính xác, trên màn hình thì kích thước theo cm, mm, in, pt, pc chỉ là gần đúng; khi chiếu lên máy chiếu, kích thước thật sẽ được phóng to rất nhiều. Ví dụ đặt độ dài 20cm chưa chắc ra đúng 20 cm trên màn hình máy tính của bạn.
// Quy đổi các đơn vị tuyệt đối về px theo giáo trình: 1in = 96px, 1pt = 1/72in, 1pc = 12pt
const px = { in: 96, cm: 96 / 2.54, mm: 96 / 25.4, pt: 96 / 72, pc: 96 / 72 * 12 };
for (const [don_vi, giaTri] of Object.entries(px)) {
console.log("1" + don_vi, "=", giaTri.toFixed(2), "px");
}
console.log("20cm =", (20 * px.cm).toFixed(1), "px (gần đúng trên màn hình)");4.3. Đơn vị tương đối
Đơn vị tương đối được dùng nhiều trong thiết kế Web vì chúng co giãn theo ngữ cảnh:
| Đơn vị | Tương đối so với |
|---|---|
em |
cỡ font hiện tại |
ex |
x-height của font hiện tại |
ch |
độ rộng của chữ số 0 |
rem |
cỡ font của phần tử html (gốc) |
vw |
1% chiều rộng khung nhìn (viewport) |
vh |
1% chiều cao khung nhìn |
vmin |
1% cạnh nhỏ hơn của khung nhìn |
vmax |
1% cạnh lớn hơn của khung nhìn |
% |
phần tử cha |
Giáo trình lưu ý: em và rem thường được dùng để thiết kế bố cục dễ co giãn; ex và ch rất ít dùng. Ví dụ h1 { font-size: 3em; } và h2 { font-size: 1em; }: tiêu đề h2 có cỡ chữ bằng đúng cỡ chữ bình thường của đoạn văn, còn h1 gấp ba lần. Ví dụ dưới in cách trình duyệt tính các giá trị đó với cỡ chữ gốc 16 px:
const goc = 16; // cỡ chữ gốc của trình duyệt (px)
console.log("h1 = 3em →", 3 * goc, "px");
console.log("h2 = 1em →", 1 * goc, "px");
// em cộng dồn qua các lớp lồng nhau, rem thì luôn tính từ gốc
let cha = goc;
for (let lop = 1; lop <= 3; lop++) {
cha = cha * 1.5; // mỗi lớp khai báo font-size: 1.5em
console.log("lớp", lop, "dùng 1.5em →", cha, "px | cùng cấp dùng 1.5rem →", 1.5 * goc, "px");
}
// vw / vh với khung nhìn giả định 1280 x 720
const W = 1280, H = 720;
console.log("50vw =", 0.5 * W, "px | 50vh =", 0.5 * H, "px | 10vmin =", 0.1 * Math.min(W, H), "px | 10vmax =", 0.1 * Math.max(W, H), "px");Điều cần rút ra từ các con số trên: đơn vị độ dài tương đối nên được ưu tiên khi thiết kế Web hoạt động trên nhiều loại thiết bị.
Ví dụ trực quan dưới đây dùng vw, vh, %, rem cho các hộp; hãy thay đổi kích thước khung xem trước (hoặc xem bài giảng trên màn hình khác) để thấy hộp co giãn:
<style>
html { font-size: 16px; }
.vw { width: 50vw; height: 40px; background: #3498db; }
.pc { width: 50%; height: 40px; background: #2ecc71; margin-top: 8px; }
.rem { width: 20rem; max-width: 100%; height: 40px; background: #e67e22; margin-top: 8px; }
.vh { width: 30%; height: 20vh; background: #9b59b6; margin-top: 8px; }
div { color: white; font: 14px sans-serif; }
</style>
<div class="vw">width: 50vw</div>
<div class="pc">width: 50%</div>
<div class="rem">width: 20rem (= 320px, tối đa 100%)</div>
<div class="vh">height: 20vh</div>
<script>
console.log("Rộng bằng 50vw:", getComputedStyle(document.querySelector(".vw")).width);
</script>Vừa học xong kích thước và đơn vị, hãy làm nhóm bài 8.2 để luyện chọn %, rem, vh, min() và các giới hạn max-width.
5. Khoảng cách lề
Giáo trình: Chương 4 — mục "Khoảng cách lề" (Thuộc tính margin; Cách viết rút gọn; Giá trị auto; Giá trị inherit; Gom lề).
5.1. Thuộc tính margin
Các thuộc tính margin (lề) tạo không gian xung quanh phần tử, bên ngoài đường viền. Có bốn thuộc tính cho bốn phía: margin-top (trên), margin-right (phải), margin-bottom (dưới), margin-left (trái). Mỗi thuộc tính nhận một trong các giá trị:
auto— trình duyệt tự tính lề;- giá trị — độ dài
px,pt,cm…; %— tính theo chiều rộng của phần tử chứa (container);inherit— kế thừa lề của phần tử cha.
Các trình duyệt tuân theo tiêu chuẩn của W3C để trang hiển thị nhất quán. Một phần của tiêu chuẩn đó là một số phần tử như body có margin mặc định để nội dung không dính sát mép cửa sổ: phần tử body mặc định có margin 8 px. Muốn bỏ lề này, khai báo body { margin: 0px; }.
<style>
body { margin: 0px; }
.hop { background: #1abc9c; color: white; padding: 10px; margin-top: 12px; margin-left: 24px; }
</style>
<div class="hop">body đã bỏ margin 8px mặc định; hộp này chỉ cách mép trái 24px nhờ margin-left</div>5.2. Cách viết rút gọn
Có thể gom bốn lề vào một thuộc tính margin duy nhất. Số giá trị quyết định cách phân bổ, và thứ tự luôn theo chiều kim đồng hồ bắt đầu từ trên: trên → phải → dưới → trái (nhớ bằng từ "TRouBLe").
| Cách viết | Ý nghĩa | Ví dụ của giáo trình |
|---|---|---|
margin: 25px 50px 75px 100px |
trên 25, phải 50, dưới 75, trái 100 | bốn giá trị |
margin: 25px 50px |
trên/dưới 25, trái/phải 50 | hai giá trị |
margin: 45px |
cả bốn phía 45 | một giá trị |
margin: 10px 20px 30px |
trên 10, trái/phải 20, dưới 30 | ba giá trị (bổ sung) |
Ví dụ sau viết một hàm "bung" cách viết rút gọn thành bốn lề — cũng là cách trình duyệt hiểu chuỗi bạn gõ. Hãy thử thêm các chuỗi khác:
function bung(chuoi) {
const v = chuoi.trim().split(/\s+/);
const [t, p = t, d = t, tr = p] = v; // thiếu giá trị thì lấy theo phía đối diện
return { top: t, right: p, bottom: d, left: tr };
}
for (const s of ["25px 50px 75px 100px", "25px 50px", "45px", "10px 20px 30px"]) {
console.log(s.padEnd(22), "→", JSON.stringify(bung(s)));
}Mẹo: Dùng DevTools của trình duyệt (ví dụ Chrome) để xem chính xác margin, padding, kích thước của một phần tử: các thông tin này được tô màu rõ ràng trong sơ đồ hộp.
5.3. Giá trị auto
Đặt lề là auto để căn giữa một phần tử trong khung chứa. Khi một phần tử khối có width xác định (ví dụ 300px) và margin: auto, trình duyệt lấy phần không gian còn lại chia đều cho lề trái và lề phải.
<style>
div { width: 300px; background-color: #1abc9c; text-align: center; color: white; padding: 20px; margin: auto; }
</style>
<div>Căn giữa bằng margin: auto</div>
<script>
const cs = getComputedStyle(document.querySelector("div"));
console.log("margin-left:", cs.marginLeft, "| margin-right:", cs.marginRight, "| width:", cs.width);
</script>Hai điều kiện cần nhớ: phần tử phải có width nhỏ hơn khối chứa (nếu không có chỗ trống để chia thì không thấy gì), và phải là phần tử khối.
5.4. Giá trị inherit
Giá trị kế thừa inherit quy định margin của phần tử giống với margin của phần tử cha (phần tử chứa nó). Trong ví dụ giáo trình, div có margin: 30px và thẻ p bên trong khai báo margin: inherit, nên p cũng có lề 30 px:
<style>
div { width: 300px; background-color: #1abc9c; text-align: center; color: white; padding: 20px; margin: 30px; }
p { margin: inherit; border: 1px solid black; }
</style>
<div>Phần tử div (margin 30px)<p>Đoạn p kế thừa margin</p></div>5.5. Gom lề
Lề trên và dưới của các phần tử đôi khi bị thu gọn thành một lề duy nhất, bằng lề lớn nhất trong hai lề. Hiện tượng này gọi là gom lề (margin collapsing). Giả sử h1 có lề dưới 50 px và h2 ngay sau có lề trên 30 px: thông thường bạn sẽ nghĩ khoảng cách là 50 + 30 = 80 px, nhưng do gom lề, khoảng cách thực tế chỉ là 50 px. Gom lề chỉ xảy ra theo chiều dọc; lề trái và phải thì không bao giờ gom.
<style>
body { margin: 0; }
h1 { margin: 0 0 50px 0; background: #f9c74f; }
h2 { margin: 30px 0 0 0; background: #90be6d; }
</style>
<h1>Tiêu đề h1 (lề dưới 50px)</h1>
<h2>Tiêu đề h2 (lề trên 30px)</h2>
<script>
const a = parseFloat(getComputedStyle(document.querySelector("h1")).marginBottom);
const b = parseFloat(getComputedStyle(document.querySelector("h2")).marginTop);
console.log("Nếu cộng dồn:", a + b, "px");
console.log("Thực tế (gom lề, lấy lớn nhất):", Math.max(a, b), "px");
</script>Cảnh báo: Bản giáo trình in có đoạn chú thích số liệu của ví dụ này nhắc "20 px" và "70 px" trong khi mã dùng 30 px; hãy luôn tính theo mã thực tế (50 và 30 → khoảng cách thực là 50 px). Điều cần nhớ là quy tắc: khoảng cách = lề lớn hơn, không phải tổng.
Mở rộng: Gom lề còn xảy ra giữa phần tử cha và con đầu tiên/cuối cùng khi cha không có padding hay border. Nếu hiện tượng này làm bạn bất ngờ, thêm
paddinghoặcbordercho cha, hoặc dùngdisplay: flow-root.
6. Khoảng cách đệm
Giáo trình: Chương 4 — mục "Khoảng cách đệm" (Thuộc tính padding; Cách viết rút gọn).
6.1. Thuộc tính padding
Thuộc tính padding (đệm) tạo không gian xung quanh nội dung của phần tử, bên trong đường viền, gồm bốn thuộc tính: padding-top, padding-right, padding-bottom, padding-left. Các giá trị hợp lệ:
- giá trị — độ dài
px,pt,cm…; %— tính theo chiều rộng của phần tử chứa (container);inherit— kế thừa từ phần tử cha.
Khác với margin, padding không nhận giá trị âm và không có auto; mặt khác nền (background) của phần tử tô cả vùng padding. Ví dụ của giáo trình: một div có viền xanh và bốn padding khác nhau:
<style>
div {
border: 1px solid #4CAF50;
padding-top: 100px;
padding-bottom: 100px;
padding-right: 150px;
padding-left: 80px;
}
</style>
<div id="d">Phần tử Div</div>6.2. Cách viết rút gọn
Giống margin, padding có cách viết rút gọn theo thứ tự trên → phải → dưới → trái. Padding 25px 50px 75px 100px nghĩa là trên 25, phải 50, dưới 75, trái 100 (px); padding: 25px 50px nghĩa là trên/dưới 25 và trái/phải 50; padding: 45px nghĩa là cả bốn phía đều 45 px.
<style>
div { border: 1px solid #4CAF50; margin-bottom: 8px; background: #e8f5e9; }
.a { padding: 25px 50px 75px 100px; }
.b { padding: 25px 50px; }
.c { padding: 45px; }
</style>
<div class="a">padding: 25px 50px 75px 100px</div>
<div class="b">padding: 25px 50px</div>
<div class="c">padding: 45px</div>Lưu ý: Phân biệt nhanh — margin đẩy các hộp ra xa nhau (ngoài viền, trong suốt, có thể âm và
auto, có gom lề); padding đẩy nội dung ra xa viền (trong viền, có màu nền, không âm, không gom lề).
Vừa học xong margin và padding, hãy làm nhóm bài 8.3 để luyện viết rút gọn bốn phía và tạo khoảng cách giữa các hộp.
7. Thuộc tính hiển thị (display)
Giáo trình: Chương 4 — mục "Thuộc tính hiển thị (display)".
Thuộc tính display xác định loại hiển thị của phần tử. Các giá trị thường gặp:
none— làm ẩn phần tử nhưng không xóa phần tử này khỏi tài liệu (phần tử không chiếm chỗ).inline— hiển thị trên một dòng; chiều rộng của thẻ phụ thuộc vào nội dung bên trong (phần tử nội tuyến).block— luôn bắt đầu trên một dòng mới và có chiều rộng bằng 100% chiều rộng khối chứa (phần tử khối).inline-block— kết hợp cả hai: phần tử nằm trên cùng một hàng nhưng vẫn thừa hưởng đặc tính của block, ví dụ có thể thiết lập chiều cao, chiều rộng.
display: blockPhần tử khối- ✓Xuống dòng mới
- ✓Đặt được width / height
- ✓Chiếm chỗ trong bố cục
display: inlinePhần tử nội tuyến- ✗Xuống dòng mới
- ✗Đặt được width / height
- ✓Chiếm chỗ trong bố cục
display: inline-blockNội tuyến dạng khối- ✗Xuống dòng mới
- ✓Đặt được width / height
- ✓Chiếm chỗ trong bố cục
display: noneẨn phần tử- —Xuống dòng mới
- —Đặt được width / height
- ✗Chiếm chỗ trong bố cục
li là list-item: xếp dọc- HTML
- CSS
- JavaScript
li { display: inline-block }: thành thanh menu ngang- HTML
- CSS
- JavaScript
display quyết định phần tử xuống dòng hay nằm cùng hàng, có đặt được kích thước hay không, và có còn tham gia bố cục hay không.
Sơ đồ đặt bốn kiểu cạnh nhau: block xuống dòng và giãn hết bề ngang, inline nối tiếp theo dòng chữ, inline-block nối tiếp nhưng có kích thước riêng, none biến mất hoàn toàn.
Một số thẻ HTML như div, p, h1 luôn bắt đầu bằng dòng mới và trải dài 100% khối chứa, trong khi các thẻ như span, a hoặc img thường nối tiếp nhau trên một dòng. Đó là vì mỗi thẻ có kiểu display mặc định (khối hoặc nội tuyến), và thuộc tính display cho phép bạn thay đổi mặc định ấy. Bạn có thể kiểm chứng kiểu mặc định của một số thẻ:
for (const thẻ of ["div", "p", "h1", "span", "a", "strong", "li", "img"]) {
const el = document.createElement(thẻ);
document.body.appendChild(el);
console.log(thẻ.padEnd(7), "→", getComputedStyle(el).display);
}Ví dụ của giáo trình: danh sách ul có ba mục li mặc định display: list-item (mỗi mục một dòng). Đổi thành inline thì các mục nối tiếp nhau thành một hàng — nhưng khi đó không đặt được chiều rộng, chiều cao cho li. Muốn vừa nằm ngang vừa đặt được kích thước thì dùng inline-block. Hãy lần lượt đổi giá trị display của li trong ví dụ:
<style>
ul { background-color: #1abc9c; list-style-type: none; padding: 8px; margin: 0 0 8px 0; }
li { color: white; }
.a li { display: list-item; }
.b li { display: inline; width: 120px; height: 50px; }
.c li { display: inline-block; width: 120px; height: 50px; background: #16a085; text-align: center; }
</style>
<ul class="a"><li>HTML</li><li>CSS</li><li>JavaScript</li></ul>
<ul class="b"><li>HTML</li><li>CSS</li><li>JavaScript</li></ul>
<ul class="c"><li>HTML</li><li>CSS</li><li>JavaScript</li></ul>
<script>
for (const ul of document.querySelectorAll("ul"))
console.log(ul.className, "→ display của li:", getComputedStyle(ul.firstElementChild).display);
</script>Quan sát: ở hàng b (inline), khai báo width và height bị bỏ qua; ở hàng c (inline-block) chúng có hiệu lực. Ngoài none, block, inline, inline-block, thuộc tính display còn nhận các giá trị flex và grid để tạo bố cục linh hoạt — nội dung của buổi 9.
Mẹo:
display: nonekhácvisibility: hidden:nonelàm phần tử biến mất và không chiếm chỗ, cònvisibility: hiddenchỉ ẩn nhìn thấy nhưng vẫn giữ nguyên chỗ trống.
8. Căn chỉnh
Giáo trình: Chương 4 — mục "Căn chỉnh" (Căn giữa phần tử; Căn chỉnh văn bản; Căn trái, căn phải cho phần tử; Căn giữa theo chiều dọc).
Căn chỉnh (alignment) là việc thiết lập vị trí của phần tử hoặc của nội dung bên trong phần tử. Mặc định, nếu không căn chỉnh gì, mọi thứ đều căn về phía bên trái.
8.1. Căn giữa phần tử
Để căn giữa một phần tử khối (như div), dùng margin với giá trị auto: phần tử chiếm một không gian nhất định, khoảng trống còn lại chia đều cho hai bên lề. Giáo trình ghi lại các lưu ý:
- Căn giữa sẽ không có tác dụng nếu không có
width(hoặcwidthbằng 100%). margin: autochỉ căn giữa cho phần tử block nhưdiv,p,h1–h6,header,footer,section,nav…; phần tử inline nhưspan,a,strong,b,i… không áp dụng được.- Có thể ghi đè kiểu hiển thị bằng
display, ví dụspan { display: block; }. - Để căn giữa một ảnh, đặt
marginthànhautovà làm ảnh thành phần tử dạngblock.
<style>
div { width: 320px; padding: 20px; background-color: #1abc9c; color: white; margin: auto; }
span.nhan { width: 320px; margin: auto; background: #e74c3c; color: white; }
img { display: block; width: 120px; height: 40px; margin: 10px auto; background: #f39c12; }
</style>
<div>div có width: 320px và margin: auto → nằm giữa</div>
<br>
<span class="nhan">span (inline) bỏ qua width và margin: auto</span>
<img alt="Ảnh minh họa căn giữa" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
<script>
console.log("div:", getComputedStyle(document.querySelector("div")).marginLeft);
console.log("span display:", getComputedStyle(document.querySelector("span")).display);
console.log("img display:", getComputedStyle(document.querySelector("img")).display);
</script>8.2. Căn chỉnh văn bản
Thuộc tính text-align căn chỉnh văn bản theo chiều ngang trong phần tử, với bốn giá trị:
center— căn giữa;left— căn trái;right— căn phải;justify— căn hai bên (thêm khoảng trống giữa các từ để các dòng cùng độ dài).
text-align cũng chỉ áp dụng cho phần tử block (căn các dòng chữ bên trong hộp, không di chuyển chính hộp). Văn bản căn đều (justify) không áp dụng cho dòng cuối cùng của đoạn văn; dòng này căn trái, phải hoặc giữa tùy ngữ cảnh. Với ngôn ngữ viết từ phải sang trái (RTL – Right To Left) như tiếng Ả Rập hay tiếng Hebrew, cần thiết lập thuộc tính dir trong HTML hoặc dùng CSS để text-align hoạt động đúng.
<style>
div { width: 300px; background-color: #1abc9c; color: white; padding: 10px; margin: 6px 0; }
.c { text-align: center; } .l { text-align: left; } .r { text-align: right; } .j { text-align: justify; }
</style>
<div class="c">center — căn giữa</div>
<div class="l">left — căn trái</div>
<div class="r">right — căn phải</div>
<div class="j">justify — căn hai bên: văn bản dài hơn một dòng sẽ được dàn đều hai mép, trừ dòng cuối cùng của đoạn.</div>8.3. Căn trái, căn phải cho phần tử
Để căn trái hoặc phải một phần tử có hai cách:
- Dùng
floatcho phần tử khối: căn trái làfloat: left;, căn phải làfloat: right;. - Dùng
margin: căn trái làmargin-left: 0; margin-right: auto;, căn phải làmargin-left: auto; margin-right: 0;.
<style>
.hop { width: 120px; padding: 10px; color: white; text-align: center; margin-bottom: 8px; }
.t { background: #3498db; margin-left: 0; margin-right: auto; }
.p { background: #e67e22; margin-left: auto; margin-right: 0; }
.f { background: #9b59b6; float: right; }
.xoa { clear: both; }
</style>
<div class="hop t">margin trái</div>
<div class="hop p">margin phải</div>
<div class="hop f">float: right</div>
<div class="xoa"></div>
<script>
console.log("t:", getComputedStyle(document.querySelector(".t")).marginRight);
console.log("p:", getComputedStyle(document.querySelector(".p")).marginLeft);
console.log("f float:", getComputedStyle(document.querySelector(".f")).float);
</script>8.4. Căn giữa theo chiều dọc
Có nhiều cách căn giữa theo chiều dọc. Giải pháp đơn giản nhất là dùng padding bằng nhau ở trên và dưới (nội dung tự nằm giữa). Cách khác là dùng line-height bằng giá trị của height (chỉ hợp với một dòng chữ). Cách thứ ba kết hợp position và transform: đặt cha position: relative, con position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);. Phép translate(-50%, -50%) kéo phần tử lùi lại một nửa kích thước của chính nó nên tâm phần tử trùng tâm khối cha.
<style>
* { box-sizing: border-box; }
.a, .b, .c { width: 320px; background-color: #1abc9c; color: white; margin-bottom: 8px; }
.a { padding: 40px 0; text-align: center; }
.b { height: 80px; line-height: 80px; text-align: center; }
.c { position: relative; height: 120px; }
.c h1 { position: absolute; top: 50%; left: 50%; margin: 0; font-size: 18px; transform: translate(-50%, -50%); }
</style>
<div class="a">padding trên = dưới</div>
<div class="b">line-height = height</div>
<div class="c"><h1>position + transform</h1></div>
<script>
console.log("b line-height:", getComputedStyle(document.querySelector(".b")).lineHeight);
console.log("c h1 transform:", getComputedStyle(document.querySelector(".c h1")).transform);
</script>Lưu ý: Căn giữa dọc bằng
paddinghayline-heightchỉ phù hợp nội dung ngắn, một dòng, chiều cao biết trước. Với nội dung bất kỳ, buổi 9 sẽ giới thiệu cách gọn hơn nhiều bằng Flexbox và Grid (align-items: center).
Vừa học xong display và căn chỉnh, hãy làm nhóm bài 8.4 để luyện căn giữa bằng margin: auto, đẩy phần tử sang một phía và căn tuyệt đối.
9. Thiết lập vị trí
Giáo trình: Chương 4 — mục "Thiết lập vị trí" (static, relative, fixed, absolute, sticky; Thuộc tính z-index).
Thuộc tính position xác định phương pháp định vị áp dụng cho một phần tử, với năm giá trị: static, relative, fixed, absolute, sticky. Các giá trị khác static kết hợp với bốn thuộc tính top, right, bottom, left để dịch chuyển phần tử.
position: staticposition: relativeposition: absoluteposition: fixedposition: stickyMọi phần tử mặc định là static. Các giá trị còn lại cho phép dùng top, right, bottom, left; điểm khác nhau là phần tử có giữ chỗ không và vị trí được tính so với đâu.
Sơ đồ so sánh năm kiểu định vị: mỗi kiểu có một "mốc" riêng để tính vị trí (chính nó, khối cha, cửa sổ trình duyệt) và quyết định phần tử còn chiếm chỗ trong dòng chảy bình thường hay không.
Để hiểu rõ, giáo trình dùng một tình huống thống nhất: hai div chồng dọc, div1 màu hồng và div2 màu xanh ngọc, cùng rộng 200 px, cao 100 px, body bỏ margin.
9.1. Vị trí tĩnh (static)
static là giá trị mặc định. Với giá trị này, vị trí phần tử không bị ảnh hưởng bởi left, right, bottom, top; phần tử nằm đúng chỗ của nó trong dòng chảy tài liệu.
9.2. Vị trí phụ thuộc tương đối (relative)
Với relative, vị trí phần tử được tính tương đối so với vị trí ban đầu của chính nó. Phần tử bị dịch đi nhưng chỗ cũ vẫn được giữ lại nên các phần tử khác không xê dịch. Ví dụ div1 dịch xuống 50 px và sang phải 50 px:
<style>
body { background-color: #f1f1f1; margin: 0px; }
div { width: 200px; height: 100px; color: white; }
div.div1 { background-color: #ed25a4; position: relative; top: 50px; left: 50px; }
div.div2 { background-color: #1abc9c; }
</style>
<div class="div1"><h1>Div 1</h1></div>
<div class="div2"><h1>Div 2</h1></div>
<script>
const s = getComputedStyle(document.querySelector(".div1"));
console.log("div1:", s.position, "top =", s.top, "left =", s.left);
console.log("div2:", getComputedStyle(document.querySelector(".div2")).position);
</script>9.3. Vị trí cố định theo cửa sổ trình duyệt (fixed)
Với fixed, phần tử được định vị theo khung nhìn (viewport) của cửa sổ trình duyệt và đứng yên khi cuộn trang. Phần tử fixed bị rút khỏi dòng chảy nên không chiếm chỗ, khiến div2 bị đẩy lên phía trên. Ứng dụng quen thuộc: thanh điều hướng cố định, nút "Về đầu trang", banner cookie.
<style>
body { background-color: #f1f1f1; margin: 0px; }
div { width: 200px; height: 100px; color: white; }
div.div1 { background-color: #ed25a4; position: fixed; top: 50px; left: 50px; }
div.div2 { background-color: #1abc9c; }
</style>
<div class="div1"><h1>Div 1</h1></div>
<div class="div2"><h1>Div 2</h1></div>
<script>
console.log("div1 position:", getComputedStyle(document.querySelector(".div1")).position);
console.log("div1 không chiếm chỗ → div2 bị đẩy lên sát đầu trang");
</script>9.4. Vị trí tuyệt đối (absolute)
Với absolute, vị trí được tính theo tổ tiên gần nhất có position khác static; nếu không có tổ tiên nào như vậy thì tính theo khối chứa ban đầu của tài liệu. Phần tử absolute cũng bị rút khỏi dòng chảy. Trong ví dụ, div1 nằm trong phần tử parent có position: relative, nên top: 50px; left: 50px được đo từ góc trên-trái của parent — nếu parent dịch chuyển thì div1 đi theo.
<style>
body { margin: 0px; }
div.parent { width: 260px; height: 200px; background-color: #ed8625; position: relative; top: 40px; left: 80px; }
div.div1 { width: 120px; height: 70px; background-color: #ed25a4; color: white; position: absolute; top: 50px; left: 50px; }
</style>
<div class="parent">parent (relative)
<div class="div1">div1 (absolute)</div>
</div>
<script>
const p = getComputedStyle(document.querySelector(".parent"));
const c = getComputedStyle(document.querySelector(".div1"));
console.log("parent:", p.position, "| div1:", c.position, "top/left =", c.top, c.left);
</script>Mẹo: Quy tắc vàng: muốn một phần tử
absolutebám theo hộp nào, hãy đặtposition: relative(không cầntop/left) cho hộp đó. Đây là mẫu dùng cho huy hiệu ở góc thẻ, nhãn "Mới", nút đóng của hộp thoại.
9.5. Vị trí cố định khi cuộn (sticky)
sticky kết hợp đặc điểm của relative và fixed. Khi phần tử còn trong ngưỡng cuộn thì nó hoạt động như relative; khi cuộn đến ngưỡng (ví dụ top: 0) thì nó "dính" vào vị trí đã xác định và hoạt động như fixed. Nhờ vậy tiêu đề, mục lục, thanh công cụ luôn nằm trong tầm nhìn khi người dùng cuộn.
<style>
body { margin: 0; }
.cuon { height: 220px; overflow: auto; background: #f1f1f1; }
div.div1 { background-color: #ed25a4; color: white; position: sticky; top: 0; padding: 6px; }
div.div2 { background-color: #1abc9c; width: 200px; height: 1000px; }
</style>
<div class="cuon">
<div class="div1">Tiêu đề sticky (cuộn thử)</div>
<div class="div2">Nội dung dài 1000px</div>
</div>
<script>
console.log("div1:", getComputedStyle(document.querySelector(".div1")).position);
</script>Cảnh báo: Phần tử cha của phần tử
stickykhông được cóoverflow: hidden,overflow: scrollhoặcoverflow: auto(kể cả các tổ tiên xa hơn). Điều đó có thể khiếnstickykhông hoạt động như mong đợi — trong ví dụ trên, khung.cuonchính là vùng cuộn nênstickydính vào đỉnh khung này, không phải đỉnh cửa sổ.
9.6. Thuộc tính z-index
Khi các phần tử chồng lên nhau, thuộc tính z-index quyết định thứ tự hiển thị theo chiều sâu (trục z vuông góc với màn hình): phần tử có z-index lớn hơn nằm phía trên, nhỏ hơn nằm phía dưới. z-index chỉ có tác dụng với phần tử có position khác static (absolute, relative, fixed, sticky).
Lưu ý: Đoạn mã minh họa ở giáo trình chỉ liệt kê
position,top,leftcủadiv1mà chưa ghi giá trịz-indexcụ thể. Ở ví dụ dưới, bạn tự thêmz-indexđể thấy hiệu ứng: hộp cóz-indexnhỏ hơn nằm dưới. Hãy đổi các con số (ví dụ-1,5,10) và quan sát.
<style>
body { margin: 0; }
div.div2 { background-color: #1abc9c; width: 200px; height: 120px; position: relative; z-index: 2; color: white; }
div.div1 { background-color: #ed25a4; width: 160px; height: 100px; position: absolute; top: 40px; left: 80px; z-index: 1; color: white; }
</style>
<div class="div2">div2 — z-index: 2</div>
<div class="div1">div1 — z-index: 1 (nằm dưới div2)</div>
<script>
const z = (s) => getComputedStyle(document.querySelector(s)).zIndex;
console.log("z-index div1 =", z(".div1"), "| z-index div2 =", z(".div2"));
console.log("Phần tử z-index lớn hơn nằm trên:", Number(z(".div2")) > Number(z(".div1")) ? "div2" : "div1");
</script>Vị trí: static, relative, absolute, fixed, sticky
đổi kiểu định vị, kéo top/left rồi cuộn khung<body> (cuộn khung này ↓)
— hết trang —
.div1 { position: relative; top: 30px; left: 40px; } .div2 { position: relative; z-index: 1; }
Trong mô phỏng, đổi lần lượt các giá trị position và giá trị top/left để xem phần tử có rời khỏi dòng chảy hay không, và mốc tính vị trí là gì.
Giáo trình: Công cụ "Position trực quan" của Khoa: https://fit.neu.edu.vn/smartdoc/congcu/web-position-truc-quan.html.
| Giá trị | Mốc tính vị trí | Còn chiếm chỗ? | Thường dùng cho |
|---|---|---|---|
static |
không dùng top/left… |
có | mặc định |
relative |
vị trí ban đầu của chính nó | có (giữ chỗ cũ) | dịch nhẹ, làm "mốc" cho con absolute |
absolute |
tổ tiên gần nhất khác static |
không | huy hiệu, nhãn, lớp phủ |
fixed |
khung nhìn (viewport) | không | thanh điều hướng cố định, nút nổi |
sticky |
ngưỡng cuộn trong khối cuộn gần nhất | có | tiêu đề/thanh công cụ dính khi cuộn |
Vừa học xong position và z-index, hãy làm nhóm bài 8.5 để luyện dịch chuyển, định vị góc, dính khi cuộn và xếp chồng.
10. Thuộc tính tràn (overflow)
Giáo trình: Chương 4 — mục "Thuộc tính tràn (overflow)".
Thuộc tính overflow xác định điều gì xảy ra khi nội dung tràn ra khỏi phần tử (nội dung dài hơn chiều cao/chiều rộng của hộp). Có bốn giá trị:
visible(mặc định) — nội dung không bị cắt, cứ tràn ra ngoài hộp;hidden— nội dung tràn bị ẩn;scroll— nội dung tràn bị ẩn nhưng xuất hiện thanh cuộn để xem phần bị ẩn (thanh cuộn luôn hiện);auto— nhưscrollnhưng thanh cuộn chỉ xuất hiện khi cần.
Ngoài ra có overflow-x và overflow-y để điều khiển riêng chiều ngang và chiều dọc; ví dụ khi tràn theo chiều x thì overflow-x: hidden, tràn theo chiều y thì overflow-y: scroll. Ví dụ dưới có bốn hộp cùng kích thước và cùng một đoạn văn dài, chỉ khác overflow:
<style>
.luoi { display: flex; flex-wrap: wrap; gap: 12px; }
.o { width: 200px; height: 90px; background-color: #1abc9c; color: #fff9c4; padding: 4px; font: 13px sans-serif; }
.v { overflow: visible; } .h { overflow: hidden; } .s { overflow: scroll; } .a { overflow: auto; }
</style>
<div class="luoi">
<div class="o v">visible: Chào mừng bạn đến với trang Web của chúng tôi! Chúng tôi rất vui mừng được chia sẻ với bạn những thông tin và dịch vụ tuyệt vời mà chúng tôi cung cấp.</div>
<div class="o h">hidden: Chào mừng bạn đến với trang Web của chúng tôi! Chúng tôi rất vui mừng được chia sẻ với bạn những thông tin và dịch vụ tuyệt vời mà chúng tôi cung cấp.</div>
<div class="o s">scroll: Chào mừng bạn đến với trang Web của chúng tôi! Chúng tôi rất vui mừng được chia sẻ với bạn những thông tin và dịch vụ tuyệt vời mà chúng tôi cung cấp.</div>
<div class="o a">auto: Chào mừng bạn đến với trang Web của chúng tôi! Chúng tôi rất vui mừng được chia sẻ với bạn những thông tin và dịch vụ tuyệt vời mà chúng tôi cung cấp.</div>
</div>
<script>
for (const el of document.querySelectorAll(".o")) console.log(el.className.split(" ")[1], "→ overflow:", getComputedStyle(el).overflow);
</script>Ví dụ thực tế: Hộp bình luận trên một trang tin: đặt
height: 300px; overflow: auto;để danh sách dài cuộn bên trong hộp thay vì kéo dài cả trang. Cònoverflow: hiddenthường dùng để cắt ảnh theo khung bo tròn (kết hợpborder-radius).
Vừa học xong overflow, hãy làm nhóm bài 8.6 để luyện hộp cuộn và tiêu đề dính trong khung cuộn.
11. Thuộc tính trôi (float)
Giáo trình: Chương 4 — mục "Thuộc tính trôi (float)" (kể cả thuộc tính
clear).
Thuộc tính float xác định một phần tử có trôi về phía trái hay phải hay không, nhờ đó các phần tử nằm cạnh nhau thay vì trên dưới nhau. Mặc định cả div lẫn p đều là phần tử khối nên đều chiếm toàn bộ chiều rộng của khối chứa. Khi dùng float, phần tử không chiếm toàn bộ chiều rộng nữa: nó "trôi" sang trái hoặc phải và các phần tử khác chảy xung quanh nó (như ảnh minh họa trong một bài báo, chữ bao quanh ảnh).
float: left— phần tử trôi về phía bên trái;float: right— phần tử trôi về phía bên phải;- (mặc định
float: none— không trôi).
Ví dụ: một khối xanh rộng 240 px, cao 140 px trôi trái, margin-right: 20px để chữ không dính sát. Đổi thành float: right (và margin-left: 20px) để thấy khối chuyển sang phải:
<style>
body { background: #f0f0f0; margin: 8px; font-family: sans-serif; }
div { background-color: #1abc9c; width: 240px; height: 140px; color: white; float: left; margin-right: 20px; }
</style>
<div><h1>Div 1</h1></div>
<p>Chào mừng bạn đến với trang Web của chúng tôi! Chúng tôi rất vui mừng được chia sẻ với bạn những thông tin và dịch vụ tuyệt vời mà chúng tôi cung cấp. Với sự cam kết với chất lượng và sự tận tâm đến khách hàng, chúng tôi hy vọng rằng trang Web này sẽ mang lại cho bạn trải nghiệm tuyệt vời và đáng nhớ.</p>
<script>
console.log("float của div:", getComputedStyle(document.querySelector("div")).float);
</script>Khi dùng float, các phần tử sẽ nằm cạnh nhau nếu không gian còn. Trong trường hợp không muốn vậy, dùng thuộc tính clear, nhận các giá trị:
| Giá trị | Ý nghĩa |
|---|---|
none |
không bị đẩy xuống dưới đối tượng đang float (mặc định) |
left |
đặt dưới đối tượng đang float: left |
right |
đặt dưới đối tượng đang float: right |
both |
đặt dưới đối tượng đang float trái hoặc phải |
inherit |
lấy giá trị clear của phần tử cha |
Trong ví dụ trước, thẻ p luôn nằm cùng hàng với div1 (chữ chảy quanh khối trôi), dù p có float hay không. Để p nằm dưới khối, thêm clear: left:
<style>
body { background-color: #f0f0f0; margin: 8px; font-family: sans-serif; }
div { background-color: #1abc9c; width: 240px; height: 140px; color: white; float: left; margin-right: 20px; }
p { clear: left; }
</style>
<div class="div1"><h1>Div 1</h1></div>
<p>Chào mừng bạn đến với trang Web của chúng tôi! Đoạn này dùng clear: left nên bị đẩy xuống dưới khối trôi.</p>
<script>
console.log("p clear:", getComputedStyle(document.querySelector("p")).clear);
</script>Mở rộng: Phần tử
floatbị rút khỏi dòng chảy nên cha của nó có thể "xẹp" chiều cao về 0 (cha không "ôm" con trôi). Cách sửa hiện đại: cho chadisplay: flow-root(hoặcoverflow: auto), hoặc thêm một phần tử cuối cóclear: both. Ngày nayfloatchủ yếu dùng để cho chữ bao quanh ảnh; việc dàn cột/hàng nên dùng Flexbox và Grid (buổi 9).
Thực hành theo giáo trình
Giáo trình: Chương 4 — mục "Thực hành" (Bài 4.1, 4.2, 4.3) và quy ước
box-sizing: border-boxở mục "Mô hình hộp".
Ba bài thực hành của chương 4 (Bài 4.1. Thiết kế Website 1, Bài 4.2. Thiết kế Website 2, Bài 4.3. Thiết kế Website MinhBlogs) dựng các trang hoàn chỉnh bằng Flexbox, Grid và thiết kế đáp ứng, nên được thực hiện ở buổi 9, sau khi bạn học xong các kỹ thuật đó. Buổi này là bước chuẩn bị: bạn cần thành thạo những "viên gạch" mà ba bài thực hành dùng liên tục. Mục tiêu: tạo một thẻ hồ sơ (profile card) áp dụng đúng quy ước của giáo trình, theo các bước sau:
- Mở đầu tệp CSS bằng quy tắc
*, *::before, *::after { box-sizing: border-box; }(quy ước áp dụng cho mọi bài thực hành trong chương). - Đặt
body { margin: 0; }, rồi tạo thẻ.cardcówidth: 280px,padding: 20px,border, và căn giữa bằngmargin: 30px auto. - Đặt ảnh đại diện làm hộp vuông bo tròn, căn giữa bằng
display: block; margin: auto. - Đặt huy hiệu "Mới" ở góc trên bên phải thẻ bằng
position: absolute(thẻ làposition: relative). - Giới hạn phần giới thiệu bằng
max-heightvàoverflow: autođể dài quá thì cuộn.
Bạn có thể chạy bản hoàn chỉnh dưới đây rồi tự thay đổi số liệu:
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-family: sans-serif; background: #f4f6f8; }
.card { position: relative; width: 280px; margin: 30px auto; padding: 20px; border: 2px solid #1abc9c; background: white; text-align: center; }
.avatar { display: block; width: 80px; height: 80px; margin: 0 auto 12px; border-radius: 50%; background: #1abc9c; }
.moi { position: absolute; top: 8px; right: 8px; background: #e74c3c; color: white; font-size: 12px; padding: 2px 8px; }
.gioithieu { max-height: 60px; overflow: auto; text-align: left; font-size: 14px; }
</style>
<div class="card">
<span class="moi">Mới</span>
<span class="avatar"></span>
<h3>Nguyễn Minh An</h3>
<div class="gioithieu">Sinh viên năm hai ngành Công nghệ thông tin, yêu thích thiết kế giao diện web. Đang học môn Thiết kế Web và xây dựng trang cá nhân đầu tiên của mình bằng HTML và CSS.</div>
</div>
<script>
const c = getComputedStyle(document.querySelector(".card"));
console.log("box-sizing:", c.boxSizing, "| width:", c.width, "| margin:", c.marginTop, c.marginLeft);
</script>Mẹo: Khi sang buổi 9 làm Bài 4.1, bạn sẽ thấy khung
flex-containercủa giáo trình chỉ là việc sắp xếp các hộp đã học ở đây theo một hướng mới; kích thước, đệm, lề vẫn tính y như cũ.
Câu hỏi ôn tập
Các câu hỏi 4.1–4.4 trong mục "Câu hỏi ôn tập lý thuyết" của chương 4 thuộc nội dung buổi này (các câu 4.5–4.13 về Flexbox và Grid sẽ ôn ở buổi 9). Đáp án đối chiếu Phụ lục. Kèm theo là ba câu tự luận ngắn để bạn kiểm tra phần tính toán.
Câu 4.1. Thuộc tính CSS nào được sử dụng để thiết lập độ rộng của một phần tử HTML? A. width — B. height — C. margin — D. padding
Đáp án: A. width đặt độ rộng; height đặt chiều cao; margin, padding là khoảng cách.
Câu 4.2. Thuộc tính CSS nào được sử dụng để thêm khoảng cách giữa các phần tử HTML? A. width — B. height — C. margin — D. padding
Đáp án: C. margin tạo khoảng cách bên ngoài viền, tức giữa các phần tử; padding là khoảng cách giữa nội dung và viền của chính phần tử.
Câu 4.3. Đơn vị kích thước CSS nào là đơn vị tương đối? A. px — B. em — C. mm — D. cm
Đáp án: B. em tương đối so với cỡ chữ hiện tại; px, mm, cm là đơn vị tuyệt đối.
Câu 4.4. Đơn vị nào dùng để định nghĩa kích thước theo tỷ lệ phần trăm của chiều cao khung nhìn? A. em — B. vmin — C. % — D. vh
Đáp án: D. vh bằng 1% chiều cao khung nhìn; vmin là 1% cạnh nhỏ hơn; % tính theo phần tử cha; em theo cỡ chữ.
Câu tự luận 1. Một div có width: 300px; padding: 20px; border: 5px solid; margin: 10px; và box-sizing: content-box. Tổng chiều rộng chiếm chỗ là bao nhiêu? Nếu đổi sang border-box thì sao?
Đáp án: content-box: 300 + 2×20 + 2×5 + 2×10 = 370 px. Với border-box: hộp (gồm viền và đệm) rộng 300 px, cộng lề 2×10 → chiếm 320 px, vùng nội dung còn 300 − 40 − 10 = 250 px.
Câu tự luận 2. h1 có margin-bottom: 50px, h2 liền sau có margin-top: 30px. Khoảng cách dọc giữa hai tiêu đề là bao nhiêu và vì sao?
Đáp án: 50 px, do gom lề (margin collapsing): hai lề dọc kề nhau thu gọn thành lề lớn nhất, không cộng dồn.
Câu tự luận 3. Vì sao margin: auto không căn giữa được một thẻ span, và sửa thế nào?
Đáp án: span là phần tử nội tuyến (inline), không nhận width và không căn giữa bằng margin: auto. Sửa bằng display: block (hoặc inline-block rồi căn giữa bằng text-align: center ở cha) và đặt width.
Sai lầm thường gặp
| Sai lầm | Hậu quả | Cách đúng |
|---|---|---|
Đặt width: 100% cùng padding hoặc border mà vẫn để content-box |
Hộp rộng hơn khối chứa, xuất hiện thanh cuộn ngang | Đặt box-sizing: border-box cho cả tài liệu |
Quên đơn vị: width: 300 |
Khai báo bị bỏ qua | Luôn ghi đơn vị (300px), chỉ số 0 mới không cần |
Dùng margin: auto cho span, a (phần tử inline) hoặc khi không có width |
Phần tử không được căn giữa | Chuyển display: block và đặt width |
| Nghĩ hai lề dọc cộng dồn (50 + 30 = 80) | Khoảng cách thực nhỏ hơn dự tính vì gom lề | Nhớ khoảng cách = lề lớn hơn; dùng padding hoặc flow-root nếu cần tách |
Đặt width, height cho phần tử display: inline |
Không có hiệu lực | Dùng inline-block hoặc block |
Dùng top, left mà không đặt position |
Không có tác dụng (vì mặc định static) |
Đặt position khác static |
position: absolute mà cha không có position |
Phần tử bám theo cả trang, lệch khỏi hộp mong muốn | Đặt position: relative cho hộp cha làm mốc |
Đặt z-index cho phần tử static |
z-index không có hiệu lực |
Đặt position: relative (hoặc khác static) cùng z-index |
sticky nằm trong cha có overflow: hidden/auto/scroll |
Không dính khi cuộn | Bỏ overflow ở các tổ tiên hoặc đặt sticky đúng vùng cuộn |
Dùng px cho mọi cỡ chữ và độ rộng |
Khó co giãn trên nhiều thiết bị | Ưu tiên đơn vị tương đối (%, rem, vw) |
Quên clear hoặc không xử lý cha khi dùng float |
Chữ chảy sai chỗ, khối cha bị xẹp chiều cao | Dùng clear, flow-root; dàn bố cục bằng Flexbox/Grid |
Tóm tắt
- Thiết kế bố cục là sắp xếp vị trí, kích thước, khoảng cách các phần tử để người dùng dễ tìm thông tin; bao gồm mô hình hộp,
display, căn chỉnh,position,overflow, Flexbox/Grid và thiết kế đáp ứng. - Mô hình hộp: content → padding → border → margin. Mặc định (
content-box)widthchỉ là vùng nội dung; dùngbox-sizing: border-boxđểwidthgồm cả padding và border. - Kích thước:
width,height(giá trịauto,%,inherit);max-width,max-heightgiúp hộp co lại khi thiếu chỗ. Đơn vị: tuyệt đối (cm,mm,in,px,pt,pc) và tương đối (em,rem,ex,ch,vw,vh,vmin,vmax,%) — ưu tiên tương đối. - Margin (ngoài viền, có
auto,inherit, gom lề dọc) và padding (trong viền, có màu nền) viết rút gọn theo thứ tự trên → phải → dưới → trái. display:none,inline,block,inline-block; căn chỉnh:margin: auto(hộp khối cówidth),text-align(văn bản),float/margin(trái, phải),padding/line-height/position + transform(căn dọc).position:static,relative,absolute,fixed,stickycùngtop/right/bottom/leftvàz-index;overflow:visible,hidden,scroll,auto;floatcùngclearcho phép chữ chảy quanh phần tử.