Trước khi chúng ta tìm hiểu các cách xử lý kiểu(style) và lớp(class) của JavaScript – đây là một khái niệm quan trọng. Hy vọng rằng nó đủ rõ ràng, nhưng chúng ta vẫn phải đề cập đến nó. Hôm nay cafedev chia sẻ về Styles và classes khi làm việc với Javascript

Nói chung có hai cách để tạo kiểu cho một phần tử:

  1. Tạo một lớp trong CSS và thêm nó: <div class="...">
  2. Tính ghi trực tiếp vào style: <div style="...">.

JavaScript có thể sửa đổi cả các lớp và thuộc tính style.

Chúng ta nên luôn thích các lớp CSS hơn style. Cái sau chỉ nên được sử dụng nếu các lớp “không thể xử lý nó”.

Ví dụ: style có thể chấp nhận được nếu chúng ta tính toán tọa độ động của một phần tử và muốn đặt chúng từ JavaScript, như sau:

let top = /* complex calculations */;
let left = /* complex calculations */;

elem.style.left = left; // e.g '123px', calculated at run-time
elem.style.top = top; // e.g '456px'

Đối với các trường hợp khác, như làm cho văn bản có màu đỏ, thêm biểu tượng nền – hãy mô tả điều đó trong CSS và sau đó thêm lớp (JavaScript có thể làm điều đó). Điều đó linh hoạt hơn và dễ hỗ trợ hơn.

1. className và classList

Thay đổi lớp là một trong những hành động thường được sử dụng nhất trong script.

Trong thời cổ đại, có một hạn chế trong JavaScript: một từ dành riêng như “class” không thể là thuộc tính đối tượng. Hạn chế đó bây giờ không tồn tại, nhưng thời đó không thể có  thuộc tính “class”, như elem.class.

Vì vậy, đối với các lớp, thuộc tính trông giống nhau “className” đã được giới thiệu: thuộc tính elem.className tương ứng với thuộc tính “class”.

Ví dụ:

<body class="main page">
  <script>
    alert(document.body.className); // main page
  </script>
</body>

Nếu chúng ta gán một cái gì đó cho elem.className, nó sẽ thay thế toàn bộ chuỗi của lớp. Đôi khi đó là những gì chúng ta cần, nhưng thường thì chúng ta muốn thêm / bớt một lớp duy nhất.

Có một thuộc tính cho rằng: elem.classList.

 elem.classList Là một đối tượng đặc biệt với các phương thức cho add/remove/toggle một lớp duy nhất.

Ví dụ:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/

<body class="main page">
  <script>
    // add a class
    document.body.classList.add('article');

    alert(document.body.className); // main page article
  </script>
</body>

Vì vậy, chúng ta có thể hoạt động cả trên chuỗi lớp đầy đủ bằng cách sử dụng className hoặc trên các lớp riêng lẻ bằng cách sử dụng classList. Những gì chúng ta chọn tùy thuộc vào nhu cầu của chúng ta.

Phương pháp classList:

  • elem.classList.add/remove(“class”) – thêm / bớt lớp.
  • elem.classList.toggle(“class”) – thêm lớp nếu nó không tồn tại, nếu không thì xóa nó.
  • elem.classList.contains(“class”)- kiểm tra lớp đã cho, trả về true/false.

Bên cạnh đó, classList có thể lặp lại, vì vậy chúng ta có thể liệt kê tất cả các lớp với for..of, như sau:

<body class="main page">
  <script>
    for (let name of document.body.classList) {
      alert(name); // main, and then page
    }
  </script>
</body>

2. Style của phần tử

Thuộc tính elem.style là một đối tượng tương ứng với những gì được viết trong thuộc tính “style”. Cài đặt elem.style.width=”100px” nó hoạt động giống như thể chúng ta có trong thuộc tính style một chuỗi width:100px.

Đối với thuộc tính nhiều từ, camelCase được sử dụng:

background-color  => elem.style.backgroundColor
z-index           => elem.style.zIndex
border-left-width => elem.style.borderLeftWidth

Ví dụ:

document.body.style.backgroundColor = prompt('background color?', 'green');

Thuộc tính tiền tố

Các thuộc tính có tiền tố trình duyệt như -moz-border-radius, -webkit-border-radius cũng tuân theo quy tắc tương tự: dấu gạch ngang có nghĩa là chữ hoa.

Ví dụ:

