+9

CSS Percentage - Hiểu rõ hơn về đơn vị phần trăm trong CSS

Trong thời đại các thiết bị mobile đang ngày càng thông dụng, thì việc làm ra một website/webapp ngày càng phức tạp khi chúng ta phải chú trọng tới nhiều kích cỡ màn hình. Dù đã có nhiều framework CSS hỗ trợ sẵn nhưng việc nắm rõ CSS để không quá lệ thuộc vào framework không bao giờ là thừa. Bên cạnh kỹ thuật hỗ trợ responsive bá đạo như media queries, thì có lẽ đơn vị phần trăm là một người bạn đồng hành không thể thiếu nếu bạn muốn cuộc đời dev của mình không bị hành bởi CSS 😀 Bài viết này mình xin tổng hợp và chia sẻ ý nghĩa của đơn vị phần trăm cho một số thuộc tính CSS thông dụng để mọi người dễ nắm bắt và áp dụng hơn.

Phần trăm của cái gì?

Đã là phần trăm thì chúng ta cần phải có một đối tượng cụ thể để đối chiếu. Đa số câu trả lời cho câu hỏi này thường sẽ là parent block (phần tử cha). Tuy câu trả lời đó không sai, nhưng chưa thực sự đầy đủ và bao hàm hết tất cả các trường hợp. Đáp án chính xác nhất là containing block, tức là phần tử chứa phần tử mà ta đang đặt đơn vị phần trăm.

Để minh họa cụ thể cho đáp án trên, các bạn cùng xem qua ví dụ dưới đây:

Trong ví dụ này mình tạo 3 div lồng nhau là 3 ô vuông với các đặc điểm sau:

  • Div ông nội bọc ngoài cùng, màu xám nhạt, kích thước là 4x4
  • Div cha màu xám đậm, kích thước là 2x2
  • Div con màu đỏ, kích thước là 50%

Nếu phần trăm là theo parent thì kích thước của con lẽ ra phải bằng 1/2 cha, nhưng không, kích thước của con lúc này là bằng cha và lại bằng 1/2 kích thước của ông nội như các bạn thấy trong ví dụ. Nguyên nhân là do div ông nội mới thực sự là containing block chứa div con, vì trong div con mình có đặt thuộc tính position: absolute, ứng với position: relative trong div ông nội.

Như vậy, việc xác định đâu mới là containing block để đơn vị phần trăm đối chiếu hoàn toàn dựa trên thuộc tính position của phần tử đó. Các bạn có thể đọc kĩ hơn trên MDN (tài liệu tiếng Anh).

Đối với một số thuộc tính, đối tượng để đối chiếu không phải là parent block hay containing block mà lại là chính nó - self element.

Các thuộc tính

Dưới đây mình liệt kê một vài thuộc tính thông dụng trong số rất nhiều thuộc tính có thể sử dụng đơn vị phần trăm cùng với ý nghĩa của chúng, đi kèm một vài ví dụ để dễ hình dung hơn.

width/height

Đây là 2 thuộc tính phổ biến nhất mà các bạn có thể đã ít nhiều áp dụng đơn vị phần trăm. Không có gì phức tạp, như ví dụ trên, khi bạn đặt phần trăm cho width của phần tử thì nó sẽ được đối chiếu với width của containing block tương ứng, và tương tự, height sẽ được đối chiếu với height của containing block.

padding

Đối với padding, cho dù là dọc (padding-top/padding-bottom) hay ngang (padding-left/padding-right) thì đơn vị phần trăm đều sẽ đối chiếu với width của containing block.

Ví dụ:

Trong ví dụ này,

  • Div cha có đặt kích thước là 6x4
  • Div con có kích thước là 0, nhưng với 2 thuộc tính padding-toppadding-left đều là 50%

Kết quả là con có kích thước bằng 1/2 width của cha, tức là một hình vuông 3x3.

margin

Tương tự như padding, đơn vị phần trăm của margin (cả dọc và ngang) đều đối chiếu với width của containing block.

Ví dụ:

Trong ví dụ này,

  • Div cha có kích thước là 6x4
  • Div con với 2 thuộc tính margin-topmargin-left đều là 50%

Kết quả là con nằm cách cạnh trên và cạnh trái của cha một khoảng bằng 3 đơn vị (1/2 width của cha).

top/bottom/left/right

Đối với các thuộc tính định vị trí của phần tử (thường được kết hợp với position), đơn vị phần trăm sẽ đối chiếu top/bottom với heightleft/right với width của containing block.

Ví dụ:

Trong ví dụ này

  • Div cha có kích thước là 6x4
  • Div con được đặt position: absolute với 2 thuộc tính topleft đều là 50%

Kết quả là con nằm cách cạnh trên của cha một khoảng 2 đơn vị (1/2 height của cha), và cách cạnh trái của cha một khoảng 3 đơn vị (1/2 width của cha).

transform: translate()

Có lẽ thuộc tính này sẽ ít bạn biết tới hơn, nhưng đây cũng là một thuộc tính cực kỳ lợi hại để định vị trí của phần tử thay cho các thuộc tính trên, thậm chí còn lợi hại hơn về mặt performance khi bạn cần kết hợp với animation hay transition. Tuy nhiên, đối với thuộc tính transform: translate() này, đơn vị phần trăm không được đối chiếu với containing block, mà sẽ được đối chiếu với self element (chính bản thân nó).

