Cho đến bây giờ, bạn đã học Flexbox và Grid — hai cách sắp xếp bố cục hiện đại. Nhưng đôi khi bạn cần điều khiển chính xác vị trí của một phần tử — chẳng hạn như làm một button cố định ở góc, hoặc một thanh navigation dính ở đầu trang.
Bài này sẽ dạy bạn position — cách điều khiển vị trí phần tử bằng các giá trị relative, absolute, fixed, và sticky.
Position là gì?
position xác định cách phần tử được định vị trên trang. Có 5 giá trị chính:
| Giá trị | Mô tả | Khi nào dùng |
|---|---|---|
static (mặc định) | Sắp xếp theo luồng bình thường (block, inline…) | Hầu hết các phần tử |
relative | Định vị dựa trên vị trí mặc định của nó | Dịch chuyển nhẹ, tạo positioned parent |
absolute | Định vị dựa trên positioned parent | Dropdown, modal, tooltip |
fixed | Định vị dựa trên viewport (không cuộn theo) | Navigation cố định, button floating |
sticky | Kết hợp relative và fixed (dính khi cuộn) | Header dính, bảng dính |
Position: static (Mặc định) – Luồng bình thường
position: static; là giá trị mặc định. Phần tử sắp xếp theo luồng bình thường của HTML.
.box {
position: static; /* Mặc định */
/* top, left, right, bottom, z-index không có tác dụng */
}Position: relative – Dịch chuyển từ vị trí ban đầu
position: relative; cho phép bạn dịch chuyển phần tử so với vị trí ban đầu bằng top, left, right, bottom. Nhưng nó vẫn chiếm không gian ở vị trí cũ.
.box {
position: relative;
top: 20px; /* Dịch xuống 20px */
left: 30px; /* Dịch sang phải 30px */
/* Phần tử vẫn chiếm không gian ở vị trí cũ */
}
/* Hoặc */
.box {
position: relative;
top: -10px; /* Dịch lên 10px */
right: 15px; /* Dịch sang trái 15px */
}Khi nào dùng relative?
- Dịch chuyển nhẹ một phần tử mà không ảnh hưởng đến layout
- Tạo positioned parent: Để
absolutechild có thể định vị dựa trên nó
/* relative thường dùng để tạo "positioned context" cho absolute */
.container {
position: relative; /* Không dịch, chỉ tạo context */
}
.container .absolute-child {
position: absolute; /* Định vị dựa trên .container */
top: 10px;
left: 10px;
}Position: absolute – Định vị dựa trên positioned parent
position: absolute; tách phần tử khỏi luồng bình thường và định vị nó dựa trên positioned parent gần nhất (parent có position khác static).
/* Parent: positioned container */
.parent {
position: relative; /* Bắt buộc! */
width: 300px;
height: 200px;
background: lightgray;
}
/* Child: absolute positioned */
.child {
position: absolute;
top: 20px;
left: 30px;
width: 100px;
height: 100px;
background: blue;
/* Định vị từ góc trên-trái của .parent */
}Các thuộc tính định vị
| Thuộc tính | Ý nghĩa | Ví dụ |
|---|---|---|
top | Khoảng cách từ trên | top: 20px; |
bottom | Khoảng cách từ dưới | bottom: 10px; |
left | Khoảng cách từ trái | left: 30px; |
right | Khoảng cách từ phải | right: 15px; |
Ví dụ thực tế – Dropdown menu
<!-- HTML -->
<div class="dropdown">
<button>Menu</button>
<div class="dropdown-menu">
<a href="#">Tùy chọn 1</a>
<a href="#">Tùy chọn 2</a>
<a href="#">Tùy chọn 3</a>
</div>
</div>/* CSS */
.dropdown {
position: relative; /* Positioned context */
display: inline-block;
}
.dropdown button {
padding: 10px 15px;
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
border-radius: 5px;
}
.dropdown-menu {
position: absolute; /* Định vị dựa trên .dropdown */
top: 100%; /* Dưới button */
left: 0; /* Sát trái button */
background-color: white;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
min-width: 200px;
display: none; /* Ẩn mặc định */
z-index: 10; /* Hiển thị trên các phần tử khác */
}
.dropdown:hover .dropdown-menu {
display: block; /* Hiện khi hover */
}
.dropdown-menu a {
display: block;
padding: 12px 15px;
color: #333;
text-decoration: none;
transition: background-color 0.3s;
}
.dropdown-menu a:hover {
background-color: #f0f0f0;
}Position: fixed – Cố định trên viewport
position: fixed; tách phần tử khỏi luồng bình thường và cố định nó trên viewport (không cuộn theo trang). Rất hữu ích cho navigation, button floating, modal…
/* Navigation cố định ở trên */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0; /* Hoặc width: 100%; */
height: 60px;
background-color: #2c3e50;
color: white;
z-index: 100; /* Hiển thị trên tất cả */
}
/* Button floating ở góc */
.float-button {
position: fixed;
bottom: 20px; /* Cách dưới 20px */
right: 20px; /* Cách phải 20px */
width: 60px;
height: 60px;
background-color: #e74c3c;
color: white;
border-radius: 50%; /* Hình tròn */
cursor: pointer;
z-index: 50;
}
/* Modal overlay - chiếm toàn màn hình */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 200;
}⚠️ Lưu ý khi dùng fixed
⚠️ Cảnh báo: Phần tử
position: fixed:
- Tách khỏi luồng (không chiếm không gian mặc định)
- Không cuộn theo trang (cố định trên viewport)
- Luôn nằm trên các phần tử khác (ngoại trừ những cái có z-index cao hơn)
- Có thể che phủ nội dung nếu không quản lý z-index
Position: sticky – Dính khi cuộn
position: sticky; là sự kết hợp của relative và fixed. Phần tử sắp xếp bình thường (relative) cho đến khi bạn cuộn đến điểm nhất định, sau đó nó “dính” ở đó (fixed).
/* Header dính ở trên khi cuộn */
.table-header {
position: sticky;
top: 0; /* Dính ở vị trí top: 0 */
background-color: white;
z-index: 10;
}
/* Sidebar dính ở bên */
.sidebar {
position: sticky;
top: 60px; /* Dính dưới navbar fixed (60px) */
max-height: calc(100vh - 60px); /* Cao tối đa = viewport - navbar */
overflow-y: auto;
}Ví dụ thực tế – Bảng với header dính
<!-- HTML -->
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Tên</th>
<th>Email</th>
<th>Phòng ban</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>Nguyễn A</td><td>a@email.com</td><td>IT</td></tr>
<tr><td>2</td><td>Trần B</td><td>b@email.com</td><td>HR</td></tr>
<!-- Thêm nhiều hàng... -->
</tbody>
</table>/* CSS */
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table thead {
position: sticky;
top: 0; /* Dính ở đầu khi cuộn */
background-color: #2c3e50;
color: white;
z-index: 10;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.data-table tbody tr:hover {
background-color: #f0f0f0;
}Z-index – Độ ưu tiên hiển thị
z-index xác định thứ tự xếp chồng của các phần tử. Phần tử có z-index cao hơn sẽ hiển thị trên phần tử có z-index thấp hơn.
/* Thứ tự từ dưới lên trên */
.background {
position: absolute;
z-index: 1;
}
.middle {
position: absolute;
z-index: 10;
}
.foreground {
position: absolute;
z-index: 100;
}
/* Hoặc dùng negative z-index */
.behind {
position: absolute;
z-index: -1; /* Nằm dưới background của element cha */
}💡 Mẹo z-index:
- Z-index chỉ hoạt động với positioned elements (
relative,absolute,fixed,sticky)- Không cần dùng z-index quá cao (100, 1000…), chỉ cần đủ lớn hơn cái khác
- Tạo một “z-index hierarchy”: navbar = 100, dropdown = 110, modal = 200…
Ví dụ hoàn chỉnh – Layout với fixed navbar + sticky sidebar
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning Demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Fixed navbar ở trên -->
<nav class="navbar">
<div class="navbar-content">
<h1 class="logo">Logo</h1>
<ul class="nav-links">
<li><a href="#">Trang chủ</a></li>
<li><a href="#">Dịch vụ</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Liên hệ</a></li>
</ul>
</div>
</nav>
<div class="main-wrapper">
<!-- Sticky sidebar ở trái -->
<aside class="sidebar">
<div class="sidebar-content">
<h3>Danh mục</h3>
<ul>
<li><a href="#">Bài viết mới</a></li>
<li><a href="#">Phổ biến nhất</a></li>
<li><a href="#">HTML</a></li>
<li><a href="#">CSS</a></li>
<li><a href="#">JavaScript</a></li>
</ul>
</div>
</aside>
<!-- Main content -->
<main class="content">
<article class="article">
<h2>Tiêu đề bài viết</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
<p>Cuộn xuống để xem navbar dính và sidebar dính!</p>
<!-- Thêm nội dung dài để tạo scroll -->
</article>
</main>
</div>
<!-- Float button ở góc phải dưới -->
<button class="float-button">↑</button>
</body>
</html>/* FILE: styles.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
}
/* Fixed navbar */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
z-index: 100;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.navbar-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
font-weight: bold;
}
.nav-links {
display: flex;
list-style: none;
gap: 30px;
}
.nav-links a {
color: white;
text-decoration: none;
transition: opacity 0.3s;
}
.nav-links a:hover {
opacity: 0.8;
}
/* Main wrapper - cách trên do navbar fixed */
.main-wrapper {
display: flex;
margin-top: 60px; /* Cách trên do navbar fixed */
min-height: calc(100vh - 60px);
}
/* Sticky sidebar */
.sidebar {
position: sticky;
top: 60px; /* Dính dưới navbar */
width: 250px;
background-color: #f9f9f9;
border-right: 1px solid #ddd;
max-height: calc(100vh - 60px);
overflow-y: auto;
}
.sidebar-content {
padding: 20px;
}
.sidebar-content h3 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 18px;
}
.sidebar-content ul {
list-style: none;
}
.sidebar-content li {
margin-bottom: 10px;
}
.sidebar-content a {
color: #667eea;
text-decoration: none;
transition: color 0.3s;
display: block;
padding: 8px 10px;
border-radius: 5px;
}
.sidebar-content a:hover {
background-color: #e8e8ff;
color: #764ba2;
}
/* Main content */
.content {
flex: 1;
padding: 40px;
}
.article {
max-width: 800px;
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.article h2 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 28px;
}
.article p {
color: #555;
margin-bottom: 15px;
text-align: justify;
}
/* Thêm nội dung dài để test scroll */
.article p::first-letter {
font-size: 1.1em;
font-weight: bold;
}
/* Float button ở góc phải dưới */
.float-button {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
background-color: #e74c3c;
color: white;
border: none;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
z-index: 90; /* Dưới navbar */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
transition: all 0.3s;
}
.float-button:hover {
background-color: #c0392b;
transform: scale(1.1);
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
position: relative;
width: 100%;
max-height: none;
border-right: none;
border-bottom: 1px solid #ddd;
}
.main-wrapper {
flex-direction: column;
}
.nav-links {
gap: 15px;
}
.content {
padding: 20px;
}
}Bảng so sánh Positioning
| Position | Luồng | Dựa trên | Cuộn | Khi nào dùng |
|---|---|---|---|---|
static | Bình thường | Luồng HTML | Theo | Mặc định |
relative | Bình thường (dịch) | Vị trí mặc định | Theo | Dịch nhẹ, positioned parent |
absolute | Tách | Positioned parent | Theo | Dropdown, modal, tooltip |
fixed | Tách | Viewport | Không | Navbar, float button, modal overlay |
sticky | Bình thường → Fixed | Viewport (khi dính) | Theo (bình thường), không (khi dính) | Header dính, sidebar dính |
Tóm tắt
- ✅ position: static: Mặc định, sắp xếp bình thường
- ✅ position: relative: Dịch chuyển từ vị trí ban đầu, tạo positioned context
- ✅ position: absolute: Định vị dựa trên positioned parent
- ✅ position: fixed: Cố định trên viewport, không cuộn theo
- ✅ position: sticky: Relative đến khi dính (fixed) khi cuộn
- ✅ top, left, right, bottom: Các thuộc tính định vị
- ✅ z-index: Độ ưu tiên hiển thị (chỉ hoạt động với positioned elements)
- ✅ relative thường dùng làm “positioned parent” cho absolute
- ✅ fixed + z-index: Quản lý navbar, modal, float button
🎯 Bài tập thực hành
Bài tập 1: Tạo dropdown menu với position absolute:
- Button “Menu” giữa trang
- Container button có
position: relative - Dropdown menu có
position: absolute, top: 100%, left: 0 - Dropdown ẩn mặc định, hiện khi hover button
- Dropdown có 5 link, styling đẹp
Bài tập 2: Tạo fixed navigation bar + float button:
- Navbar cố định ở trên:
position: fixed, top: 0, left: 0, right: 0 - Nội dung chính cách trên (margin-top: 60px)
- Float button ở góc phải dưới:
position: fixed, bottom: 20px, right: 20px - Thêm z-index để navbar hiển thị trên button
Bài tập 3: Tạo bảng dữ liệu với header sticky:
- Bảng có ít nhất 10 hàng dữ liệu
- Header bảng:
position: sticky, top: 0, background đặc biệt - Khi cuộn xuống, header dính ở đầu (không cuộn cùng)
- Styling đẹp cho header, row, hover effect
- Responsive: horizontal scroll trên mobile
Chúc bạn thành công! 🚀