Menu

z-index trong CSS: thứ tự xếp lớp và stacking context

z-index quyết định phần tử nào được vẽ nằm trên khi các phần tử chồng lên nhau. Tìm hiểu z-index hoạt động với những phần tử nào, cách stacking context gom nhóm phần tử khiến z-index 9999 vẫn có thể thua, thứ gì tạo ra stacking context và cách sửa z-index không hoạt động.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

z-index làm gì

Khi các phần tử chồng lên nhau, z-index quyết định phần tử nào được vẽ ở trước. Số lớn hơn sẽ thắng. Nó hoạt động với các phần tử được định vị (mọi position trừ static) và với flex item, grid item:

Khi không có z-index, phần tử đứng sau trong HTML được vẽ đè lên phần tử đứng trước, nên C sẽ che B và B sẽ che A. Các giá trị z-index đảo ngược điều đó: A ở trước, rồi tới C, rồi tới B.

Các giá trị

Giá trịÝ nghĩa
auto (mặc định)Xếp ở mức 0 cùng các phần tử anh em, và không tạo stacking context
0Mức 0, nhưng có tạo stacking context
Số nguyên dươngNằm trước các giá trị thấp hơn
Số nguyên âmNằm sau mức 0, và nằm sau cả nội dung của phần tử cha nếu phần tử cha tạo ra stacking context

Chỉ số nguyên là hợp lệ: z-index: 1.5 bị bỏ qua.

Vì sao z-index 9999 vẫn có thể thua: stacking context

Các giá trị z-index không được so sánh trên toàn trang. Các phần tử được gom vào những stacking context (ngữ cảnh xếp lớp), và z-index chỉ sắp thứ tự các phần tử trong cùng một context. Sau đó cả context được đặt như một lớp duy nhất, theo z-index của phần tử đã tạo ra nó.

Bên dưới, menu thả xuống màu xanh dương có z-index: 9999, nhưng nó nằm trong một header có z-index: 1. Banner màu xanh lá, anh em của header đó, có z-index: 2. Cả lớp header, tính luôn menu thả xuống, nằm bên dưới banner:

Nhấn nút. Với z-index: auto, header không còn tạo stacking context, nên 9999 của menu thả xuống được so trực tiếp với 2 của banner, và menu thả xuống nổi lên trước. Nâng menu thả xuống lên 99999 sẽ chẳng bao giờ giúp được gì.

Thứ gì tạo ra stacking context

Bất kỳ điều nào dưới đây cũng biến một phần tử thành stacking context riêng, giam z-index của mọi thứ bên trong nó:

Thuộc tínhĐiều kiện
position: relative hoặc absoluteCó z-index khác auto
position: fixed hoặc stickyLuôn luôn
Flex item hoặc grid itemCó z-index khác auto
opacityNhỏ hơn 1
transform, scale, rotate, translateGiá trị bất kỳ khác none
filter, backdrop-filterGiá trị bất kỳ khác none
isolationisolate
mix-blend-modeGiá trị bất kỳ khác normal
will-changeChỉ định bất kỳ thuộc tính nào ở trên
containlayout hoặc paint (và strict, content)

Gây bất ngờ nhất là opacity và transform: thêm hiệu ứng mờ dần hay animation khi hover cho phần tử cha có thể bỗng dưng đẩy menu thả xuống của nó ra sau khu vực kế tiếp.

z-index: 10 của tooltip lẽ ra phải đưa nó lên trên cột tiếp theo. Nhưng opacity: 0.99 biến cột đầu tiên thành stacking context với z-index auto (mức 0), và cột thứ hai đứng sau trong HTML ở cùng mức, nên nó được vẽ đè lên toàn bộ cột đầu tiên, tính luôn tooltip. Xóa dòng opacity là tooltip lại nằm phía trước.

z-index âm

z-index âm đặt phần tử nằm sau các phần tử anh em. Nếu phần tử cha không tạo stacking context, nó thậm chí có thể lùi ra sau cả nền của phần tử cha, một bất ngờ thường gặp. isolation: isolate trên phần tử cha tạo ra context mà không có hiệu ứng hiển thị nào khác, và giữ phần tử con có z-index âm ở bên trong:

Không có isolation, khung màu hổ phách lùi ra sau nền xám của chính nút, nên chỉ lộ ra phần mép. Với isolation: isolate, khung nằm trong stacking context của nút: trên nền của nút, dưới phần chữ.

Một thang z-index

Chọn vài lớp có tên cho cả trang web thay vì tăng số liên tục:

:root {
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-overlay: 30;
  --z-modal: 40;
  --z-toast: 50;
}
.modal { position: fixed; z-index: var(--z-modal); }

Phần lớn lỗi xếp lớp là lỗi stacking context, không phải lỗi con số, nên một thang nhỏ cộng với isolation: isolate trên các component dùng z-index bên trong là đã giải quyết được nhiều. z-index cần định vị mới hoạt động; trang position giải thích phần đó.

Lỗi thường gặp

  • z-index trên phần tử static. Thêm position: relative (hoặc biến nó thành flex item hay grid item).
  • Leo thang lên 9999. Nếu phần tử cha tạo ra stacking context nằm dưới đối thủ, không giá trị nào của phần tử con thắng được. Hãy tìm ra context đó.
  • Một opacity, transform hay filter mới trên phần tử cha bỗng dưng giam menu thả xuống của nó.
  • Giá trị thập phân. z-index: 1.5 không hợp lệ.
  • Tưởng z-index: 0 và auto hoạt động giống nhau. 0 tạo stacking context; auto thì không.
  • Modal nằm sâu trong các component lồng nhau. Hãy render chúng gần cuối <body> (hoặc dùng top layer của phần tử <dialog>), để không context tổ tiên nào giam được chúng.

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

z-index dùng để làm gì?

Nó đặt thứ tự xếp lớp của các phần tử chồng lên nhau: trong cùng một stacking context, giá trị cao hơn được vẽ nằm trước giá trị thấp hơn. Mặc định là auto, và giá trị là số nguyên, kể cả số âm.

Vì sao z-index không hoạt động?

Hoặc phần tử chưa được định vị (z-index cần position khác static, hoặc phần tử phải là con của flex hay grid), hoặc nó nằm trong một phần tử cha tạo ra stacking context có thứ tự thấp hơn. Một phần tử con không bao giờ vượt lên trên được phần tử mà stacking context của nó nằm bên dưới, dù z-index của chính nó là bao nhiêu.

Stacking context là gì?

Là một nhóm phần tử được xếp chồng cùng nhau như một lớp duy nhất. Bên trong, các phần tử con được sắp theo z-index của chúng; bên ngoài, cả nhóm được đặt theo z-index của phần tử đã tạo ra nó.

Thứ gì tạo ra stacking context?

Có thể kể: phần tử gốc, phần tử được định vị có z-index khác auto, position: fixed hoặc sticky, phần tử con của flex hay grid có z-index, opacity nhỏ hơn 1, transform, filter, isolation: isolate, và will-change trên các thuộc tính đó.

z-index có hoạt động khi không có position không?

Không, với phần tử block hay inline thông thường: position: static bỏ qua nó. Nhưng nó vẫn hoạt động với flex item và grid item mà không cần position.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu