11/08/2018, 21:20

Có thể bạn biết rồi: CSS Floating và Clearfix

Nếu ai đã từng làm việc với CSS, chắc ko hề lạ gì 2 khái niệm float và clearfix, tuy nhiên có thể một số bạn cứ biết chức năng của nó và cứ dùng vậy thôi chứ ko biết (hoặc ko thèm quan tâm) tại sao dùng nó thì có tác dụng như vậy. Bài viết sau mình sẽ quay về cơ bản một tí để giải thích cụ thể ...

Nếu ai đã từng làm việc với CSS, chắc ko hề lạ gì 2 khái niệm float và clearfix, tuy nhiên có thể một số bạn cứ biết chức năng của nó và cứ dùng vậy thôi chứ ko biết (hoặc ko thèm quan tâm) tại sao dùng nó thì có tác dụng như vậy.

Bài viết sau mình sẽ quay về cơ bản một tí để giải thích cụ thể chức năng và cơ chế hoạt động của 2 khái niệm này.

Mặc định, một thẻ div sẽ chiếm hết một dòng, và không có một element nào có thể nằm ngay sau nó được, mà phải sang một dòng khác.

Ví dụ với đoạn code sau:

<div class="container">
    <div class="box green"> ... </div>
    <div class="box yellow"> ... </div>
    <div class="box red"> ... </div>
</div>

Ta có kết quả như hình sau:

alt text

Nhưng nhiều lúc chúng ta muốn sắp xếp các đối tượng nối tiếp nhau trên cùng 1 dòng. Khi đó chúng ta sẽ phải dùng một thuộc tính của CSS gọi là float, chức năng của thuộc tính này là khiến cho các element nằm ngay sau nó bị trôi theo element mang thuộc tính float.

Ví dụ, chúng ta gán thuộc tính float: left; cho các div có class là box như sau:

.box {
    float: left;
    ...
}

Kết quả là các thẻ div sẽ nằm lên cùng 1 dòng, như hình sau:

alt text

Có 2 kiểu là float: left và float: right, tương ứng với 2 chiều "trôi" của element sang trái và phải.

Trong trường hợp chúng ta muốn bỏ thuộc tính float của một element đi, ví dụ trong trường hợp ở hình sau:

alt text

Chúng ta không muốn DIV 3 bị trôi theo DIV 2 mà muốn nó nằm trên một dòng riêng biệt, khi đó, chúng ta dùng thuộc tính clear.

Chức năng của thuộc tính này là dọn đường thông thoáng về bên trái left hoặc phải right hoặc cả 2 bên both, khi bị dọn dẹp sạch sẽ như vậy thì element đó sẽ ko bị trôi theo element trước đó nữa.

Ở ví dụ trên, chúng ta sẽ gán thêm 1 thuộc tính clear: left cho DIV 3, có class là red, dọn dẹp phần bên trái của nó để không cho nó trôi theo DIV 2 nữa.

.red {
    clear: left;
}

Khi sử dụng float, hẳn chúng ta sẽ gặp phải những tình huống lỗi rất khó chịu, ví dụ như thế này:

alt text

Ở đây chúng ta có một thẻ div cha (parent), chứa 4 thẻ div con mang thuộc tính float: left, nếu có thêm một nội dung nằm sau div cha, thì nội dung này sẽ bị 4 thẻ div con đè lên. Hoặc nếu các bạn dùng dev tool của trình duyệt để kiểm tra, sẽ thấy thuộc tính height của div cha này bằng 0.

Lý do là vì trình duyệt không thể tính được chiều cao của div cha chứa các div mang thuộc tính float, nên các element nằm sau div cha này sẽ bị các div con đó đè lên.

Để giải quyết vấn đề trên, chúng ta có thể chèn thêm 1 thẻ div nằm ở vị trí cuối cùng, đằng sau các thẻ div float, thẻ div cuối cùng này được gán thuộc tính clear: both, có nhiệm vụ dọn dẹp 2 bên đường, và chui vào nằm ở vị trí dưới cùng, từ đó nó sẽ "kéo" thẻ div cha về lại đúng chiều cao của nó. Ví dụ:

<div class="container">
    <div class="box green"> ... </div>
    <div class="box yellow"> ... </div>
    <div class="box red"> ... </div>
    <div class="clear"></div>
</div>
.clear {
    clear: both;
    ...
}

Nhưng cách làm này có rất nhiều hạn chế: đó là bạn phải tự thêm vào thẻ div clear mỗi khi muốn sử dụng float, gây ra nhiều phiền phức và cấu trúc HTML của trang sẽ trở nên không "đẹp".

Vì thế, chúng ta sử dụng một element ảo (pseudo element), đó là ::after đặt vào cuối của div cha để mang thuộc tính clear.

<div class="container">
    <div class="box green"> ... </div>
    <div class="box yellow"> ... </div>
    <div class="box red"> ... </div>
</div>
.container:after {
    ...
    clear: both;
    ...
}

Với đoạn CSS trên, khi trình duyệt hiển thị trang web ra, thì nó sẽ có cấu trúc như thế này

<div class="container">
    <div class="box green"> ... </div>
    <div class="box yellow"> ... </div>
    <div class="box red"> ... </div>
    ::after
</div>

Đoạn nội dung ::after sẽ được chèn vào ở vị trí như trên, và mang thuộc tính clear: both giúp chúng ta dọn dẹp phần cuối của div cha, từ đó trình duyệt có thể tính được chiều cao thực tế của div cha.

Chúng ta thường tạo ra một class tên là clearfix để sử dụng lại pseudo element ::after ở trên cho những lần sau, và đây là nội dung của một class clearfix đơn giản:

.clearfix:after {
    content: ".";
    visibility: hidden;
    display: block; 
    clear: both;
}

Một thẻ luôn luôn phải có nội dung bên trong nó, và nội dung này không được rỗng, hoặc bằng ksi tự trắng (space), nếu không nó sẽ không xuất hiện trên trình duyệt, chính vì thế, sau khi tạo ra pseudo element ::after, chúng ta sử dụng thuộc tính content: "." để gán nội dung bên trong nó là một dấu chấm.

Tuy nhiên, dấu chấm này không nên xuất hiện trên màn hình, chính vì thế chúng ta tạm ẩn nó đi bằng thuộc tính visibility: hidden, thuộc tính này giúp ẩn một element đi mà không làm nó biến mất. Các bạn lưu ý sự khác nhau giữa display: none và visibility: hidden trong trường hợp này.

Cuối cùng là đặt thuộc tính clear: both để làm cho nó nằm tách ra khỏi dòng nội dung đang chứa các div con.

Hy vọng qua bài viết này, các bạn sẽ hiểu được bản chất vấn đề của clearfix, và kĩ thuật nằm đằng sau nó.

Mình xin kết thúc bài viết bằng câu nói của ông bạn:

"Hiểu để xài nhau tốt hơn bạn nhé :)" — @dungcoi

0