Đang tải...

Ếch Trendy

Cùng chú ếch nhỏ khám phá thế giới Web đầy biến động. Cập nhật trending nhanh như cách ếch đớp mồi! ⌨️🌿


0

CSS Grid bị tràn trên màn hình nhỏ: tìm nguyên nhân từ min-width và kích thước cột

Ếch Trendy
Ếch Trendy

6 giờ tới · 8 phút đọc

Tại sao 1fr 1fr vẫn tạo thanh cuộn ngang?

Bạn đặt grid-template-columns: 1fr 1fr và nghĩ lưới sẽ tự chia đôi chiều rộng màn hình. Trên desktop hoạt động tốt. Nhưng trên điện thoại, thanh cuộn ngang xuất hiện.

Vấn đề không nằm ở 1fr. Đơn vị fr (fraction) tính toán không gian còn lại sau khi trình duyệt đã phân bổ kích thước tối thiểu cho nội dung. Nếu một ô lưới chứa văn bản dài hoặc ảnh không có max-width, kích thước tối thiểu của ô đó có thể vượt quá 1fr tính được - và trình duyệt sẽ mở rộng ô đó thay vì cắt nội dung.

348177 1fr chia phần dư, không phải chia toàn bộ chiều rộng

Cụ thể hơn: mỗi cột trong CSS Grid có thuộc tính min-width ngầm định là auto. Giá trị auto nghĩa là cột tối thiểu phải đủ rộng để chứa nội dung bên trong mà không bị vỡ dòng. Khi nội dung quá rộng (ảnh 400px, tiêu đề dài không có word-break...), min-width: auto khiến cột phình ra, đẩy tổng chiều rộng grid vượt viewport.

Đây là điểm khác biệt quan trọng: 1fr không phải là 1/2 của chiều rộng container. Nó là 1/2 của không gian khả dụng sau khi các ràng buộc kích thước tối thiểu đã được thỏa mãn.


Chẩn đoán lỗi: lần theo min-width từng bước

Trước khi sửa, cần xác định phần tử nào đang đẩy chiều rộng ra. Có ba công cụ nhanh:

1. DevTools - Elements panel

Mở Chrome DevTools, chuyển sang chế độ responsive (Ctrl+Shift+M), chọn kích thước 375px (iPhone SE). Dùng Inspector click vào từng ô grid để xem kích thước thực tế trong box model. Nếu ô rộng hơn 375px / 2 = 187.5px trong khi bạn kỳ vọng đó là giới hạn, thì có vấn đề.

2. CSS tạm thời để highlight

Thêm đoạn này vào stylesheet trong khi debug (bài tập minh họa, không dùng production):

* {
  outline: 1px solid red;
}

Khi mọi phần tử đều có viền đỏ, bạn dễ thấy phần tử nào đang tràn ra ngoài container.

348178 Viền đỏ tạm thời giúp thấy ngay phần tử nào tràn khỏi grid

3. overflow-x: hidden trên body - bẫy phổ biến

Nhiều người thêm overflow-x: hidden vào body hoặc html để ẩn thanh cuộn. Cách này che triệu chứng, không sửa nguyên nhân. Nội dung vẫn tràn, chỉ là người dùng không cuộn được tới nó. Tệ hơn: trên iOS Safari, overflow-x: hidden đôi khi không hoạt động đúng như mong đợi.


Sửa lỗi với minmax() và min-width: 0

minmax(0, 1fr) thay cho 1fr

minmax(min, max) cho phép đặt rõ giới hạn dưới cho kích thước cột. Khi bạn viết:

.grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

Trình duyệt hiểu: cột tối thiểu là 0px, tối đa là 1fr. Điều này ghi đè min-width: auto ngầm định - cột không còn bị ràng buộc phải chứa hết nội dung mà không co lại. Nội dung bên trong có thể bị cắt hoặc xuống dòng, nhưng layout không bị vỡ.

348179 minmax(0, 1fr) cho phép cột co lại thay vì đẩy layout vỡ

min-width: 0 trên phần tử con

Cách thứ hai: giữ nguyên 1fr trên grid container, nhưng đặt min-width: 0 lên trực tiếp phần tử con của grid:

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.grid > * {
  min-width: 0;
}

Kết quả tương đương. Lý do cách này hoạt động: min-width: 0 ghi đè min-width: auto của phần tử con, cho phép nó nhỏ hơn kích thước nội dung của mình (và nội dung sẽ bị overflow hoặc wrap thay vì đẩy phần tử ra ngoài).

Nên dùng cách nào? Cả hai đều hợp lệ. Nếu bạn kiểm soát CSS của grid container, dùng minmax(0, 1fr) vì nó tường minh hơn. Nếu grid container đến từ thư viện bên ngoài, dùng min-width: 0 trên con là lựa chọn ít xâm phạm hơn.


Bài tập: sửa card grid có tiêu đề dài và ảnh

Dưới đây là bài tập minh họa - đây là ví dụ giả định để luyện tập chẩn đoán và sửa lỗi.

Markup ban đầu (có lỗi)

