Khi trình duyệt tải trang, trình duyệt sẽ “đọc” (một từ khác: “phân tích cú pháp”) HTML và tạo các đối tượng DOM từ đó. Đối với các nút phần tử, hầu hết các thuộc tính HTML tiêu chuẩn tự động trở thành thuộc tính của các đối tượng DOM. Hôm nay cafedev chia sẻ cho ace về các thuộc tính và đặc tính của chúng.

Ví dụ: nếu là thẻ  <body id="page">, thì đối tượng DOM có body.id=”page”.

Nhưng ánh xạ thuộc tính-thuộc tính không phải là một-một! Trong chương này, chúng ta sẽ chú ý đến việc tách biệt hai khái niệm này, để xem cách làm việc với chúng, khi nào chúng giống nhau và khi chúng khác nhau.

1.Thuộc tính DOM

Chúng ta đã thấy các thuộc tính DOM tích hợp sẵn. Có rất nhiều. Nhưng về mặt kỹ thuật thì không ai giới hạn chúng ta, và nếu không đủ, chúng ta có thể thêm cái của mình.

Các nút DOM là các đối tượng JavaScript thông thường. Chúng ta có thể thay đổi chúng.

Ví dụ: hãy tạo một thuộc tính mới trong document.body:

document.body.myData = {
  name: 'Caesar',
  title: 'Imperator'
};

alert(document.body.myData.title); // Imperator

Chúng ta cũng có thể thêm một phương thức:

document.body.sayTagName = function() {
  alert(this.tagName);
};

document.body.sayTagName(); // BODY (the value of "this" in the method is document.body)

Chúng ta cũng có thể sửa đổi các nguyên mẫu tích hợp sẵn như Element.prototype và thêm các phương thức mới vào tất cả các phần tử:

Element.prototype.sayHi = function() {
  alert(`Hello, I'm ${this.tagName}`);
};

document.documentElement.sayHi(); // Hello, I'm HTML
document.body.sayHi(); // Hello, I'm BODY

Vì vậy, các thuộc tính và phương thức DOM hoạt động giống như các thuộc tính và phương thức của các đối tượng JavaScript thông thường:

  • Chúng có thể có bất kỳ giá trị nào.
  • Chúng phân biệt chữ hoa chữ thường (viết elem.nodeType, không phải elem.NoDeTyPe).

2. Thuộc tính(Attributes) HTML

Trong HTML, các thẻ có thể có các thuộc tính. Khi trình duyệt phân tích cú pháp HTML để tạo các đối tượng DOM cho các thẻ, nó sẽ nhận ra các thuộc tính chuẩn và tạo các thuộc tính DOM từ chúng.

Vì vậy, khi một phần tử có id hoặc một thuộc tính chuẩn khác , thuộc tính tương ứng sẽ được tạo. Nhưng điều đó không xảy ra nếu thuộc tính không chuẩn.

Ví dụ:

<body id="test" something="non-standard">
  <script>
    alert(document.body.id); // test
    // non-standard attribute does not yield a property
    alert(document.body.something); // undefined
  </script>
</body>

Xin lưu ý rằng một thuộc tính chuẩn cho một phần tử có thể không xác định cho một phần tử khác. Ví dụ: “type”là chuẩn cho <input>( HTMLInputElement ), nhưng không phải cho <body>( HTMLBodyElement ). Các thuộc tính tiêu chuẩn được mô tả trong đặc tả cho lớp phần tử tương ứng.

Ở đây chúng ta có thể thấy nó:

<body id="body" type="...">
  <input id="input" type="text">
  <script>
    alert(input.type); // text
    alert(body.type); // undefined: DOM property not created, because it's non-standard
  </script>
</body>

Vì vậy, nếu một thuộc tính không phải là tiêu chuẩn, sẽ không có thuộc tính DOM cho nó. Có cách nào để truy cập các thuộc tính như vậy không?

Chắc chắn rồi. Tất cả các thuộc tính đều có thể truy cập được bằng cách sử dụng các phương pháp sau:

  • elem.hasAttribute(name) – kiểm tra sự tồn tại.
  • elem.getAttribute(name) – nhận giá trị.
  • elem.setAttribute(name, value) – đặt giá trị.
  • elem.removeAttribute(name) – loại bỏ thuộc tính.

Các phương thức này hoạt động chính xác với những gì được viết bằng HTML.

Ngoài ra, người ta có thể đọc tất cả các thuộc tính bằng cách sử dụng elem.attributes: một tập hợp các đối tượng thuộc một lớp Attr dựng sẵn , với các thuộc tính name và value.

Dưới đây là bản demo về cách đọc thuộc tính không chuẩn:

<body something="non-standard">
  <script>
    alert(document.body.getAttribute('something')); // non-standard
  </script>
</body>

Thuộc tính HTML có các tính năng sau:

  • Tên của họ không phân biệt chữ hoa chữ thường ( id giống như ID).
  • Giá trị của chúng luôn là chuỗi.

Đây là bản demo mở rộng về cách làm việc với các thuộc tính:

/*
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>
  <div id="elem" about="Elephant"></div>

  <script>
    alert( elem.getAttribute('About') ); // (1) 'Elephant', reading

    elem.setAttribute('Test', 123); // (2), writing

    alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes)

    for (let attr of elem.attributes) { // (4) list all
      alert( `${attr.name} = ${attr.value}` );
    }
  </script>
</body>

Xin lưu ý:

  1. getAttribute(‘About’)- chữ cái đầu tiên là chữ hoa ở đây, và trong HTML thì tất cả đều là chữ thường. Nhưng điều đó không quan trọng: tên thuộc tính không phân biệt chữ hoa chữ thường.
  2. Chúng ta có thể gán bất kỳ thứ gì cho một thuộc tính, nhưng nó sẽ trở thành một chuỗi. Vì vậy, ở đây chúng ta có giá trị “123”.
  3. Tất cả các thuộc tính bao gồm cả những cái mà chúng ta thiết lập có thể nhìn thấy trong outerHTML.
  4. Bộ sưu tập attributes có thể lặp lại và có tất cả các thuộc tính của phần tử (chuẩn và không chuẩn) dưới dạng các đối tượng có  thuộc tính name và value.

3. Đồng bộ hóa thuộc tính

Khi một thuộc tính chuẩn thay đổi, thuộc tính tương ứng sẽ được tự động cập nhật và ngược lại (với một số ngoại lệ).

Trong ví dụ bên dưới id được sửa đổi dưới dạng một thuộc tính và chúng ta có thể thấy thuộc tính cũng đã thay đổi. Và sau đó ngược lại:

<input>

<script>
  let input = document.querySelector('input');

  // attribute => property
  input.setAttribute('id', 'id');
  alert(input.id); // id (updated)

  // property => attribute
  input.id = 'newId';
  alert(input.getAttribute('id')); // newId (updated)
</script>

Nhưng có những loại trừ, chẳng hạn như input.value chỉ đồng bộ hóa từ thuộc tính → đến thuộc tính, nhưng không đồng bộ hóa trở lại:

/*
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/
*/

<input>

<script>
  let input = document.querySelector('input');

  // attribute => property
  input.setAttribute('value', 'text');
  alert(input.value); // text

  // NOT property => attribute
  input.value = 'newValue';
  alert(input.getAttribute('value')); // text (not updated!)
</script>

Trong ví dụ trên:

  • Thay đổi thuộc tính sẽ cập nhật value thuộc tính .
  • Nhưng sự thay đổi thuộc tính không ảnh hưởng đến thuộc tính.

“Tính năng” đó thực sự có thể hữu ích, bởi vì các hành động của người dùng có thể dẫn đến các thay đổi value và sau đó, nếu chúng ta muốn khôi phục giá trị “gốc” từ HTML, thì đó là thuộc tính.

4. Thuộc tính DOM được nhập

Thuộc tính DOM không phải lúc nào cũng là chuỗi. Ví dụ, thuộc tính input.checked (dành cho hộp kiểm) là boolean:

<input id="input" type="checkbox" checked> checkbox

<script>
  alert(input.getAttribute('checked')); // the attribute value is: empty string
  alert(input.checked); // the property value is: true
</script>

Có những ví dụ khác. Các thuộc tính style là một chuỗi, nhưng thuộc tính style là một đối tượng:

<div id="div" style="color:red;font-size:120%">Hello</div>

<script>
  // string
  alert(div.getAttribute('style')); // color:red;font-size:120%

  // object
  alert(div.style); // [object CSSStyleDeclaration]
  alert(div.style.color); // red
</script>

Hầu hết các thuộc tính là chuỗi mặc dù.

Rất hiếm, ngay cả khi loại thuộc tính DOM là một chuỗi, nó có thể khác với thuộc tính. Ví dụ: thuộc tính href DOM luôn là một URL đầy đủ , ngay cả khi thuộc tính chứa một URL tương đối hoặc chỉ một #hash.

Đây là một ví dụ:

<a id="a" href="#hello">link</a>
<script>
  // attribute
  alert(a.getAttribute('href')); // #hello

  // property
  alert(a.href ); // full URL in the form http://site.com/page#hello
</script>

Nếu chúng ta cần giá trị của href hoặc bất kỳ thuộc tính nào khác chính xác như được viết trong HTML, chúng ta có thể sử dụng getAttribute.

5. Thuộc tính không chuẩn, tập dữ liệu

Khi viết HTML, chúng ta sử dụng rất nhiều thuộc tính chuẩn. Nhưng những gì về những cái không chuẩn, tùy chỉnh? Đầu tiên, hãy xem chúng có hữu ích hay không? Để làm gì?

Đôi khi các thuộc tính không chuẩn được sử dụng để chuyển dữ liệu tùy chỉnh từ HTML sang JavaScript hoặc để “đánh dấu” các phần tử HTML cho JavaScript.

Như thế này:

<!-- mark the div to show "name" here -->
<div show-info="name"></div>
<!-- and age here -->
<div show-info="age"></div>

