Bỏ qua tới nội dung
Performance· ·9 phút đọc

Dùng aspect-ratio giữ chỗ cho ảnh, chặn nhảy layout (CLS)

Nguyen Hien
Dùng aspect-ratio giữ chỗ cho ảnh, chặn nhảy layout (CLS)
Cỡ chữ

Một tấm ảnh chưa khai báo kích thước là thủ phạm kinh điển của CLS (Cumulative Layout Shift — điểm tích luỹ độ dịch chuyển bố cục). Khi ảnh tải xong, trình duyệt mới biết nó cao bao nhiêu, chèn vào và đẩy toàn bộ chữ phía dưới nhảy xuống. Người đọc đang dò mắt thì mất dòng, đôi khi bấm nhầm nút. Giữ chỗ trước cho ảnh là cách gọn nhất để chặn cú nhảy đó.

Bài này đi sâu đúng một kỹ thuật: dùng thuộc tính CSS aspect-ratio cùng cặp width/height của HTML để trình duyệt biết trước phải chừa bao nhiêu chỗ. Nếu bạn muốn xem toàn cảnh mọi nguyên nhân gây CLS (font hoán đổi, quảng cáo chèn động, nút “đọc thêm”…), đọc bài cách sửa Cumulative Layout Shift. Ở đây chỉ tập trung vào khung hình.

Vì sao thiếu kích thước thì layout nhảy

Trình duyệt dựng bố cục theo từng dòng, từ trên xuống, ngay khi đọc HTML — trước khi ảnh kịp tải về. Nếu thẻ <img> không nói cao bao nhiêu, trình duyệt mặc định chừa chiều cao bằng 0, dồn mọi thứ phía dưới lên sát. Vài trăm mili-giây sau ảnh tải xong, nó “bung” ra đúng chiều cao thật và xô mọi nội dung bên dưới đi một đoạn. Đoạn dịch chuyển đó chính là phần điểm CLS bị cộng dồn.

Ngưỡng “Tốt” của CLS theo Google là ≤ 0.1, đo ở phân vị 75 (p75 — 75% lượt tải trang phải đạt). CLS là một trong ba Core Web Vitals chính thức, cùng với LCP ≤ 2.5s và INP (Interaction to Next Paint — thời gian phản hồi tới khung hình kế tiếp) ≤ 200ms. Một vài tấm ảnh không khai kích thước là đủ để đẩy CLS vượt 0.1.

Sơ đồ giải thích vì sao ảnh thiếu kích thước làm trình duyệt đẩy nhảy layout gây CLS
Thiếu chiều cao báo trước, trình duyệt đặt sai chỗ rồi phải đẩy nội dung khi ảnh tải xong.

Cách 1 — Khai width và height trên thẻ HTML

Cách kinh điển và bền nhất cho ảnh nội dung: đặt thẳng thuộc tính widthheight bằng kích thước pixel thật của tệp gốc, rồi để CSS co giãn.

<img src="bia-bai-viet.webp" width="1600" height="900" alt="..." />
img {
  height: auto;   /* để ảnh co theo chiều rộng, giữ đúng tỉ lệ */
  max-width: 100%;
}

Từ 2019, các trình duyệt hiện đại đọc cặp width/height này như một “gợi ý trình bày” (presentational hint) và tự suy ra aspect-ratio: auto 1600 / 900. Nhờ vậy trình duyệt giữ đúng chỗ ngay từ lần dựng đầu, trước khi tệp ảnh về.

Từ khoá quan trọng là auto: trình duyệt dùng tỉ lệ từ thuộc tính để giữ chỗ tạm, nhưng khi ảnh tải xong sẽ chuyển sang tỉ lệ thật của ảnh. Nếu bạn lỡ ghi sai kích thước, nó tự sửa lại đúng — đây là điểm cách này an toàn hơn ghi cứng tỉ lệ trong CSS.

Bẫy lớn nhất: width: auto trong CSS

Một dòng CSS vô tình có thể vô hiệu hoá toàn bộ. Nếu trong stylesheet bạn đặt width: auto (hoặc height một con số cố định mà thiếu phía còn lại), trình duyệt vứt bỏ tỉ lệ suy ra từ thuộc tính HTML và lại không biết phải chừa bao nhiêu chỗ. Quy tắc an toàn: chỉ ghi đè một chiều và để chiều kia là auto — phổ biến nhất là height: auto kèm max-width: 100%.

Cách 2 — Dùng thuộc tính CSS aspect-ratio

Khi không có sẵn kích thước pixel (ảnh nền từ CMS, ô thumbnail co cứng theo lưới, khung video nhúng, placeholder cho nội dung tải sau), hãy ép tỉ lệ thẳng bằng CSS:

.thumb {
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;   /* tránh méo nếu ảnh không đúng tỉ lệ */
}