<div class="card-grid">
  <div class="card">
    <img src="photo.jpg" alt="Ảnh sản phẩm">
    <h3>Tiêu đề rất dài không có dấu cách nào để xuống dòng AAABBBCCCDDDEEE</h3>
    <p>Mô tả ngắn</p>
  </div>
  <div class="card">
    <img src="photo2.jpg" alt="Ảnh khác">
    <h3>Tiêu đề bình thường</h3>
    <p>Mô tả ngắn</p>
  </div>
</div>
/* CSS ban đầu - có lỗi */
.card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.card img {
  width: 100%;
}

Lỗi ở đây: chuỗi AAABBBCCCDDDEEE không có khoảng trắng nên min-width: auto của .card phải đủ rộng để hiển thị toàn bộ chuỗi đó. Ảnh không có max-width ràng buộc bổ sung (dù width: 100% hoạt động, ảnh vẫn có intrinsic size có thể ảnh hưởng trong một số trường hợp).

348180 Chuỗi không có khoảng trắng buộc cột phải mở rộng

CSS đã sửa

.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
}

.card img {
  width: 100%;
  max-width: 100%; /* phòng hờ */
  height: auto;
}

.card h3 {
  overflow-wrap: break-word; /* xuống dòng khi không có khoảng trắng */
  word-break: break-word;    /* fallback cho trình duyệt cũ */
}

Kiểm thử ở ba độ rộng

Sau khi sửa, mở DevTools và kiểm tra theo thứ tự:

Độ rộng Kỳ vọng Cách kiểm tra
1280px (desktop) Hai cột đều nhau, nội dung hiển thị đầy đủ Mắt thường
768px (tablet) Hai cột vẫn đều nhau, tiêu đề dài xuống dòng DevTools responsive mode
375px (mobile) Không có thanh cuộn ngang, tiêu đề wrap DevTools + kéo thanh cuộn để xác nhận không còn overflow

Nếu ở 375px vẫn còn thanh cuộn: dùng bước chẩn đoán từ section trước - thêm outline: 1px solid red tạm thời để tìm phần tử còn tràn.


Những lỗi liên quan thường gặp

gap cộng vào kích thước cột không như kỳ vọng

Nếu bạn dùng grid-template-columns: 1fr 1fr và gap: 24px, tổng chiều rộng grid là (chiều rộng container) - 24px chia đều cho hai cột. gap không gây tràn vì nó được trừ ra trước khi tính fr. Tuy nhiên nếu bạn dùng padding hoặc margin bên trong cột thêm vào mà box-sizing chưa đặt, kích thước có thể bị cộng ra ngoài.

Đảm bảo bạn có:

*, *::before, *::after {
  box-sizing: border-box;
}

Đây là reset phổ biến và thường có sẵn trong các framework CSS, nhưng nếu bạn đang viết CSS thuần từ đầu, dễ quên.

Ảnh không có height: auto

.card img {
  width: 100%; /* đúng */
  /* thiếu height: auto -> ảnh có thể bị méo */
}

Thêm height: auto để ảnh giữ tỉ lệ khi co lại.

grid-template-columns cứng với px

/* Tránh trên mobile */
grid-template-columns: 200px 200px;

Nếu viewport nhỏ hơn 400px + gap, lưới sẽ tràn. Dùng fr hoặc minmax() thay cho px cứng, hoặc kết hợp với media query để chuyển sang một cột:

.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

@media (max-width: 480px) {
  .card-grid {
    grid-template-columns: 1fr;
  }
}

348181 Media query chuyển sang một cột khi viewport nhỏ hơn 480px

Muốn tìm hiểu sâu hơn về Grid và layout responsive, khóa HTML CSS từ Zero đến Hero của F8 có phần thực hành xây dựng layout thực tế, miễn phí.


Tóm tắt: trước và sau khi sửa

Bảng dưới so sánh trực tiếp hành vi của hai cách viết phổ biến (dữ liệu từ đặc tả CSS Grid và hành vi trình duyệt chuẩn):

Thuộc tính 1fr minmax(0, 1fr)
min-width mặc định auto (theo nội dung) 0 (có thể co về 0)
Cột bị tràn khi nội dung rộng? Có Không
Nội dung dài không có khoảng trắng Đẩy cột ra ngoài Overflow trong ô, có thể dùng overflow-wrap
Ảnh không có max-width Có thể gây tràn Ít bị ảnh hưởng hơn
Tương thích trình duyệt Tốt Tốt (CSS Grid Level 1)

Các bước sửa lỗi theo thứ tự ưu tiên:

  1. Đổi 1fr sang minmax(0, 1fr) trên grid-template-columns
  2. Thêm overflow-wrap: break-word cho text dài
  3. Đảm bảo ảnh có width: 100%; height: auto; max-width: 100%
  4. Xác nhận box-sizing: border-box đã được đặt toàn cục
  5. Kiểm thử ở 375px, 768px, 1280px

348182 Năm bước, làm theo thứ tự từ trên xuống


Nguồn tham khảo:

Kiểm tra tài liệu chính thức MDN và web.dev để xác nhận hành vi với phiên bản trình duyệt hiện tại của bạn.

0

Trang web dùng cookie để hoạt động và đo hiệu quả quảng cáo. Xem chính sách bảo mật