Mô hình hộp CSS khi một thẻ không vừa chỗ
Tính chiều rộng nội dung, padding, border và margin qua bốn trạng thái đo được; phân biệt thẻ quá rộng với chữ tràn và hình chữ nhật đã biến đổi.

Một thẻ có width: 320px vẫn có thể chiếm 356 pixel theo chiều ngang. Với content-box, chiều rộng chỉ phần nội dung; padding và border được cộng ra bên ngoài. Với border-box, cùng khai báo đó thường đã bao gồm hai phần này. Đổi mô hình tính kích thước làm thay đổi chỗ dành cho văn bản, nên có thể sửa một lỗi tính toán thực sự mà vẫn giữ khoảng đệm mong muốn.
Nếu bạn đã biết viết bộ chọn CSS, hãy dùng ví dụ này để lần theo cách một thành phần bị tràn nhận chiều rộng của nó. Ví dụ dùng một khối thông thường, bố cục ngang và hướng từ trái sang phải. Thẻ không chịu tác động của cách tính kích thước flex hay grid, phép biến đổi hoặc thanh cuộn. Những giả định này giúp ta tính từng phần trước khi xét thêm các ràng buộc bố cục khác.
Bắt đầu với khối chứa đo được
Đặt thẻ bài viết giả định sau vào một tài liệu HTML trống. Trong ví dụ này, stage là khối chứa của thẻ: chiều rộng phần nội dung của nó là cơ sở để tính chiều rộng theo phần trăm.
<div class="stage">
<article class="card">
<h2>Một thử nghiệm bố cục nhỏ</h2>
<p>Padding cần chừa khoảng trống quanh đoạn chữ này.</p>
</article>
</div>
.stage {
width: 320px;
max-width: 100%;
outline: 1px dashed #696d45;
}
.card {
box-sizing: content-box;
width: 100%;
padding: 16px;
border: 2px solid #696d45;
}
Dùng vùng nhìn đủ rộng để stage còn ít nhất 320 pixel sau khi trừ margin của tài liệu. Đường outline đánh dấu stage mà không thêm border vào chiều rộng của nó. Khai báo content-box tường minh giúp tái hiện ví dụ ngay cả khi stylesheet có sẵn một quy tắc đặt lại mặc định.
Chiều rộng phần nội dung của thẻ là 320 pixel. Padding hai bên đóng góp 32 pixel, còn hai border đóng góp bốn pixel. Hộp tính đến mép ngoài border vì vậy rộng 356 pixel. Stage không rộng thêm; thẻ nhô ra ngoài mép phải của stage 36 pixel. Chỉ một thẻ cũng đã gây tràn, nên thay đổi khoảng cách giữa các thẻ lân cận không loại bỏ được nguyên nhân này.
Đổi mô hình của thẻ sang border-box. Hộp tính đến border lúc này rộng 320 pixel, còn phần nội dung rộng 284. Padding vẫn chiếm 16 pixel ở mỗi bên. Văn bản có thể xuống dòng sớm hơn vì dòng chữ có ít chỗ hơn. Tài liệu box-sizing của MDN định nghĩa cả hai cách tính và liệt kê những điều khiển được stylesheet của trình duyệt đặt thành border-box. Vì thế, mặc định của một nút không cho biết mô hình của phần tử article thông thường.
Cả hai hàng đều có cùng padding và border. Chỉ phần chịu ràng buộc bởi khai báo chiều rộng là thay đổi.
Chiều rộng phần trăm đầy đủ không chừa chỗ cho margin
Giữ border-box rồi thêm margin-inline: 20px cho thẻ. Chiều rộng vẫn là 320 pixel; nó không trở thành phần còn lại sau khi trừ margin. Trong bố cục khối thông thường từ trái sang phải này, mép border trái bắt đầu ở tọa độ 20 so với stage, còn mép phải đến tọa độ 340. Thẻ vì vậy nhô ra ngoài stage 20 pixel.
Có một chi tiết khiến ta không nên mô tả đây là hai khoảng trống 20 pixel cùng được giữ lại. Chiều rộng đầy đủ cộng với cả hai margin yêu cầu tạo ra một khối bị ràng buộc quá mức. Quy tắc tính chiều rộng trong luồng thông thường của CSS giải quyết xung đột bằng cách tính lại giá trị margin phải thực sự được dùng khi khối chứa có hướng từ trái sang phải. Khoảng trống bên phải đã yêu cầu không được giữ nguyên. Hướng từ phải sang trái xử lý phía đối diện; flex và grid dùng thuật toán khác.
Nếu mục đích là một khối có khoảng trống 20 pixel ở cả hai bên, hãy đổi chiều rộng sang auto. Trình duyệt lúc này có thể giải ra chiều rộng sau khi tính các margin đó. Hộp tính đến border rộng 280 pixel, trải từ tọa độ 20 đến 300. Phần nội dung còn 244 pixel sau khi trừ padding và border. Chuyển khoảng cách thành padding của phần tử cha cũng là một lựa chọn, nhưng nó tác động đến các phần tử con khác và thay đổi khối chứa cần kiểm tra.
Hàng thứ hai thể hiện vị trí border thực tế, không giả định cả hai margin yêu cầu đều được giữ lại. Hàng thứ ba tạo được hai khoảng trống có thể sử dụng.
Kiểm tra bốn trạng thái trong console
Chạy đoạn sau sau khi đã thêm HTML và CSS ở trên. Nó tạm thay đổi cùng một thẻ, ghi lại từng hình chữ nhật border so với stage, rồi khôi phục các style nội tuyến ban đầu. Tên trạng thái và số đo chỉ là dữ liệu chẩn đoán; đoạn mã không gửi yêu cầu mạng hay thay đổi trạng thái ứng dụng.
const card = document.querySelector(".card");
const stage = document.querySelector(".stage");
const originalStyle = card.style.cssText;
const cases = [
["content", "content-box", "100%", "0"],
["border", "border-box", "100%", "0"],
["full-with-margins", "border-box", "100%", "20px"],
["auto-with-margins", "border-box", "auto", "20px"],
];
const rows = cases.map(([name, sizing, width, margin]) => {
card.style.boxSizing = sizing;
card.style.width = width;
card.style.marginInline = margin;
const parent = stage.getBoundingClientRect();
const box = card.getBoundingClientRect();
return {
name,
width: box.width,
left: box.left - parent.left,
right: box.right - parent.left,
};
});
card.style.cssText = originalStyle;
console.table(rows);
Với stage rộng 320 pixel, các chiều rộng lần lượt phải là 356, 320, 320 và 280. Tọa độ trái lần lượt là 0, 0, 20 và 20; tọa độ phải là 356, 320, 340 và 300. Nếu stage hẹp hơn do chiều rộng tối đa, hãy thay chiều rộng phần nội dung thực tế của nó vào phép tính. Các hằng số do padding, border và margin đóng góp vẫn giữ nguyên trong ví dụ này.
Tài liệu về hình chữ nhật của MDN giải thích rằng API này bao gồm padding và border, đồng thời trả vị trí so với vùng nhìn. Trừ mép trái của stage loại bỏ độ dịch chuyển chung theo vùng nhìn trong trường hợp đơn giản, không có phép biến đổi này. Các hình chữ nhật trả về là ảnh chụp số đo tại thời điểm gọi, nên cần đọc lại giá trị sau mỗi lần thay đổi.
Một phép biến đổi làm thay đổi ý nghĩa của hình chữ nhật đó. Phóng thẻ đang vừa 320 pixel lên tỷ lệ 1,1 thì chiều rộng được báo là 352, trong khi chiều rộng bố cục chưa biến đổi vẫn là 320. Thuật toán CSSOM View áp dụng phép biến đổi của phần tử và các phần tử tổ tiên khi thu thập hình chữ nhật. Hãy bỏ phép biến đổi trước khi dùng số đo này để kiểm chứng phép tính hộp CSS. Bóng đổ lại khác: nó có thể được vẽ vượt ra ngoài border mà không làm lớn hình chữ nhật border này.
Biết giới hạn của quy tắc tiện dụng
Ngay cả border-box cũng không thể làm phần nội dung có chiều rộng âm. Đặt chiều rộng thẻ thành 20 pixel, vẫn giữ padding 16 pixel và border hai pixel ở mỗi bên. Những phần cố định đó đã cần 36 pixel. Chiều rộng nội dung dừng ở 0 pixel, còn hộp tính đến border đo được 36 pixel dù khai báo nhỏ hơn. Định nghĩa box-sizing trong CSS Sizing quy định giới hạn dưới này. Một thành phần quá hẹp có thể cần giảm khoảng đệm, thay vì đổi sang một từ khóa tính kích thước khác.
Border có kích thước đúng cũng không bảo đảm nội dung bên trong vừa. Hãy chèn một mã định danh dài không có dấu cách. Với cách ngắt dòng thông thường, các ký tự có thể vượt ra khỏi thẻ dù chiều rộng border đo được vẫn đúng. Với nội dung cho phép ngắt tại vị trí bất kỳ, overflow-wrap: anywhere tạo thêm cơ hội xuống dòng mà không sửa chuỗi. Tài liệu overflow-wrap mô tả hành vi đó. Ảnh chụp các ký tự bị tràn chưa chứng minh padding đã bị cộng hai lần. Khi tăng cỡ chữ, ngay một từ thông thường trong tiêu đề cũng có thể gặp vấn đề tương tự. Hãy áp dụng cách ngắt dòng đã chọn cho cả tiêu đề, thay vì chỉ kiểm tra các đoạn văn.
Các ràng buộc khác cần được kiểm tra riêng. Ảnh có thể cần giới hạn kích thước tối đa theo trục inline; phần tử flex có thể có kích thước tối thiểu dựa trên nội dung; track của grid có thể có mức tối thiểu riêng. Ẩn phần tràn có thể che văn bản và dấu chỉ báo focus mà không giải quyết ràng buộc nào trong số đó. Ví dụ này kiểm tra phép tính hộp, không đưa ra một cách sửa chung cho mọi loại tràn.
Chọn mặc định rồi kiểm tra phạm vi áp dụng
Các dự án thường chọn quy tắc đặt lại mặc định dựa trên kế thừa như sau:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
Thuộc tính này mặc định không kế thừa, nên cần quy tắc thứ hai. Các phần tử giả được tạo ra cũng tham gia. Hãy bỏ khai báo ghi đè tường minh của .card trong ví dụ trước khi mong quy tắc đặt lại điều khiển nó. Một thành phần cũ có thể chủ động chọn mô hình khác tại phần tử gốc của nó, rồi các phần tử con tuân theo quy tắc kế thừa sẽ dùng lựa chọn đó. Đổi mặc định trong ứng dụng hiện có có thể làm thay đổi biểu mẫu và widget vốn phụ thuộc vào phép tính cũ; hãy kiểm tra những thành phần đó thay vì cho rằng việc đặt lại không tác động đến giao diện.
Ở bài tập cuối, tăng padding của thẻ lên 24 pixel và border lên ba pixel. Dự đoán số đo trước khi chạy lại ví dụ bốn trạng thái trong console. Với chiều rộng stage ban đầu, hộp tính đến border dưới content-box phải rộng 374 pixel, phần nội dung dưới border-box vừa khít phải rộng 266, còn thẻ auto có margin vẫn giữ border rộng 280 pixel và chừa 226 cho nội dung. Sau đó tăng cỡ chữ. Chiều cao sẽ tăng khi văn bản xuống dòng, còn các số đo ngang này giữ nguyên. Nếu chúng thay đổi, hãy kiểm tra quy tắc bổ sung nào đã tham gia phép tính.