Giới thiệu
Nếu bạn đã từng thêm một margin vào một phần tử trong CSS và thấy nó biến mất một cách bí ẩn, bạn không phải là người duy nhất. Margin collapse là một trong những tính năng khó hiểu và thường bị hiểu sai trong hành vi bố cục của CSS.
Dù bạn là một người mới bắt đầu gặp khó khăn trong việc tạo khoảng cách giữa các phần tử, một lập trình viên trung cấp đối diện với các lỗi bố cục kỳ lạ, hay một nhà thiết kế giàu kinh nghiệm tự hỏi tại sao khoảng cách của bạn đột nhiên bị hỏng - margin collapse có thể là nguyên nhân vô hình.
Mục tiêu bài viết
Bài viết này sẽ giúp bạn hiểu rõ:
- Margin collapse là gì và tại sao nó xảy ra
- Các quy tắc chính điều khiển nó
- Các trường hợp ngoại lệ và điều bạn cần lưu ý
- Và quan trọng nhất: cách kiểm soát nó như một chuyên gia
Hãy cùng làm sáng tỏ một trong những hành vi bố cục tinh tế nhất của CSS nhé!
1. Quy tắc chính: Margins chỉ sụp đổ khi chúng chạm nhau 🎯
Margins sẽ sụp đổ khi hai margin dọc gặp nhau trực tiếp mà không có gì ở giữa. Thay vì chồng lên nhau, chúng kết hợp thành một margin duy nhất.
Ví dụ:
css
<div id="box-1">
-- Hộp 1 --
</div>
<div id="box-2">
-- Hộp 2 --
</div>
#box-1{
margin-bottom: 60px;
background: gray;
height: 80px;
}
#box-2{
margin-top: 30px;
background: lightgreen;
height: 80px;
}
Bạn có thể mong đợi 90px không gian. Trên thực tế, bạn chỉ nhận được 60px (margin lớn hơn sẽ thắng).
👉 Margin collapse chỉ xảy ra với các phần tử block-level (ví dụ: div, p, section). Các phần tử inline (span, a, strong, v.v.) không làm sụp đổ margins, vì chúng không tạo ra khoảng cách khối dọc theo cách tương tự.
2. Margins của parent và child cũng sụp đổ 😱
Margin collapse không chỉ giới hạn ở các phần tử anh chị em. Margin trên của một phần tử con có thể sụp đổ với margin của phần tử cha — thậm chí có thể đẩy phần tử cha ra ngoài.
Ví dụ:
css
<div id="parent">
<div id="child">Đoạn văn con</div>
</div>
#parent{
background: gray;
height: 120px;
}
#child{
margin-top: 50px;
background: lightgreen;
}
Khi bạn thêm một giá trị dương — chẳng hạn như 50px — vào margin trên của đoạn văn con, bạn có thể mong đợi nó sẽ được đẩy xuống chính xác 50px từ đỉnh của phần tử cha, đúng không?
🔴 Sai — Margin collapse không chỉ giới hạn ở các phần tử anh chị em.
👉 Margin trên của phần tử con có thể sụp đổ với margin của phần tử cha, thậm chí đẩy toàn bộ phần tử cha xuống.
Điều này giải thích tại sao margin collapse là một trong những phát hiện gây khó chịu nhất cho các lập trình viên CSS mới.
3. Việc lồng nhau không cứu bạn 🤨
Không quan trọng phần tử đó được lồng sâu như thế nào. Nếu margin trên của nó chạm vào cạnh trên của tổ tiên, nó vẫn có thể đẩy tổ tiên, bất kể nó lồng sâu đến đâu.
Ví dụ:
css
<div id="grandparent">
<div id="parent">
<div id="grandchild">
<div id="deepchild">
Con sâu
</div>
</div>
</div>
</div>
#grandparent{
margin-top: 20px;
background: gray;
height: 100px;
}
#deepchild{
margin-top: 50px;
background: lightgreen;
}
👉 Ngay cả khi có nhiều lớp bọc, margin của phần tử con vẫn sụp đổ với phần tử cha.
4. Không chạm, không sụp đổ 👌
Margins chỉ sụp đổ khi chúng thực sự chạm nhau. Thêm padding, border, hoặc nội dung inline giữa chúng, và tình trạng sụp đổ sẽ không xảy ra.
Ví dụ (với padding):
css
<div id="parent">
<div id="child"> Con </div>
</div>
#parent{
background: gray;
height: 100px;
}
#child{
margin-top: 50px;
background: lightgreen;
}
👉 Margin của phần tử con sụp đổ với margin của phần tử cha, đẩy toàn bộ div cha xuống thay vì chính div con.
Giờ — kiểm tra điều gì xảy ra khi chúng ta thêm chỉ 1px vào padding trên của div cha.
css
#parent{
padding-top: 1px;
background: gray;
height: 100px;
}
#child{
margin-top: 49px;
background: lightgreen;
}
🎉 Nó hoạt động như đúng mong đợi!
Div con đã được đẩy xuống, chính xác 50px (49px + 1px), vì chúng ta đã thêm padding vào phần tử cha, ngăn hai margins chạm nhau.
5. Chỉ dọc 🤷
Margins chỉ sụp đổ theo chiều dọc. Margins ngang (margin-left / margin-right) không bao giờ sụp đổ.
Ví dụ:
css
<div id="box-1"> Hộp 1 </div><div id="box-2"> Hộp 2 </div>
#box-1{
margin-right: 200px;
background: gray;
height: 200px;
width: 200px;
display: inline-block;
}
#box-2{
margin-left: 100px;
background: lightgreen;
height: 200px;
width: 200px;
display: inline-block;
}
👉 Bạn sẽ luôn nhận được 300px theo chiều ngang.
6. Margins âm ⛔
Margin collapse cũng hoạt động với các margin âm: nếu hai margin âm gặp nhau, margin âm lớn hơn sẽ thắng, nhưng khi một margin là dương và margin còn lại là âm, hai giá trị sẽ được cộng lại.
Ví dụ:
css
<div id="box">Hộp 1</div>
<div id="box2">Hộp 2</div>
#box {
margin-bottom: -40px; /* margin dương */
height: 100px;
background: lightgreen;
}
#box2 {
margin-top: -20px; /* margin âm */
height: 100px;
background: gray;
width: 600px;
}
👉 Cũng như với các margin dương, margin âm lớn nhất sẽ thắng.
Ví dụ (dương và âm):
css
<div id="box">Hộp 1</div>
<div id="box2">Hộp 2</div>
#box {
margin-bottom: -40px; /* margin dương */
height: 100px;
background: lightgreen;
}
#box2 {
margin-top: 80px;
height: 100px;
background: gray;
width: 600px;
}
👉 Hai giá trị sẽ được cộng lại.
7. Nhiều margins 🤔
Khi có hơn hai margins gặp nhau, trình duyệt sẽ theo công thức này:
- Tìm margin dương lớn nhất
- Tìm margin âm lớn nhất theo tuyệt đối
- Cộng chúng lại → đó là margin hiệu quả của bạn
Ví dụ:
css
<div id="box">Hộp 1</div>
<div id="box2-parent">
<div id="box2"> Hộp 2 </div>
</div>
#box {
margin-bottom: -40px; /* margin dương */
height: 100px;
background: lightgreen;
}
#box2-parent{
margin-top: 20px;
height: 100px;
background: lightgreen;
width: 600px;
}
#box2 {
margin-top: 80px;
height: 100px;
background: gray;
width: 50%;
height: 50%;
}
👉 Hai margin dương (80px - 20px) đã sụp đổ, sau đó margin dương lớn nhất (80px) và margin âm lớn nhất (-40px) đã được cộng lại.
8. Cách thoát nhanh nhất: Flexbox và Grid ⚡️
Nếu margin collapse đang khiến bạn đau đầu, đây là thủ thuật đơn giản nhất: bọc các phần tử của bạn trong một container Flexbox hoặc Grid.
Ngay khi một phần tử cha trở thành một container flex hoặc grid, các phần tử con của nó sẽ ngừng sụp đổ margins với nó. Thay vào đó, các margins của chúng sẽ hoạt động chính xác như bạn mong đợi — không còn sụp đổ vào cha hoặc khoảng cách “mất tích” kỳ lạ nữa.
Ví dụ (với Flexbox):
css
<div id="wrapper">
<div id="box1"> Hộp 1 </div>
<div id="box2"> Hộp 2 </div>
</div>
#wrapper{
display: flex;
flex-direction: column;
}
#box1{
margin-bottom: 30px;
background: lightblue;
height: 50px;
}
#box2 {
margin-top: 30px;
background: lightgreen;
height: 50px;
width: 600px;
}
👉 Mỗi hộp giữ margin riêng của nó.
Kết luận chính 🗝️
- Margin collapse chỉ xảy ra theo chiều dọc (trên/dưới), không bao giờ theo chiều ngang.
- Nó ảnh hưởng đến các phần tử block-level (như div, p, section).
- Margins sụp đổ khi chúng chạm nhau trực tiếp — anh chị em, parent/child, hoặc thậm chí là các phần tử con sâu.
- Thêm padding, border, hoặc nội dung inline ngăn chặn tình trạng sụp đổ.
- Margins âm cũng sụp đổ:
- Hai margin âm → giá trị tuyệt đối lớn nhất thắng.
- Một dương + một âm → các giá trị được cộng lại.
- Với nhiều margins, trình duyệt kết hợp margin dương lớn nhất và margin âm lớn nhất.
- Cách sửa đơn giản nhất? Sử dụng Flexbox hoặc Grid, nơi mà margin collapse không xảy ra, và khoảng cách có thể thay thế hoàn toàn cho margins.
Suy nghĩ cuối cùng 🏁
Margin collapse có thể cảm thấy bí ẩn lúc đầu, nhưng một khi bạn biết các quy tắc, nó không còn là lỗi mà trở thành một phần của logic CSS. Nắm vững điều này có nghĩa là ít bất ngờ về bố cục hơn và thiết kế sạch hơn, dễ đoán hơn.