display: flex làm gì
Đặt display: flex cho phần tử cha, các phần tử con trực tiếp sẽ xếp thành một hàng, nằm cạnh nhau và giãn ra cao bằng nhau. Chỉ một khai báo đó thay được float, inline-block và phần lớn các mẹo định vị khi xếp một hàng phần tử.
Trong hộp đầu tiên, mỗi div chiếm trọn một dòng. Trong hộp thứ hai, ba item nằm trên cùng một hàng, mỗi item chỉ rộng bằng nội dung của nó, và One với Three giãn ra cao bằng Two. Chỉ phần tử cha thay đổi: display: flex đặt trên container, không bao giờ đặt trên các item.
Playground flexbox
Chọn giá trị và xem các item di chuyển. Dòng code dưới hộp là CSS của những gì bạn đang thấy, còn danh sách bên dưới là vị trí trình duyệt thực sự đặt từng item, đo bằng getBoundingClientRect().
Thử những điều sau:
justify-content: space-between: item 1 nằm ở x 0 và item 3 chạm mép bên kia.align-items: center: ba item giữ nguyên chiều cao riêng và tâm của chúng thẳng hàng. Với giá trị mặc địnhstretch, item 1 không có chiều cao riêng nên lấp đầy toàn bộ 170px của container.flex-direction: column: lúc nàyjustify-contentdi chuyển item theo chiều dọc cònalign-itemstheo chiều ngang. Hai thuộc tính này đi theo trục, không theo màn hình.
Trục chính và trục phụ
Mọi flex container đều có hai trục. Trục chính (main axis) là hướng các item nối tiếp nhau, do flex-direction quyết định. Trục phụ (cross axis) chạy vuông góc với nó.
flex-direction | Trục chính | Trục phụ | justify-content di chuyển item | align-items di chuyển item |
|---|---|---|---|---|
row (mặc định) | ngang, theo thứ tự đọc | dọc | theo chiều ngang | theo chiều dọc |
row-reverse | ngang, đảo ngược | dọc | theo chiều ngang | theo chiều dọc |
column | dọc, từ trên xuống dưới | ngang | theo chiều dọc | theo chiều ngang |
column-reverse | dọc, từ dưới lên trên | ngang | theo chiều dọc | theo chiều ngang |
Trên trang viết từ phải sang trái (dir="rtl"), row bắt đầu từ mép phải, vì trục chính đi theo hướng viết.
Thuộc tính của container
Các thuộc tính này đặt trên phần tử có display: flex:
| Thuộc tính | Tác dụng | Mặc định |
|---|---|---|
flex-direction | Hàng hay cột, và theo hướng nào | row |
flex-wrap | Item có xuống dòng mới khi hết chỗ hay không | nowrap |
flex-flow | Viết tắt của flex-direction và flex-wrap | row nowrap |
justify-content | Vị trí và khoảng cách dọc theo trục chính | normal (hoạt động như flex-start) |
align-items | Vị trí của item dọc theo trục phụ | normal (hoạt động như stretch) |
align-content | Khoảng cách giữa các dòng khi item xuống dòng | normal |
gap | Khoảng cách giữa các item (row-gap, column-gap) | 0 |
justify-content và align-items đều có trang riêng với mọi giá trị: justify-content và align-items. Xuống dòng và hướng được trình bày ở trang flex-wrap.
Thuộc tính của item: flex-grow, flex-shrink, flex-basis
Các thuộc tính này đặt trên phần tử con và quyết định cách chúng chia chiều rộng của container:
flex-basis: kích thước ban đầu của item trước khi chia phần không gian trống (autonghĩa là "lấy width hoặc nội dung của tôi").flex-grow: item lấy bao nhiêu phần của không gian còn dư.0nghĩa là không bao giờ giãn.flex-shrink: item nhường lại bao nhiêu khi không đủ chỗ.0nghĩa là không bao giờ co.
Cách viết tắt flex đặt cả ba cùng lúc:
| Bạn viết | Nghĩa là | Kết quả |
|---|---|---|
flex: initial (mặc định) | 0 1 auto | Kích thước theo nội dung, có thể co, không bao giờ giãn |
flex: 1 | 1 1 0% | Bắt đầu từ 0 và lấy một phần không gian bằng nhau |
flex: auto | 1 1 auto | Kích thước theo nội dung cộng thêm một phần không gian còn dư |
flex: none | 0 0 auto | Đúng bằng nội dung hoặc width, không giãn cũng không co |
flex: 2 | 2 1 0% | Gấp đôi phần của một item có flex: 1 |
Mỗi hàng rộng 480px bên trong padding, và hai khoảng gap 8px để lại 464px cho các item. Chiều rộng được in ra cho thấy cách chia: khoảng 155px mỗi item ở hàng đầu, 116 + 232 + 116 ở hàng thứ hai (item flex: 2 nhận gấp đôi phần của mỗi item bên cạnh), và 100 + 372 ở hàng thứ ba, nơi item flex: none giữ nguyên chiều rộng còn item kia lấy toàn bộ phần còn lại.
Bố cục thực tế với flexbox
Một header có logo ở đầu và các liên kết ở cuối là bố cục flex phổ biến nhất. margin-inline-start: auto trên một item nuốt toàn bộ không gian trống phía trước nó, đẩy nó cùng mọi thứ phía sau về cuối:
align-items: center làm phần chữ logo to hơn thẳng hàng với các liên kết, còn gap tạo khoảng cách giữa các liên kết mà không cần margin rồi lại phải bỏ margin ở liên kết cuối.
Một mẫu thường ngày khác là hàng media: một biểu tượng có kích thước cố định và phần chữ chiếm phần còn lại, tự xuống dòng trong cột của riêng nó.
flex: none là sự đảm bảo. Flex item mặc định có thể co, nên nếu bạn bỏ nó khỏi hình tròn và đồng thời bỏ flex: 1 khỏi phần chữ, đoạn văn sẽ ép hình tròn chỉ còn rộng khoảng 23px: thành hình bầu dục. Để căn giữa một thứ trong hộp, flexbox chỉ cần hai dòng (justify-content: center; align-items: center); trang căn giữa div so sánh cách này với các cách khác.
order và khả năng tiếp cận
Thuộc tính order và các hướng -reverse thay đổi vị trí item hiển thị, không thay đổi vị trí của chúng trong HTML. Trình đọc màn hình và phím Tab vẫn đi theo thứ tự HTML, nên người dùng bàn phím có thể nhảy từ liên kết đầu tiên trên màn hình sang liên kết cuối cùng. Chỉ dùng chúng để trang trí; khi thứ tự mang ý nghĩa, hãy sửa HTML.
Flexbox hay grid
Flexbox làm việc theo một chiều tại một thời điểm: các item trong hàng tự lấy kích thước theo nội dung, và dòng thứ hai (khi xuống dòng) không thẳng hàng với dòng đầu. Grid làm việc theo hai chiều: bạn định nghĩa cột và hàng, các item khớp vào đó. Thanh điều hướng, thanh công cụ hay một hàng form là flexbox; thư viện ảnh hay trang có thanh bên là grid.
Lỗi thường gặp
- Đặt
display: flextrên item thay vì phần tử cha. Container nhậndisplay: flex; các con của nó là item. - Mong
align-itemsdi chuyển item theo chiều dọc khi container không có chiều cao. Nếu container chỉ cao bằng item cao nhất, sẽ không còn chỗ để di chuyển. Hãy cho container một chiều cao, còn nếu không thì thuộc tính vẫn đang hoạt động, chỉ là không có gì để làm. - Chữ tràn ra ngoài thay vì co lại. Flex item có
min-width: auto. Thêmmin-width: 0cho item cần co. - Dùng
flex: 1rồi thắc mắc vì sao chiều rộng khác nhau. Các item cóflex: 1chia đều không gian, nhưng item có nội dung không vừa (một URL dài, một ảnh) sẽ không co nhỏ hơn nội dung đó.min-width: 0cũng sửa được trường hợp này. - Dùng margin giữa các item. Dùng
gaptrên container; nó chỉ nằm giữa các item. - Lồng flex không cần thiết. Chỉ con trực tiếp mới là flex item. Các phần tử cháu theo bố cục bình thường, trừ khi chính phần tử cha của chúng cũng có
display: flex.
Câu hỏi thường gặp
display: flex dùng để làm gì?
Nó biến một phần tử thành flex container. Các phần tử con trực tiếp trở thành flex item và mặc định được xếp thành một hàng, nằm cạnh nhau, giãn ra cao bằng nhau, còn các thuộc tính như justify-content, align-items và gap quyết định cách chúng chia sẻ không gian.
flex: 1 có nghĩa là gì?
flex: 1 là cách viết tắt của flex: 1 1 0%: item được phép giãn (flex-grow: 1), được phép co (flex-shrink: 1) và bắt đầu từ kích thước bằng 0 (flex-basis: 0%). Các item cùng có flex: 1 chia đều phần không gian trống, nên cuối cùng rộng bằng nhau, trừ khi nội dung của chúng không vừa.
justify-content và align-items khác nhau thế nào?
justify-content đặt các item dọc theo trục chính (theo chiều ngang khi là hàng), còn align-items đặt chúng dọc theo trục phụ (theo chiều dọc khi là hàng). Với flex-direction: column hai trục đổi chỗ cho nhau, nên justify-content chuyển sang theo chiều dọc.
Nên dùng flexbox hay grid?
Dùng flexbox cho một hàng hoặc một cột mà các item tự lấy kích thước theo nội dung: thanh điều hướng, nhóm nút, nhãn đặt cạnh ô nhập. Dùng grid khi bạn cần các hàng và cột thẳng hàng với nhau, như lưới thẻ hoặc bố cục trang.
Vì sao flex item tràn ra khỏi container?
Flex item mặc định có min-width: auto, nên nó không chịu co nhỏ hơn chiều rộng của từ dài nhất hay phần tử con rộng nhất. Đặt min-width: 0 (hoặc overflow: hidden) cho item để nó co được, chẳng hạn để text-overflow: ellipsis hoạt động.
Flexbox có dùng được với phần tử inline như span không?
Được. Mọi phần tử có display: flex đều là flex container, và các con của nó trở thành flex item bất kể giá trị display riêng, nên các con <span> thôi là inline và nhận width, height, margin như block.