Cafedev chia sẻ cho ace về Thuộc tính điều hướng DOM rất tuyệt vời khi các phần tử gần nhau. Nếu không thì sao? Làm thế nào để lấy một phần tử tùy ý của trang?

Có các phương thức tìm kiếm bổ sung cho điều đó.

1.document.getElementById hoặc chỉ id

Nếu một phần tử có thuộc tính id, chúng ta có thể lấy phần tử đó bằng cách sử dụng phương thức document.getElementById(id), bất kể nó ở đâu.

Ví dụ:

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // get the element
  let elem = document.getElementById('elem');

  // make its background red
  elem.style.background = 'red';
</script>

Ngoài ra, có một biến toàn cục được đặt tên bởi tham chiếu id đến phần tử:

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

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // elem is a reference to DOM-element with id="elem"
  elem.style.background = 'red';

  // id="elem-content" has a hyphen inside, so it can't be a variable name
  // ...but we can access it using square brackets: window['elem-content']
</script>

… Đó là trừ khi chúng ta khai báo một biến JavaScript có cùng tên, thì nó sẽ được ưu tiên:

<div id="elem"></div>

<script>
  let elem = 5; // now elem is 5, not a reference to <div id="elem">

  alert(elem); // 5
</script>

Vui lòng không sử dụng các biến toàn cục có tên id để truy cập các phần tử

Hành vi này được mô tả trong thông số kỹ thuật , vì vậy nó là loại chuẩn. Nhưng nó được hỗ trợ chủ yếu để tương thích.

Trình duyệt cố gắng giúp chúng ta bằng cách trộn không gian tên của JS và DOM. Điều đó tốt cho các tập lệnh đơn giản, được nội dung trong HTML, nhưng nhìn chung không phải là một điều tốt. Có thể có xung đột đặt tên. Ngoài ra, khi một người đọc mã JS và không có HTML trong chế độ xem, thì không rõ biến đến từ đâu.

Ở đây trong hướng dẫn, chúng ta sử dụng id để tham chiếu trực tiếp một phần tử cho ngắn gọn, khi rõ ràng phần tử đến từ đâu.

Trong thực tế document.getElementById là phương thức được ưu tiên.

Các id phải là duy nhất

Các id phải là duy nhất. Chỉ có thể có một phần tử trong tài liệu với phần tử đã cho id.

Nếu có nhiều phần tử giống nhau id, thì hành vi của các phương thức sử dụng nó là không thể đoán trước, ví dụ: document.getElementById có thể trả về bất kỳ phần tử nào một cách ngẫu nhiên. Vì vậy, hãy tuân thủ quy tắc và giữ id độc nhất.

Chỉ document.getElementById, không anyElem.getElementById

Phương thức getElementById chỉ có thể được gọi trên đối tượng document. Nó tìm kiếm những gì đã có id trong toàn bộ tài liệu.

2.querySelectorAll

Cho đến nay, phương thức linh hoạt nhất, elem.querySelectorAll(css)trả về tất cả các phần tử bên trong elem khớp với bộ chọn CSS đã cho.

Ở đây chúng ta tìm kiếm tất cả các phần tử <li> là con cuối cùng:

<ul>
  <li>The</li>
  <li>test</li>
</ul>
<ul>
  <li>has</li>
  <li>passed</li>
</ul>
<script>
  let elements = document.querySelectorAll('ul > li:last-child');

  for (let elem of elements) {
    alert(elem.innerHTML); // "test", "passed"
  }
</script>

Phương thức này thực sự mạnh mẽ, vì có thể sử dụng bất kỳ bộ chọn CSS nào.

Cũng có thể sử dụng các lớp giả

Các lớp giả trong bộ chọn CSS thích :hover và :active cũng được hỗ trợ. Ví dụ: document.querySelectorAll(‘:hover’) sẽ trả về tập hợp với các phần tử mà con trỏ đã kết thúc ngay bây giờ (theo thứ tự lồng nhau: từ ngoài cùng <html> đến lồng nhau nhất).

3. querySelector

Lệnh gọi elem.querySelector(css) trả về phần tử đầu tiên cho bộ chọn CSS đã cho.

Nói cách khác, kết quả giống như elem.querySelectorAll(css)[0], nhưng cái sau là tìm kiếm tất cả các phần tử và chọn một phần tử, trong khi elem.querySelector chỉ tìm một phần tử. Vì vậy, nó nhanh hơn và cũng ngắn hơn để viết.

4. matches

Các phương thức trước đây đang tìm kiếm DOM.

Các elem.matches (css) không tìm kiếm bất cứ điều gì, nó chỉ đơn thuần là kiểm tra nếu elem phù hợp với CSS-selector nhất định. Nó trả về true hoặc false.

Phương thức này rất hữu ích khi chúng ta lặp lại các phần tử (như trong một mảng hoặc một cái gì đó) và cố gắng lọc ra những phần tử mà chúng ta quan tâm.

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

<a href="http://example.com/file.zip">...</a>
<a href="http://ya.ru">...</a>

<script>
  // can be any collection instead of document.body.children
  for (let elem of document.body.children) {
    if (elem.matches('a[href$="zip"]')) {
      alert("The archive reference: " + elem.href );
    }
  }
</script>

5. gần nhất – closest

Tổ tiên(Ancestors) của một phần tử là: cha mẹ, cha mẹ, cha mẹ của nó, v.v. Tổ tiên cùng nhau tạo thành chuỗi phần tử cha mẹ đến phần ngọn.

Phương thức này có vẻ elem.closest(css) là tổ tiên gần nhất phù hợp với CSS-selector. Bản thân elem nó cũng được bao gồm trong tìm kiếm.

Nói cách khác, phương thức closest đi lên từ phần tử và kiểm tra từng phụ huynh. Nếu nó khớp với bộ chọn, thì quá trình tìm kiếm sẽ dừng lại và tổ tiên được trả về.

Ví dụ:

<h1>Contents</h1>

<div class="contents">
  <ul class="book">
    <li class="chapter">Chapter 1</li>
    <li class="chapter">Chapter 1</li>
  </ul>
</div>

<script>
  let chapter = document.querySelector('.chapter'); // LI

  alert(chapter.closest('.book')); // UL
  alert(chapter.closest('.contents')); // DIV

  alert(chapter.closest('h1')); // null (because h1 is not an ancestor)
</script>

6. getElementsBy *

Ngoài ra còn có các phương thức khác để tìm kiếm các nút theo thẻ, lớp, v.v.

Ngày nay, chúng chủ yếu là lịch sử, vì querySelector nó mạnh hơn và ngắn hơn để viết.

Vì vậy, ở đây chúng ta chủ yếu đề cập đến chúng để hoàn thiện, trong khi bạn vẫn có thể tìm thấy chúng trong các tập lệnh cũ.

  • elem.getElementsByTagName(tag) tìm kiếm các phần tử có thẻ đã cho và trả về tập hợp chúng. Các tham số tag cũng có thể là một ngôi sao “*” cho “bất kỳ thẻ”.
  • elem.getElementsByClassName(className) trả về các phần tử có lớp CSS đã cho.
  • document.getElementsByName(name)trả về các phần tử có thuộc tính name đã cho, trên toàn tài liệu. Rất hiếm khi được sử dụng.

Ví dụ:

// get all divs in the document
let divs = document.getElementsByTagName('div');

Tìm tất cả các thẻ input bên trong bảng:

<table id="table">
  <tr>
    <td>Your age:</td>

    <td>
      <label>
        <input type="radio" name="age" value="young" checked> less than 18
      </label>
      <label>
        <input type="radio" name="age" value="mature"> from 18 to 50
      </label>
      <label>
        <input type="radio" name="age" value="senior"> more than 60
      </label>
    </td>
  </tr>
</table>

<script>
  let inputs = table.getElementsByTagName('input');

  for (let input of inputs) {
    alert( input.value + ': ' + input.checked );
  }
</script>

Đừng quên ký tự “s”!

