3.3. Dàn trang giao diện với Flexbox

Nào, đã đến lúc thực hiện dàn trang cho trang web của chúng ta rồi. Cho đến lúc này, kỹ năng duy nhất còn thiếu chính là cách sắp đặt các mảng khối vào những vị trí mà chúng ta mong muốn : tiêu đề ở trên đầu trang, danh mục nằm cạnh bên, vv… Nắm trong tay mảnh ghép này và chúng ta sẽ sẵn sàng để hoàn thiện bức tranh trang web mà chúng ta vẫn ao ước.

Qua thời gian, chúng ta từng chứng kiến nhiều cách thức khác nhau để lên trang cho trang web :

  • Hồi đầu, các lập trình viên thường lên trang bằng cách sử dụng bảng HTML
  • Thời kỳ tiếp theo, người ta có thói quen dàn trang nhờ thuộc tính float.
  • Cách thức trên còn tồn tại nhược điểm nên người ta dần thích cách thức thực dụng hơn là sử dụng display : inline-block;.
  • Ngày nay, chúng ta đang biết đến 1 giải pháp tuyệt vời, đó là Flexbox ! Nó cho phép chúng ta thực hiện hầu như tất cả các ý tưởng sắp xếp mà chúng ta có thể nghĩ ra. Đây là cách dàn trang mà tôi khuyên các bạn nên sử dụng nếu các bạn đang bắt đầu xây dựng 1 trang web mới. Tính năng này đã được tất cả các trình duyệt hiện đại chấp nhận và hỗ trợ nên chúng ta không cần lo lắng gì cả.
Một khung chứa, nhiều thành phần

Nguyên lý của phương thức dàn trang sử dụng Flexbox rất đơn giản : chúng ta tạo ra 1 khung chứa và thêm vào bên trong đó nhiều đối tượng thành phần. Chúng ta có thể hiểu đơn giản như 1 chiếc hộp với nhiều tầng và mỗi tầng có nhiều ngăn vậy.

Trong cùng trang web, chúng ta hoàn toàn có thể có nhiều khung chứa. Các bạn chính là người quyết định tạo ra bao nhiêu khung chứa mà các bạn cần để hoàn thành mục đích sắp xếp của mình.

Hãy bắt đầu tìm hiểu cách thức hoạt động của khung chứa vạn năng này.

Khung chứa là 1 thẻ HTML và các đối tượng thành phần là các thẻ nằm bên trong thẻ này.

<div id="khungChua">
    <div class="thanhPhan">Thành phần 1</div>
    <div class="thanhPhan">Thành phần 2</div>
    <div class="thanhPhan">Thành phần 3</div>
</div>

Đến đây thì chưa có gì mới mẻ cả.

? Nếu tôi làm thế thì các thành phần của tôi sẽ xếp dọc từ trên xuống dưới đúng không? Đấy vốn là các khối hộp mà !

Đúng vậy. Nếu tôi thêm đường viền cho khung chứa, chỉnh kích thước và tô màu các thành phần khác nhau bên trong, chúng ta sẽ dễ dàng quan sát được chuyện gì đang diễn ra.

Không có gì mới, kết quả nhận được vẫn quen thuộc như những gì chúng ta đã biết.

Linh động, thật linh động !

Bây giờ, hãy cùng khám phá Flexbox. Tôi chỉ cần thêm vào 1 thuộc tính duy nhất, đó là flex, và tất cả sẽ thay đổi.

#khungChua {
    display: flex;
}

… Bùm, các khối hộp tự động xếp thành hàng ngang ngay cạnh nhau !

Điều chỉnh phương hướng

Flexbox cho phép chúng ta xếp các khối hộp theo phương mà chúng ta muốn. Bằng cách sử dụng thuộc tính flex-direction, chúng ta có thể xếp các khối hộp theo chiều dọc, chiều ngang hoặc thậm chí là đảo chiều. Thuộc tính này có thể nhận các giá trị dưới đây :

  • row : xếp thành hàng ngang (mặc định)
  • column : xếp theo hàng dọc
  • row-reverse : xếp hàng ngang nhưng theo thứ tự ngược với trình tự xuất hiện
  • column-reverse : xếp theo hàng dọc theo thứ tự ngược với trình tự xuất hiện
#khungChua {
    display: flex;
    flex-direction: column;
}

Ặc, chúng ta quay lại điểm đầu à ? Thế này có khác gì khi không dùng Flexbox đâu.

Đúng vậy. Thế nhưng các thành phần của chúng ta đã trở nên linh hoạt hơn nhiều so với lúc nãy. Chúng đã sở hữu hàng đống thuộc tính hữu dụng mà chúng ta sẽ cùng thảo luận trong phần bài học tiếp đây.

Chúng ta cũng có thể dùng thử giá trị cho phép hiển thị ngược chiều để xem kết quả nhận được là gì.

#khungChua {
    display: flex;
    flex-direction: column-reverse;
}