Ví dụ:

Trong ví dụ này,

  • Div cha có kích thước là 6x4
  • Div con có kích thước là 2x1 với thuộc tính transform: translate(50%, 50%)

Kết quả là con nằm cách cạnh trên của cha một khoảng 0.5 đơn vị (1/2 height của con), và cách cạnh trái của cha một khoảng 1 đơn vị (1/2 width của con).

background-size

Đối với background-size thì sẽ hơi phức tạp hơn. Đơn vị phần trăm của thuộc tính này sẽ dựa vào background positioning area (tạm dịch: phạm vi đặt vị trí ảnh). Hiểu đơn giản thì background positioning area cũng chính là containing block, nhưng bổ sung 3 yếu tố sau:

  • Block chỉ bao gồm content (content-box)
  • Block bao gồm content và padding (padding-box)
  • Block bao gồm cả content, padding và border (border-box)

3 yếu tố được quy định theo thuộc tính background-origin.

Ví dụ:

Trong ví dụ này,

  • Div cha có kích thước là 6x4
  • Div con có kích thước là 3x2, không padding, ko border
  • Mình dùng một biểu tượng (tỉ lệ thật là hình vuông 1:1) đặt làm background-image cho div con, với thuộc tính background-size50% 50%

Kết quả là ảnh background bị co giãn để có kích thước là 1.5x1, ứng với 1/2 kích thước của div con.

background-position

Tương tự như background-size, đơn vị phần trăm của background-position cũng dựa vào background positioning area.

Ví dụ:

Ở ví dụ này, mình dùng hình ảnh và bố cục tương tự như ví dụ background-size. Khi thay đổi giá trị của background-position, chúng ta có thể quan sát thấy vài điểm sau:

  • Khi không đặt giá trị background-position (giá trị mặc định là 0 0), ảnh background sẽ nằm ở góc trái trên.
  • Khi đặt background-position: 0 50%, ảnh background sẽ nằm ở góc trái giữa.
  • Khi đặt background-position: 50% 50%, ảnh background sẽ nằm ở ngay chính giữa.
  • Khi đặt background-position: 100% 100%, ảnh background sẽ nằm ở góc phải dưới.

Lưu ý: background-position: 0 50% tương đương:

  • background-position-x: 0
  • background-position-y: 50%

Rõ ràng là có một sự tính toán đằng sau đơn vị phần trăm của thuộc tính này, thay vì chỉ là khoảng cách từ cạnh trên của div con đến ảnh. Lấy một trường hợp cụ thể là background-position: 100% 100%, nếu tính như top/left thì lẽ ra ảnh đã phải nằm ra ngoài div con, hay nếu tính như transform: translate() thì ảnh phải nằm ở góc giữa dưới. Vậy tại sao trường hợp này ảnh lại nằm ở góc phải dưới? Nguyên nhân là do thuộc tính background-position phải tham giao vào công thức sau trước khi trả về vị trí thực của ảnh:

offset X = (container's width - image's width) * background-position-x

offset Y = (container's height - image's height) * background-position-y

Trong trường hợp này,

  • container là div con
  • image's width/height là kích thước của ảnh đã qua xử lý của background-size

font-size

Đối với font-size, bất kể như thế nào, đơn vị phần trăm chỉ được đối chiếu với parent block.

Ví dụ:

Ví dụ này mình dùng bố cục tương tự như ví dụ đầu tiên, với các thuộc tính font-size được đặt như sau:

  • Div ông nội13px
  • Div cha26px
  • Div con50%

Kết quả như chúng ta có thể thấy, font-size của con lúc này có vẻ là bằng với ông nội và bằng 1/2 của cha, mặc dù ông nội có thuộc tính position: relative còn cha thì không.

line-height

Tuy ít phổ biến hơn nhưng mình cũng nhắc đến thuộc tính này vì nó cũng hỗ trợ đơn vị phần trăm. Đơn vị phần trăm của line-height sẽ đối chiếu với font-size của chính bản thân nó - self element. Cách tính là lấy giá trị phần trăm * font-size để được giá trị thực của line-height.

Ví dụ:

Trong ví dụ này,

  • Đoạn text có 11 dòng
  • font-size20px
  • line-height150%

Kết quả height thực tế của khối là ~329px,

  • Tính theo cách trên thì kết quả của line-height = 20 * 150 / 100 = 30px.
  • Tính theo line-height thì height = 30 * 11 = 330px, cũng sấp sỉ kết quả thực tế.

Kết

Hi vọng bài viết phần nào giúp các bạn dễ hiểu hơn về đơn vị phần trăm, chứ không làm tình hình càng tệ hơn 😅

Dưới đây mình làm 1 bảng tóm sơ lại các đối tượng để đối chiếu phần trăm, để có cái gọi là "takeaway":

Thuộc tính Đối tượng để đối chiếu %
width containing block's width
height containing block's height
padding containing block's width
margin containing block's width
left/right containing block's width
top/bottom containing block's height
transform: translateX() self element's width
transform: translateY() self element's height
background-size background positioning area
background-position background positioning area (có công thức tính)
font-size parent block's font-size
line-height self element's font-size (có công thức tính)

Còn đây là trọn bộ các ví dụ trong bài viết: https://codepen.io/collection/xKwgdW

Tham khảo


@khangnd
Github Linkedin Dev.to Fandom


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.