button.style.MozBorderRadius = '5px';
button.style.WebkitBorderRadius = '5px';

3. Đặt lại thuộc tính kiểu

Đôi khi chúng ta muốn gán một thuộc tính kiểu và sau đó xóa nó.

Ví dụ, để ẩn một phần tử, chúng ta có thể đặt elem.style.display = “none”.

Sau đó, sau đó chúng ta có thể muốn xóa style.display như thể nó chưa được đặt. Thay vì delete elem.style.display chúng ta nên gán một chuỗi rỗng với nó: elem.style.display = “”.

// if we run this code, the <body> will blink
document.body.style.display = "none"; // hide

setTimeout(() => document.body.style.display = "", 1000); // back to normal

Nếu chúng ta đặt style.display thành một chuỗi trống, thì trình duyệt sẽ áp dụng các lớp CSS và các kiểu tích hợp của nó một cách bình thường, như thể không có thuộc tính nào như vậy style.display.

Viết lại đầy đủ với style.cssText

Thông thường, chúng ta sử dụng style.*để gán các thuộc tính kiểu riêng lẻ. Chúng ta không thể đặt kiểu đầy đủ như div.style=”color: red; width: 100px”vì div.style là một đối tượng và nó ở chế độ chỉ đọc.

Để đặt kiểu đầy đủ dưới dạng chuỗi, có một thuộc tính đặc biệt style.cssText:

<div id="div">Button</div>

<script>
  // we can set special style flags like "important" here
  div.style.cssText=`color: red !important;
    background-color: yellow;
    width: 100px;
    text-align: center;
  `;

  alert(div.style.cssText);
</script>

Thuộc tính này hiếm khi được sử dụng, bởi vì việc gán như vậy loại bỏ tất cả các kiểu hiện có: nó không thêm, nhưng thay thế chúng. Đôi khi có thể xóa một cái gì đó cần thiết. Nhưng chúng ta có thể sử dụng nó một cách an toàn cho các phần tử mới, khi chúng ta biết mình sẽ không xóa một kiểu hiện có.

Điều tương tự cũng có thể được thực hiện bằng cách thiết lập một thuộc tính: div.setAttribute(‘style’, ‘color: red…’).

4. Ghi nhớ các đơn vị

Đừng quên thêm các đơn vị CSS vào các giá trị.

Ví dụ, chúng ta không nên đặt elem.style.top thành 10, mà là 10px. Nếu không nó sẽ không hoạt động:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/

<body>
  <script>
    // doesn't work!
    document.body.style.margin = 20;
    alert(document.body.style.margin); // '' (empty string, the assignment is ignored)

    // now add the CSS unit (px) - and it works
    document.body.style.margin = '20px';
    alert(document.body.style.margin); // 20px

    alert(document.body.style.marginTop); // 20px
    alert(document.body.style.marginLeft); // 20px
  </script>
</body>

Xin lưu ý: trình duyệt “giải nén” thuộc tính style.margin ở những dòng cuối cùng style.marginLeft và style.marginTop.

5. Kiểu tính toán: getComputedStyle

Vì vậy, việc sửa đổi một style rất dễ dàng. Nhưng làm thế nào để đọc nó?

Ví dụ, chúng ta muốn biết kích thước, lề, màu sắc của một phần tử. Làm thế nào để làm nó?

Các style hoạt động chỉ vào giá trị của các thuộc tính “style”, mà không cần bất kỳ CSS.

Vì vậy, chúng ta không thể đọc bất kỳ thứ gì đến từ các lớp CSS bằng cách sử dụng elem.style.

Ví dụ, ở đây style không thấy lề:

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  The red text
  <script>
    alert(document.body.style.color); // empty
    alert(document.body.style.marginTop); // empty
  </script>
</body>

… Nhưng nếu chúng ta cần, giả sử, để tăng  20px  thì sao? Chúng ta muốn giá trị hiện tại của nó.

Có một phương thức khác cho rằng: getComputedStyle.

Cú pháp là:

getComputedStyle(element, [pseudo])

element: Phần tử để đọc giá trị cho cái gì đó.

pseudo:

Ví dụ: một phần tử pseudo nếu được yêu cầu ::before. Một chuỗi rỗng hoặc không có đối số có nghĩa là chính phần tử đó.

Kết quả là một đối tượng có các kiểu, như elem.style, nhưng bây giờ đối với tất cả các lớp CSS.