Các bạn thấy rồi chứ, các khối hộp được xếp theo trình tự hoàn toàn đảo ngược dù tôi hoàn toàn không thay đổi gì trong đoạn mã HTML.

Xuống hàng

Đôi khi chúng ta bắt gặp trường hợp các khối hộp cố hiển thị trên 1 hàng dù không đủ chỗ. Điều này có thể dẫn đến các lỗi hiển thị không đáng có. Để tránh hậu quả này, chúng ta có thể yêu cầu khối hộp đó hiển thị ở hàng dưới khi hàng bên trên không còn đủ chỗ.

Chúng ta sẽ cần dùng đến flex-wrap và các giá trị của nó.

  • now-wrap : không xuống hàng (mặc định)
  • wrap : xuống hàng khi không đủ chỗ để hiển thị
  • wrap-inverse : khi không đủ chỗ để hiển thị thì thay vì xuống hàng, chiếc hộp của chúng ta sẽ hiển thị ở hàng trên
#khungChua {
    display: flex;
    flex-wrap: wrap;
}

Căn chỉnh vị trí

Vậy là chúng ta đã biết là các khối hộp thành phần sẽ được xếp theo hàng ngang hoặc hàng dọc. Chúng ta gọi đó là trục chính. Khi 1 trục trở thành trục chính, vậy đương nhiên trục còn lại trở thành trục phụ. Vậy nên nếu trục dọc là trục chính thì trục ngang sẽ là trục phụ và ngược lại.

Tại sao lại nhắc đến tất cả những thứ này ? Bởi vì ngay sao đây, chúng ta sẽ cùng thảo luận cách mà chúng ta có thể căn chỉnh vị trí của các khối hộp theo trục chính và trục phụ.

Căn theo trục chính

Để bắt đầu, hãy xét trường hợp mặc định, đơn giản nhất là các thành phần xếp theo hàng ngang.

Để điều chỉnh kích thước nội dung các khối, chúng ta sẽ sử dụng thuộc tính justify-content với 1 trong các giá trị dưới đây :

  • flex-start : căn thẳng đầu dòng
  • flex-end : căn thẳng cuối dòng
  • center : căn giữa
  • space-between : các thành phần cách đều trên hàng (có các khoảng trống giữa các khối thành phần này)
  • space-around : tương tự, các thành phần vẫn cách đều trên hàng. Thêm vào đó chúng ta có cả khoảng trống ở 2 đầu khung chứa.
#khungChua {
    display: flex;
    justify-content: space-around;
}

Thật tốt nếu có hình minh họa kết quả chúng ta đạt được chứ nhỉ.

Các bạn thấy rồi chứ. Chỉ với 1 thuộc tính đơn giản mà chúng ta đã thực hiện được kha khá cách sắp xếp khác nhau.

Chúng ta cần chú ý là thuộc tính này cũng hoạt đông tương tự theo chiều dọc nếu trục dọc là trục chính của chúng ta.

#khungChua {
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 350px;
}

Căn theo trục phụ

Chúng ta đã biết nếu các thành phần được xếp theo hàng ngang thì trục dọc sẽ trở thành trục phụ và ngược lại.

Thuộc tính cần sử dụng lần này là align-items. Chúng ta có thể căn chỉnh các đối tượng theo trục phụ nhờ các giá trị dưới đây :

  • stretch : các thành phần kéo dài theo chiều dọc (mặc định)
  • flex-start : căn đầu (cạnh trên theo trục dọc, cạnh trái theo trục ngang)
  • flex-end : căn cuối (cạnh dưới theo trục dọc, cạnh phải theo trục ngang)
  • center : căn giữa
  • baseline : căn theo cạnh cơ bản (kết quả nhận được tương tự như flex-start)

Trong ví dụ dưới đây thì chúng ta chọn trục chính là trục ngang nhưng với trục dọc cũng không có gì khác biệt cả.

#khungChua {
    display: flex;
    justify-content: center;
    align-items: center;
}

! Việc thực hiện căn trung tâm, chiếc chén thánh mà các lập trình viên web luôn tìm kiếm, được thực hiện 1 cách vô cùng dễ dàng nếu sử dụng Flexbox. Chúng ta chỉ  cần tạo ra 1 khung chứa flexbox và thiết lập lề ngoài tự động cho thành phần bên trong, vậy là đủ để có 1 thành phần lơ lửng ở chính giữa của khung chứa.

#khungChua {
    display: flex;
}
.thanhPhan {
    margin: auto;
}
Căn chỉnh riêng từng thành phần

Chúng ta thậm chí có thể phân ra, căn chỉnh riêng biệt từng thành phần theo trục phụ nhờ thuộc tính align-self.

#khungChua {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}
.thanhPhan:nth-child(2) { /* Thanh phan thu 2 */
    background-color: blue;
    align-self: flex-end; /* Rieng thanh phan nay duoc can cuoi*/
}

Dàn nhiều dòng

Nếu trong đối tượng Flexbox của chúng ta có nhiều dòng, chúng ta có thể tùy chỉnh sắp xếp các thành phần theo từng dòng với align-content.

