Bài 3.5: Bài tập thực hành Flexbox

Chào mừng trở lại với chuỗi bài học về Lập trình Web Front-end! Sau khi đã nắm vững lý thuyết về Flexbox, đã đến lúc xắn tay áo và cùng nhau thực hành. Flexbox là một công cụ cực kỳ mạnh mẽ để tạo ra các bố cục linh hoạt và đáp ứng (responsive), và cách tốt nhất để làm chủ nó chính là thông qua các bài tập thực tế.

Bài viết này sẽ tập trung vào việc áp dụng các thuộc tính Flexbox đã học để giải quyết một số vấn đề bố cục phổ biến. Chúng ta sẽ đi từ những ví dụ đơn giản đến phức tạp hơn một chút. Hãy chuẩn bị trình soạn thảo code và trình duyệt của bạn!

Tại Sao Cần Thực Hành Flexbox?

Lý thuyết chỉ là bước khởi đầu. Khi bạn tự tay viết code, tự mình thấy các phần tử di chuyển, căn chỉnh theo ý muốn, đó là lúc kiến thức thực sự "thấm". Thực hành giúp bạn:

  • Hiểu rõ hơn về cách các thuộc tính Flexbox tương tác với nhau.
  • Nắm được các tình huống nên sử dụng thuộc tính nào.
  • Phát triển khả năng debug khi bố cục không như ý.
  • Xây dựng trực giác về cách Flexbox hoạt động.

Không dài dòng thêm nữa, chúng ta hãy bắt đầu với bài tập đầu tiên!

Bài Tập 1: Xếp Hàng Đơn Giản

Mục tiêu: Tạo một container chứa ba phần tử, và xếp chúng trên cùng một hàng ngang.

Đây là bài tập cơ bản nhất để làm quen với display: flex;.

HTML:

<div class="container bai-tap-1">
    <div class="item">Item 1</div>
    <div class="item">Item 2</div>
    <div class="item">Item 3</div>
</div>

CSS:

.container.bai-tap-1 {
    display: flex; /* Biến container thành flex container */
    border: 1px solid #ccc; /* Thêm viền để dễ nhìn */
    padding: 10px;
    margin-bottom: 20px;
}

.item {
    background-color: lightblue;
    padding: 10px;
    margin: 5px;
}

Giải thích:

Chỉ cần thêm thuộc tính display: flex; vào phần tử cha (.container.bai-tap-1), các phần tử con trực tiếp của nó (.item) sẽ tự động trở thành các flex item và được xếp theo chiều ngang (mặc định của flex-directionrow). Các thuộc tính border, padding, margin, background-color chỉ giúp các phần tử hiển thị rõ ràng hơn.

Bài Tập 2: Căn Giữa "Thần Thánh"

Mục tiêu: Căn giữa một phần tử duy nhất cả theo chiều ngang và chiều dọc bên trong một container.

Đây là một trong những vấn đề bố cục kinh điển và Flexbox giải quyết nó một cách đẹp đẽ.

HTML:

<div class="container bai-tap-2">
    <div class="item center-me">Căn giữa tôi!</div>
</div>

CSS:

.container.bai-tap-2 {
    display: flex; /* Bật flexbox */
    justify-content: center; /* Căn giữa theo trục chính (ngang) */
    align-items: center; /* Căn giữa theo trục phụ (dọc) */
    height: 150px; /* Cần có chiều cao để thấy căn giữa theo chiều dọc */
    border: 1px solid #ccc;
    margin-bottom: 20px;
}

.item.center-me {
    background-color: lightgreen;
    padding: 20px;
}

Giải thích:

Với display: flex;, chúng ta có thể sử dụng justify-content để điều khiển việc căn chỉnh các item dọc theo trục chính (ở đây là trục ngang do flex-direction mặc định là row). Giá trị center sẽ đưa tất cả item về giữa trục chính. Tương tự, align-items điều khiển việc căn chỉnh các item dọc theo trục phụ (trục dọc). Giá trị center sẽ căn giữa item theo trục dọc. Kết hợp cả hai, chúng ta dễ dàng căn giữa một (hoặc nhiều) item bên trong container. Tuyệt vời phải không?

Bài Tập 3: Phân Bố Khoảng Trống Giữa Các Phần Tử

Mục tiêu: Tạo một hàng gồm nhiều phần tử và phân bổ đều khoảng trống giữa chúng.

Khi bạn cần tạo menu, thanh điều hướng hoặc các mục sản phẩm xếp hàng ngang, việc phân bổ khoảng trống là rất quan trọng.

HTML:

<div class="container bai-tap-3">
    <div class="item">Link 1</div>
    <div class="item">Link 2</div>
    <div class="item">Link 3</div>
    <div class="item">Link 4</div>
</div>

CSS:

.container.bai-tap-3 {
    display: flex;
    justify-content: space-between; /* Phân bổ khoảng trống chỉ giữa các item */
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 20px;
}