<script>
  // the code finds an element with the mark and shows what's requested
  let user = {
    name: "David",
    age: 25
  };

  for(let div of document.querySelectorAll('[show-info]')) {
    // insert the corresponding info into the field
    let field = div.getAttribute('show-info');
    div.innerHTML = user[field]; // first David into "name", then 25 into "age"
  }
</script>

Ngoài ra, chúng có thể được sử dụng để tạo kiểu cho một phần tử.

Ví dụ: ở đây đối với trạng thái đơn hàng, thuộc tính order-state được sử dụng:

<style>
  /* styles rely on the custom attribute "order-state" */
  .order[order-state="new"] {
    color: green;
  }

  .order[order-state="pending"] {
    color: blue;
  }

  .order[order-state="canceled"] {
    color: red;
  }
</style>

<div class="order" order-state="new">
  A new order.
</div>

<div class="order" order-state="pending">
  A pending order.
</div>

<div class="order" order-state="canceled">
  A canceled order.
</div>

Tại sao sử dụng một thuộc tính được thích hợp hơn để có các lớp học như .order-state-new, .order-state-pending, order-state-canceled?

Bởi vì một thuộc tính sẽ thuận tiện hơn để quản lý. Trạng thái có thể được thay đổi dễ dàng như:

// a bit simpler than removing old/adding a new class
div.setAttribute('order-state', 'canceled');

Nhưng có thể có một vấn đề có thể xảy ra với các thuộc tính tùy chỉnh. Điều gì sẽ xảy ra nếu chúng ta sử dụng một thuộc tính phi tiêu chuẩn cho các mục đích của mình và sau đó tiêu chuẩn giới thiệu nó và khiến nó hoạt động? Ngôn ngữ HTML vẫn tồn tại, nó phát triển và nhiều thuộc tính xuất hiện hơn để phù hợp với nhu cầu của các nhà phát triển. Có thể có những tác động không mong muốn trong trường hợp đó

Để tránh xung đột, có các thuộc tính data- * .

Tất cả các thuộc tính bắt đầu bằng “data-” được dành riêng cho lập trình viên sử dụng. Chúng có sẵn trong thuộc tính dataset.

Ví dụ: nếu một thuộc tính elem có tên “data-about”, thuộc tính đó có sẵn dưới dạng elem.dataset.about.

Như thế này:

<body data-about="Elephants">
<script>
  alert(document.body.dataset.about); // Elephants
</script>

Nhiều từ các thuộc tính như data-order-state trở thành lạc đà: dataset.orderState.

Đây là một ví dụ về “trạng thái đơn hàng” được viết lại:

<style>
  .order[data-order-state="new"] {
    color: green;
  }

  .order[data-order-state="pending"] {
    color: blue;
  }

  .order[data-order-state="canceled"] {
    color: red;
  }
</style>

<div id="order" class="order" data-order-state="new">
  A new order.
</div>

<script>
  // read
  alert(order.dataset.orderState); // new

  // modify
  order.dataset.orderState = "pending"; // (*)
</script>

Sử dụng thuộc tính data-* là một cách hợp lệ, an toàn để chuyển dữ liệu tùy chỉnh.

Xin lưu ý rằng chúng ta không chỉ có thể đọc mà còn có thể sửa đổi các thuộc tính dữ liệu. Sau đó CSS ​​cập nhật chế độ xem cho phù hợp: trong ví dụ trên, dòng cuối cùng (*)thay đổi màu thành màu xanh lam.

6. Tóm lược

  • Đặc tính(Attributes) – là những gì được viết bằng HTML.
  • Thuộc tính(Properties) – là những gì trong các đối tượng DOM.

Một so sánh nhỏ:


Tính chấtThuộc tính
KiểuBất kỳ giá trị nào, thuộc tính tiêu chuẩn có các loại được mô tả trong thông số kỹ thuậtMột chuỗi
TênTên phân biệt chữ hoa chữ thườngTên không phân biệt chữ hoa chữ thường

Các phương pháp để làm việc với các thuộc tính là:

  • elem.hasAttribute(name) – để kiểm tra sự tồn tại.
  • elem.getAttribute(name) – để nhận giá trị.
  • elem.setAttribute(name, value) – để đặt giá trị.
  • elem.removeAttribute(name) – để loại bỏ thuộc tính.
  • elem.attributes là một tập hợp của tất cả các thuộc tính.

Đối với hầu hết các tình huống, việc sử dụng thuộc tính DOM là tốt hơn. Chúng ta chỉ nên tham khảo các thuộc tính khi các thuộc tính DOM không phù hợp với chúng ta, khi chúng ta cần các thuộc tính chính xác, ví dụ:

  • Chúng ta cần một thuộc tính không chuẩn. Nhưng nếu nó bắt đầu bằng data-, thì chúng ta nên sử dụng dataset.
  • Chúng ta muốn đọc giá trị “như được viết” trong HTML. Giá trị của thuộc tính DOM có thể khác nhau, ví dụ: thuộc tính href luôn là một URL đầy đủ và chúng ta có thể muốn lấy giá trị “gố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!