Menu

Flexbox trong CSS: display flex, trục, căn chỉnh và flex

Flexbox xếp các phần tử thành một hàng hoặc một cột và điều khiển cách chúng chia sẻ không gian và thẳng hàng với nhau. Tìm hiểu display: flex, trục chính và trục phụ, mọi thuộc tính của container, flex-grow, flex-shrink và flex-basis, với một playground trực tiếp đo xem từng item nằm ở đâu.

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.

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 định stretch, 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ày justify-content di chuyển item theo chiều dọc còn align-items theo 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-directionTrục chínhTrục phụjustify-content di chuyển itemalign-items di chuyển item
row (mặc định)ngang, theo thứ tự đọcdọctheo chiều ngangtheo chiều dọc
row-reversengang, đảo ngượcdọctheo chiều ngangtheo chiều dọc
columndọc, từ trên xuống dướingangtheo chiều dọctheo chiều ngang
column-reversedọc, từ dưới lên trênngangtheo chiều dọctheo 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ínhTác dụngMặc định
flex-directionHàng hay cột, và theo hướng nàorow
flex-wrapItem có xuống dòng mới khi hết chỗ hay khôngnowrap
flex-flowViết tắt của flex-direction và flex-wraprow nowrap
justify-contentVị trí và khoảng cách dọc theo trục chínhnormal (hoạt động như flex-start)
align-itemsVị trí của item dọc theo trục phụnormal (hoạt động như stretch)
align-contentKhoảng cách giữa các dòng khi item xuống dòngnormal
gapKhoả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 (auto nghĩ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ư. 0 nghĩa là không bao giờ giãn.
  • flex-shrink: item nhường lại bao nhiêu khi không đủ chỗ. 0 nghĩa là không bao giờ co.

Cách viết tắt flex đặt cả ba cùng lúc:

Bạn viếtNghĩa làKết quả
flex: initial (mặc định)0 1 autoKích thước theo nội dung, có thể co, không bao giờ giãn
flex: 11 1 0%Bắt đầu từ 0 và lấy một phần không gian bằng nhau
flex: auto1 1 autoKích thước theo nội dung cộng thêm một phần không gian còn dư
flex: none0 0 autoĐúng bằng nội dung hoặc width, không giãn cũng không co
flex: 22 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: flex trên item thay vì phần tử cha. Container nhận display: flex; các con của nó là item.
  • Mong align-items di 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êm min-width: 0 cho item cần co.
  • Dùng flex: 1 rồi thắc mắc vì sao chiều rộng khác nhau. Các item có flex: 1 chia đề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: 0 cũng sửa được trường hợp này.
  • Dùng margin giữa các item. Dùng gap trê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.

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