Khi trình duyệt gặp một phần tử có aspect-ratio và một chiều để auto, nó tính ra chiều còn thiếu ngay trong lần render đầu — trước cả khi mạng kịp tải gì. Khác với cách 1, ở đây tỉ lệ là cố định: ảnh nào không khớp 16/9 sẽ bị kéo méo, nên gần như luôn cần kèm object-fit: cover để cắt gọn thay vì méo.

Đừng quên video và iframe

CLS không chỉ đến từ ảnh. Video nhúng, iframe bản đồ, khung quảng cáo, widget mạng xã hội cũng “bung” ra khi tải xong. Bọc chúng trong khung có tỉ lệ cố định để giữ chỗ:

.video-wrap {
  aspect-ratio: 16 / 9;
}
.video-wrap iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

Chọn cách nào?

Tình huốngNên dùng
Ảnh nội dung trong bài, biết kích thước tệpThuộc tính HTML width/height + height: auto
Ô thumbnail, ảnh nền, lưới đồng cỡCSS aspect-ratio + object-fit: cover
Video / iframe nhúngKhung bọc có aspect-ratio
Ảnh responsive (srcset, picture)Vẫn khai width/height trên <img> — xem mục dưới

Hai cách không loại trừ nhau. Cách dùng thuộc tính HTML thực chất cũng đang đặt aspect-ratio ngầm cho trình duyệt — chỉ là an toàn hơn nhờ từ khoá auto tự sửa khi ảnh thật về.

Ảnh responsive cũng cần khai kích thước

Nhiều người tưởng khi đã dùng srcset nhiều cỡ thì khỏi cần width/height nữa. Sai. Trình duyệt vẫn lấy tỉ lệ từ cặp width/height trên thẻ <img> để giữ chỗ, dù cuối cùng nó chọn tải biến thể nào. Miễn các biến thể trong srcset cùng tỉ lệ, bạn chỉ cần khai một cặp width/height bất kỳ là đủ. Chi tiết về cách dựng srcsetsizes cho đúng, xem bài ảnh responsive với srcset và sizes.

Một lưu ý thực chiến từ chính việc vận hành web22.dev: khi dùng phần tử <picture> với các <source> có tỉ lệ khác nhau (ví dụ ảnh ngang cho desktop, ảnh vuông cho mobile), một số trình duyệt như Firefox vẫn lấy kích thước của thẻ <img> bên trong thay vì source đang hoạt động — có thể sinh dịch chuyển nhỏ. Nếu cần đổi hẳn tỉ lệ theo breakpoint, hãy đặt aspect-ratio riêng cho từng trường hợp bằng media query thay vì dựa vào thuộc tính.

Checklist nhanh khai kích thước ảnh video iframe bằng width height và aspect-ratio để chặn CLS dưới 0.1
Mỗi khung hình đều cần báo trước tỉ lệ để trình duyệt giữ chỗ sẵn.

Checklist nhanh chặn CLS từ khung hình

  1. Mọi <img> nội dung có đủ widthheight bằng pixel thật.
  2. CSS ảnh dùng height: auto; max-width: 100% — không có width: auto phá tỉ lệ.
  3. Thumbnail và ảnh nền dùng aspect-ratio + object-fit: cover.
  4. Mọi video/iframe nhúng nằm trong khung bọc có tỉ lệ cố định.
  5. Đo lại bằng Lighthouse hoặc dữ liệu thực địa (field data) để xác nhận CLS ≤ 0.1 ở p75.

Câu hỏi thường gặp

Khai width/height có làm ảnh hiển thị méo không?

Không, nếu bạn để height: auto trong CSS. Cặp số trên HTML chỉ dùng để suy ra tỉ lệ giữ chỗ; CSS vẫn quyết định kích thước hiển thị thật.

Đã dùng lazy-load ảnh thì còn cần aspect-ratio không?

Càng cần. Ảnh lazy-load (tải trễ) tải xuống muộn hơn, nên nếu không giữ chỗ trước thì cú nhảy còn dễ xảy ra hơn khi người đọc cuộn tới.

aspect-ratio hỗ trợ rộng chưa?

Thuộc tính CSS aspect-ratio đã được mọi trình duyệt phổ biến hỗ trợ từ 2021, dùng an toàn cho dự án mới. Với trình duyệt rất cũ, cặp width/height trên HTML là phương án dự phòng luôn chạy.

Nếu bạn muốn rà soát toàn bộ Core Web Vitals và dọn sạch các điểm dịch chuyển bố cục còn sót, Web22 nhận dịch vụ tối ưu tốc độ website theo hướng đo trước, sửa đúng chỗ.

Đọc tiếp

Bài viết
cùng chủ đề.

Tất cả bài viết