Các nhà phát triển mới làm quen đôi khi quên chữ cái “s”. Đó là, họ cố gắng gọi getElementByTagName thay vì .getElementsByTagName

Chữ cái “s” không có trong getElementById, vì nó trả về một phần tử duy nhất. Nhưng getElementsByTagName trả về một tập hợp các phần tử, vì vậy có “s” bên trong.

Nó trả về một tập hợp, không phải một phần tử!

Một sai lầm phổ biến khác của người mới là viết:

// doesn't work
document.getElementsByTagName('input').value = 5;

Điều đó sẽ không hoạt động, bởi vì nó lấy một tập hợp các đầu vào và gán giá trị cho nó chứ không phải cho các phần tử bên trong nó.

Chúng ta nên lặp lại tập hợp hoặc lấy một phần tử theo chỉ mục của nó, rồi gán, như thế này:

// should work (if there's an input)
document.getElementsByTagName('input')[0].value = 5;

Tìm kiếm phần tử .article:

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

<form name="my-form">
  <div class="article">Article</div>
  <div class="long article">Long article</div>
</form>

<script>
  // find by name attribute
  let form = document.getElementsByName('my-form')[0];

  // find by class inside the form
  let articles = form.getElementsByClassName('article');
  alert(articles.length); // 2, found two elements with class "article"
</script>

7. Bộ sưu tập đang sống

Tất cả các phương thức “getElementsBy*”trả về một tập hợp đang sống(live). Các bộ sưu tập như vậy luôn phản ánh trạng thái hiện tại của tài liệu và “tự động cập nhật” khi nó thay đổi.

Trong ví dụ dưới đây, có hai tập lệnh.

  1. Cái đầu tiên tạo một tham chiếu đến bộ sưu tập của <div>. Như bây giờ, chiều dài của nó là 1.
  2. Các tập lệnh thứ hai chạy sau khi trình duyệt gặp một tập lệnh khác <div>, vì vậy độ dài của nó là 2.
<div>First div</div>

<script>
  let divs = document.getElementsByTagName('div');
  alert(divs.length); // 1
</script>

<div>Second div</div>

<script>
  alert(divs.length); // 2
</script>

Ngược lại, querySelectorAll trả về một tập hợp tĩnh . Nó giống như một mảng cố định của các phần tử.

Nếu chúng ta sử dụng nó thay thế, thì cả hai tập lệnh sẽ xuất 1:

<div>First div</div>

<script>
  let divs = document.querySelectorAll('div');
  alert(divs.length); // 1
</script>

<div>Second div</div>

<script>
  alert(divs.length); // 1
</script>

Bây giờ chúng ta có thể dễ dàng nhận thấy sự khác biệt. Bộ sưu tập tĩnh không tăng sau khi xuất hiện một bộ sưu tập mới div trong tài liệu.

8. Tóm lược

Có 6 phương thức chính để tìm kiếm các nút trong DOM:

phương thứcTìm kiếm theo …Có thể gọi một phần tử không?Trực tiếp?
querySelectorBộ chọn CSS
querySelectorAllBộ chọn CSS
getElementByIdid
getElementsByNamename
getElementsByTagNamethẻ hoặc ‘*’
getElementsByClassNamelớp học

Cho đến nay, được sử dụng nhiều nhất là querySelector và querySelectorAll, nhưng đôi khi getElementBy* có thể hữu ích hoặc được tìm thấy trong các tập lệnh cũ.

Bên cạnh đó:

  • Phải elem.matches(css) kiểm tra xem có elem khớp với bộ chọn CSS đã cho hay không.
  • Cần elem.closest(css) phải tìm kiếm tổ tiên gần nhất phù hợp với CSS-selector đã cho. Bản thân elem nó cũng được kiểm tra.

Và chúng ta hãy đề cập đến một phương thức nữa ở đây để kiểm tra mối quan hệ con-cha mẹ, vì nó đôi khi hữu ích:

  • elemA.contains(elemB) trả về true nếu elem bởi bên trong elemA(hậu duệ của elemA) hoặc khi elemA==elemB.

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!