Ví dụ:

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  <script>
    let computedStyle = getComputedStyle(document.body);

    // now we can read the margin and the color from it

    alert( computedStyle.marginTop ); // 5px
    alert( computedStyle.color ); // rgb(255, 0, 0)
  </script>

</body>

Các giá trị được tính toán và phân giải

Có hai khái niệm trong CSS :

  1. Một tính toán giá trị style là giá trị sau khi tất cả quy tắc CSS và CSS thừa kế được áp dụng, như là kết quả của thác CSS. Nó có thể trông giống như height:1em hoặc font-size:125%.
  2. Một giải quyết giá trị style là một trong những cái cuối cùng áp dụng cho phần tử. Giá trị tương đối 1em hoặc 125% tương đối. Trình duyệt lấy giá trị được tính toán và làm cho tất cả các đơn vị cố định và tuyệt đối, ví dụ: height:20px hoặc font-size:16px. Đối với thuộc tính hình học, các giá trị được phân giải có thể có dấu phẩy động, như width:50.5px.

Cách đây rất lâu getComputedStyle đã được tạo ra để lấy các giá trị được tính toán, nhưng hóa ra các giá trị đã phân giải thuận tiện hơn nhiều và chuẩn đã thay đổi.

Vì vậy, ngày nay getComputedStyle thực sự trả về giá trị đã phân giải của thuộc tính, thường là px đối với hình học.

getComputedStyle yêu cầu tên thuộc tính đầy đủ

Chúng ta nên luôn yêu cầu thuộc tính chính xác mà chúng ta muốn, thích paddingLeft hoặc marginTop hoặc borderTopWidth. Nếu không, kết quả chính xác không được đảm bảo.

Ví dụ, nếu có thuộc tính paddingLeft/paddingTop, thì chúng ta nên lấy để làm getComputedStyle(elem).paddinggì? Không có gì hoặc có thể là giá trị “được tạo” từ các vùng đệm đã biết? Không có quy tắc tiêu chuẩn nào ở đây.

Có những mâu thuẫn khác. Ví dụ: một số trình duyệt (Chrome) hiển thị 10px trong tài liệu bên dưới và một số trình duyệt (Firefox) – không hiển thị:

<style>
  body {
    margin: 10px;
  }
</style>
<script>
  let style = getComputedStyle(document.body);
  alert(style.margin); // empty string in Firefox
</script>

Các kiểu áp dụng cho :visited các liên kết bị ẩn!

Các liên kết được truy cập có thể được tô màu bằng cách sử dụng :visited CSS giả cổ điển.

Nhưng getComputedStyle không cấp quyền truy cập vào màu đó, vì nếu không, một trang tùy ý có thể tìm ra liệu người dùng có truy cập vào một liên kết hay không bằng cách tạo liên kết đó trên trang và kiểm tra các kiểu.

JavaScript có thể không thấy các kiểu được áp dụng bởi :visited. Ngoài ra, có một hạn chế trong CSS cấm áp dụng các kiểu thay đổi hình học trong :visited. Điều đó để đảm bảo rằng không có cách nào để một trang độc kiểm tra xem một liên kết có được truy cập hay không và do đó phá vỡ quyền riêng tư.

6. Tóm lược

Để quản lý các lớp, có hai thuộc tính DOM:

  • className – giá trị chuỗi, tốt để quản lý toàn bộ tập hợp các lớp.
  • classList- đối tượng với các phương thức add/remove/toggle/contains, tốt cho các lớp riêng lẻ.

Để thay đổi kiểu:

  • Các style tỉnh là một đối tượng với phong cách camelCased. Đọc và ghi nó có cùng ý nghĩa với việc sửa đổi các thuộc tính riêng lẻ trong thuộc tính “style”. Để xem cách áp dụng important và những thứ hiếm hoi khác – có một danh sách các phương thức tại MDN .
  • Các style.cssText tỉnh tương ứng với toàn bộ thuộc tính “style”, chuỗi đầy đủ các phong cách.

Để đọc các kiểu đã phân giải (đối với tất cả các lớp, sau khi tất cả CSS được áp dụng và các giá trị cuối cùng được tính toán):

  • Trả getComputedStyle(elem, [pseudo]) về đối tượng kiểu giống với chúng. Chỉ đọc.

Nguồn và tài liệu tiếng anh tham khảo:

Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!