/* Hoặc thử với: */
/* justify-content: space-around; */ /* Phân bổ khoảng trống xung quanh mỗi item (nửa ở hai đầu) */
/* justify-content: space-evenly; */ /* Phân bổ khoảng trống đều nhau giữa và hai đầu item */


.item {
    background-color: lightcoral;
    padding: 10px;
    /* Không cần margin ngang giữa các item khi dùng justify-content */
}

Giải thích:

Thuộc tính justify-content không chỉ dùng để căn giữa. Nó có nhiều giá trị khác nhau để phân bổ khoảng trống:

  • space-between: Khoảng trống được đặt chỉ ở giữa các item. Item đầu tiên nằm sát cạnh container bên trái, item cuối cùng sát cạnh container bên phải.
  • space-around: Khoảng trống được đặt xung quanh mỗi item. Điều này có nghĩa là item đầu tiên và cuối cùng sẽ có khoảng trống bằng một nửa khoảng trống giữa các item.
  • space-evenly: Khoảng trống được phân bổ đều nhau giữa tất cả các item và cả ở hai đầu container.

Hãy thử nghiệm với cả ba giá trị này để thấy sự khác biệt nhé!

Bài Tập 4: Các Phần Tử Co Giãn Linh Hoạt

Mục tiêu: Có các phần tử trong một hàng, một số co giãn để lấp đầy không gian trống, một số giữ nguyên kích thước.

Đây là lúc chúng ta sử dụng các thuộc tính flex-grow, flex-shrinkflex-basis.

HTML:

<div class="container bai-tap-4">
    <div class="item fixed-size">Cố định</div>
    <div class="item growable">Co giãn</div>
    <div class="item fixed-size">Cố định</div>
    <div class="item growable">Co giãn</div>
</div>

CSS:

.container.bai-tap-4 {
    display: flex;
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 20px;
    width: 100%; /* Đảm bảo container có đủ chiều rộng */
}

.item {
     background-color: lightseagreen;
     padding: 10px;
     margin: 5px;
     color: white;
     min-width: 80px; /* Đặt kích thước tối thiểu để tránh co quá nhỏ */
}

.fixed-size {
    /* Mặc định flex-grow: 0, flex-shrink: 1, flex-basis: auto */
    /* Có thể ghi rõ hơn nếu muốn */
    flex-grow: 0; /* Không co giãn */
    flex-shrink: 0; /* Không co nhỏ hơn min-width */
    flex-basis: auto; /* Kích thước dựa vào nội dung hoặc width/height */
    background-color: darkorange;
}

.growable {
    flex-grow: 1; /* Cho phép co giãn để lấp đầy không gian trống */
    /* Mặc định flex-shrink: 1 */
    /* Mặc định flex-basis: auto */
    background-color: dodgerblue;
}

/* Mẹo nhỏ: Có thể dùng shorthand 'flex' */
/* .growable { flex: 1 1 auto; } */ /* flex-grow: 1, flex-shrink: 1, flex-basis: auto */
/* .fixed-size { flex: 0 0 auto; } */ /* flex-grow: 0, flex-shrink: 0, flex-basis: auto */

Giải thích:

  • flex-grow: Xác định khả năng một flex item co giãn để chiếm không gian còn trống trong flex container. Giá trị là một số dương (ví dụ: 1, 2). Item nào có flex-grow lớn hơn sẽ chiếm tỉ lệ không gian trống lớn hơn.
  • flex-shrink: Xác định khả năng một flex item co nhỏ lại khi không có đủ không gian trong flex container. Giá trị là một số dương. Mặc định là 1 (có thể co lại). 0 nghĩa là không co lại (item có thể tràn ra ngoài nếu không đủ chỗ).
  • flex-basis: Xác định kích thước ban đầu của flex item trước khi không gian trống được phân bổ hoặc co nhỏ xảy ra. Giá trị có thể là độ dài tuyệt đối (100px), phần trăm (50%), hoặc auto (dựa vào kích thước nội dung hoặc width/height của item).

Trong ví dụ này, .growable với flex-grow: 1; sẽ chia đều phần không gian trống còn lại sau khi các .fixed-size item đã chiếm chỗ. Các .fixed-size item sẽ giữ kích thước cố định (hoặc tối thiểu).

Bài Tập 5: Xuống Hàng Khi Hết Chỗ

Mục tiêu: Tạo một hàng gồm nhiều phần tử, và khi không đủ chỗ trên một dòng, chúng sẽ tự động xuống dòng mới.

Đây là lúc flex-wrap phát huy tác dụng, giúp tạo ra bố cục đáp ứng (responsive) một cách tự nhiên.

HTML:

<div class="container bai-tap-5">
    <div class="item">Item 1</div>
    <div class="item">Item 2</div>
    <div class="item">Item 3</div>
    <div class="item">Item 4</div>
    <div class="item">Item 5</div>
    <div class="item">Item 6</div>
    <div class="item">Item 7</div>
    <div class="item">Item 8</div>
</div>

CSS:

.container.bai-tap-5 {
    display: flex;
    flex-wrap: wrap; /* Cho phép các item xuống dòng khi không đủ chỗ */
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 20px;
    width: 300px; /* Giới hạn chiều rộng để dễ thấy hiệu ứng wrap */
    /* Bỏ width: 300px nếu muốn xem trên toàn bộ chiều rộng màn hình */
}

.item {
    background-color: lightsalmon;
    padding: 10px;
    margin: 5px;
    width: 100px; /* Cho mỗi item một chiều rộng cố định */
    text-align: center;
}

Giải thích:

Thuộc tính flex-wrap trên flex container xác định liệu các flex item có nên xuống dòng (wrap) hay không khi không đủ không gian trên một dòng/cột.

  • nowrap (mặc định): Các item sẽ cố gắng nằm trên một dòng/cột duy nhất, có thể bị co nhỏ (flex-shrink: 1;) hoặc tràn ra ngoài.
  • wrap: Các item sẽ xuống dòng khi cần thiết. Dòng mới được tạo ra theo hướng của trục phụ (vuông góc với flex-direction).
  • wrap-reverse: Giống như wrap, nhưng các dòng mới được xếp theo hướng ngược lại của trục phụ.

Trong ví dụ này, khi container có chiều rộng 300px và mỗi item rộng 100px (cộng thêm margin), chỉ có thể chứa 2 item trên một dòng (100 + 100 + 5 + 5 = 210px, vẫn còn chỗ). Item thứ 3 sẽ tràn sang dòng mới. Thử thay đổi width của container hoặc item để thấy rõ hiệu ứng xuống dòng.

Bài Tập 6: Thay Đổi Thứ Tự Hiển Thị

Mục tiêu: Sắp xếp lại thứ tự hiển thị của các flex item mà không cần thay đổi thứ tự trong mã HTML.

Điều này rất hữu ích khi bạn muốn trình bày thông tin khác nhau trên các kích thước màn hình khác nhau (ví dụ: trên mobile thì mục quảng cáo lên đầu, trên desktop thì xuống cuối).

HTML:

<div class="container bai-tap-6">
    <div class="item" style="order: 2;">Item B</div>
    <div class="item" style="order: 3;">Item C</div>
    <div class="item" style="order: 1;">Item A</div>
</div>

CSS:

.container.bai-tap-6 {
    display: flex;
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 20px;
}

.item {
    background-color: lightblue;
    padding: 10px;
    margin: 5px;
    /* order: giá trị mặc định là 0 */
}

Giải thích:

Thuộc tính order trên từng flex item cho phép bạn điều khiển thứ tự hiển thị của nó. Item với giá trị order thấp hơn sẽ xuất hiện trước. Giá trị mặc định của order0. Các item có cùng giá trị order sẽ được sắp xếp theo thứ tự xuất hiện trong mã HTML. Bạn có thể sử dụng số âm.

Trong ví dụ trên, mặc dù Item A nằm cuối trong HTML, order: 1; khiến nó hiển thị đầu tiên (vì 1 < 2 < 3).

Bài Tập 7: Căn Riêng Một Phần Tử

Mục tiêu: Có một nhóm các item được căn chỉnh theo một quy tắc chung, nhưng một item duy nhất lại được căn chỉnh khác biệt trên trục phụ.

Đây là lúc thuộc tính align-self trên từng item phát huy tác dụng.

HTML:

<div class="container bai-tap-7">
    <div class="item">Item Cao</div>
    <div class="item align-this-differently">Item Ngắn Hơn</div>
    <div class="item">Item Cao</div>
</div>

CSS:

.container.bai-tap-7 {
    display: flex;
    align-items: stretch; /* Mặc định: các item kéo dài đầy container theo trục phụ */
    /* Hoặc thử với: align-items: flex-start; */
    /* Hoặc thử với: align-items: center; */
    height: 150px; /* Cần có chiều cao để thấy hiệu ứng align-items và align-self */
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 20px;
}

.item {
    background-color: lightsalmon;
    padding: 10px;
    margin: 5px;
    /* align-self: giá trị mặc định là auto, thừa hưởng từ align-items của cha */
}

.align-this-differently {
    align-self: flex-start; /* Căn item này lên đầu trục phụ */
    background-color: lightgreen;
}

Giải thích:

Thuộc tính align-items trên flex container đặt quy tắc căn chỉnh cho tất cả các flex item con theo trục phụ. Tuy nhiên, thuộc tính align-self trên từng flex item riêng lẻ có thể ghi đè quy tắc của align-items cho item đó. Các giá trị của align-self giống với align-items: auto (mặc định), flex-start, flex-end, center, stretch, baseline.

Trong ví dụ này, .container.bai-tap-7align-items: stretch;, làm cho tất cả các item (trừ item thứ hai) kéo dài đầy chiều cao của container. Tuy nhiên, .align-this-differently item với align-self: flex-start; chỉ bị đẩy lên phía đầu của trục phụ (phía trên), bất kể cài đặt của container.

Comments

There are no comments at the moment.