! Thuộc tính này không có tác dụng gì nếu tất cả thành phần của Flexbox đều chỉ nằm trên 1 dòng.

Hãy sử dụng ví dụ tương tự lúc trước nhưng them vào đó nhiều thành phần hơn.

<div id="khungChua">
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
    <div class="thanhPhan"></div>
</div>

Tôi tùy chỉnh để các thành phần tự động xuống hàng nếu thiếu chỗ.

#khungChua {
    display: flex;
    flex-wrap: wrap;
}

Chỉ thế thôi thì vẫn chưa có gì đặc biệt cả. Bây giờ, hãy thử áp dụng thuộc tính align-content để xem kết quả nhận được là gì nhé. Chú ý, chúng ta có thể sử dụng 6 giá trị. (Các giải thích dưới có ý nghĩa nếu trục chính là trục ngang. Các khái niệm sẽ thay đổi tương ứng nếu trục chính là trục dọc.)

  • flex-start: các dòng bắt đầu ở đầu cột.
  • flex-end: các dòng bắt đầu ở cuối cột
  • center: các dòng lơ lửng giữa cột
  • space-between: có khoảng trống giữa các dòng
  • space-around: khoản trống giữa các dòng cũng như ở đầu và cuối cột
  • stretch (mặc định): các dòng tách ra chiếm cứ không gian tương ứng phù hợp nhất.

Chỉnh sửa thứ tự

Không cần thay đổi mã HTML, chúng ta vẫn có thể thay đổi thứ tự của các thành phần nhớ thuộc tính order của CSS. Đơn giản là gán cho mỗi thành phần 1 giá trị và chúng sẽ được sắp xếp theo thứ tự tăng dần giá trị đó của thành phần.

Hãy quay lại với ví dụ chỉ có 3 đối tượng thành phần.

#khungChua {
    display: flex;
}

Nếu chúng ta muốn đối tượng đầu tiên hiện ở vị trí cuối, đối tượng thứ 2 hiện đầu tiên và đối tượng thứ 3 ở vị trí giữa, đây sẽ là đoạn mã chúng ta cần sử dụng.

.thanhPhan:nth-child(1) {
    order: 3;
}
.thanhPhan:nth-child(2) {
    order: 1;
}
.thanhPhan:nth-child(3) {
    order: 2;
}

Linh động hơn, hơn nữa…

1 tính năng cuối cùng không thể không nhắc đến trước khi đi vào thực hành, đó là với thuộc tính flex, chúng ta có thể tăng giảm kích thước đối tượng để nó chiếm nốt khoảng không gian còn lai.

.thanhPhan:nth-child(2) {
    flex: 1;
}

Giá trị của thuộc tính flex cho chúng ta biết cần phóng lơn 1 đối tượng tới kích thước nào so với các thành phần bình thường khác.

.thanhPhan:nth-child(1) {
    flex: 2;
}
.thanhPhan:nth-child(2) {
    flex: 1;
}

Vậy là đối tượng thức 2 sẽ có kích thước gấp đôi đối tượng thứ 1.

! Thuộc tính flex thực ra là 1 thuộc tính phức hợp bao gồm flex-grow để điều khiển tăng kích thước, flex-shrink điều khiển giảm kích thước, flex-basis cho cỡ chữ.

Tóm tắt bài học :
  • Chúng ta có rất nhiều cách để lên trang cho trang web. Flexbox là tính năng mới nhất và cũng mạnh mẽ nhất theo ý kiến chủ quan của tôi. Đây là cách thức lên trang mà tôi khuyên chúng ta nên sử dụng.
  • Nguyên lý hoạt động của Flexbox dựa trên hình ảnh 1 khối hộp với nhiều thành phần nhỏ bên trong. Với thuộc tính display: flex; được áp dụng cho khung chứa, tất cả các thành phần con sẽ tự động chuyển sang chế độ Flexbox (mặc định là sắp xếp theo chiều ngang).
  • Flexbox có thể hoạt động theo nhiều phương. Chúng ta dùng thuộc tính flex-direction để chỉ ra là chúng ta muốn sắp xếp ngang (mặc định) hoặc dọc. Chúng ta gọi phương này là trục chính.
  • Việc căn chỉnh các thành phần trên trục chính cần dùng thuộc tính justify-content và trên trục phụ thì cần align-items.
  • Với flex-wrap, chúng ta cho phép thành phần được hiển thị ở dòng khác nếu không có đủ chỗ để hiển thị trong cùng dòng.
  • Chúng ta sử dụng align-content để dàn khoảng cách giữa các dòng trong trường hợp các thành phần được hiển thị trên nhiều dòng.
  • Chúng ta có thể sắp xếp lại thứ tự các thành phần mà không cần sửa mã HTML nhờ thuộc tính order của CSS.
  • Với thuộc tính phức flex, chúng ta tùy chỉnh kích thước mà chúng ta muốn hiển thị đối tượng trong tương quan không gian hiển thị còn lại.
  • Flexbox muôn năm !