0
0
Lập trình
Sơn Tùng Lê
Sơn Tùng Lê103931498422911686980

Khám Phá Mô Hình Đối Tượng Tài Liệu (DOM) Trong JavaScript

Đăng vào 8 tháng trước

• 7 phút đọc

Khám Phá Mô Hình Đối Tượng Tài Liệu (DOM) Trong JavaScript

Trong hành trình tìm hiểu sâu về các khối xây dựng của Kỹ Thuật Frontend, tôi đã tham gia một khóa học trên Frontend Masters có tên là Vanilla JavaScript: You might not need a Framework do Maximiliano Firtman giảng dạy. Đây là một khóa học thú vị mà bạn chắc chắn nên tham khảo.

Bài viết này là nỗ lực của tôi để chia sẻ những gì tôi đã học được từ khóa học và làm sâu sắc thêm sự hiểu biết của mình về Vanilla JS. Một trong những khái niệm chính mà khóa học khám phá một cách sâu sắc là Mô Hình Đối Tượng Tài Liệu (DOM), là nền tảng cho cách JavaScript tương tác với các trang web.

Tại Sao DOM Quan Trọng?

Hiểu rõ cách thức hoạt động của DOM là rất quan trọng đối với một Kỹ Sư Frontend, vì nó giúp chúng ta đưa ra những quyết định tốt hơn và có ý thức về hiệu suất khi làm việc với JavaScript, dù bạn đang sử dụng Vanilla JS hay các framework như React.

Định Nghĩa Về DOM

Tôi đã ghi nhớ những định nghĩa về DOM từ khóa học:

  • DOM là một đại diện trong bộ nhớ của HTML của bạn.
  • DOM là một cấu trúc mà các trình duyệt tạo ra trong bộ nhớ khi hiển thị các trang.
  • DOM là một cầu nối kết nối các trang web với JavaScript thông qua cấu trúc tài liệu.

Để tương tác với DOM, trình duyệt cung cấp các Giao Diện Lập Trình Ứng Dụng (API) gọi là DOM API. DOM API là một API của trình duyệt được công khai cho các nhà phát triển để thao tác với DOM từ một ngôn ngữ kịch bản như JavaScript.

Cấu Trúc Của DOM

Nói chung, DOM là một cấu trúc trong bộ nhớ trong khi DOM API cung cấp các phương thức để thay đổi cấu trúc đó. API này có sẵn trên nhiều đối tượng như:

  1. Đối Tượng Window (Đối tượng toàn cục)
  2. Đối Tượng Document (đại diện cho DOM hiện tại)
  3. Mỗi phần tử HTML trong một ứng dụng web

Làm Việc Với DOM

Như đã đề cập trước đó, các API DOM cho phép chúng ta tương tác và thao tác với DOM. Một số cách để thực hiện điều này bao gồm:

1. Chọn Các Phần Tử Từ DOM

Chúng ta có thể chọn các phần tử từ DOM bằng nhiều phương pháp khác nhau:

- Bằng ID

Để lấy một phần tử HTML theo thuộc tính id của nó trong JavaScript, chúng ta sử dụng phương thức document.getElementById(). Phương thức này trả về một phần tử HTML duy nhất (hoặc null nếu không tìm thấy phần tử nào). Vì ID là duy nhất, nó chỉ trả về phần tử đầu tiên mà nó gặp với ID đó.

html Copy
<!-- HTML -->
<div id="header">Chào mừng đến với trang của tôi</div>
<p id="intro">Đây là đoạn giới thiệu</p>
javascript Copy
// JavaScript
const headerElement = document.getElementById('header');
const introElement = document.getElementById('intro');

console.log(headerElement.textContent); // "Chào mừng đến với trang của tôi"
console.log(introElement.textContent); // "Đây là đoạn giới thiệu"

// Nếu ID không tồn tại
const nonExistent = document.getElementById('notFound');
console.log(nonExistent); // null

- Bằng Classname

Chúng ta cũng có thể lấy một phần tử HTML theo classname của nó, bằng cách sử dụng phương thức document.getElementsByClassName(). Phương thức này trả về một HTMLCollection (bộ sưu tập HTML) - một bộ sưu tập đang sống tự động cập nhật khi DOM thay đổi.

html Copy
<!-- HTML -->
<div class="card">Thẻ 1</div>
<div class="card">Thẻ 2</div>
<p class="card">Thẻ 3</p>
javascript Copy
// JavaScript
const cardElements = document.getElementsByClassName('card');
console.log(cardElements.length); // 3

// Truy cập các phần tử riêng lẻ
console.log(cardElements[0].textContent); // "Thẻ 1"
console.log(cardElements[1].textContent); // "Thẻ 2"

// Bộ sưu tập sống - cập nhật tự động
const newCard = document.createElement('div');
newCard.className = 'card';
newCard.textContent = 'Thẻ 4';
document.body.appendChild(newCard);

console.log(cardElements.length); // Bây giờ là 4 (tự động cập nhật!)

Lưu ý - HTMLCollections sống không có tất cả các hàm hiện đại của Array như filter, map, reduce hay forEach. Các hàm mảng hiện đại có thể được thêm vào một HTMLCollection sống bằng cách tạo một mảng từ nó bằng phương thức Array.from().

- Bằng Tên

Các phần tử cũng có thể được lấy bằng thuộc tính tên của chúng với phương thức document.getElementsByName(). Phương thức này trả về một NodeList (Bộ sưu tập tĩnh). Vì thuộc tính tên không yêu cầu phải duy nhất (khác với id), phương thức này thường trả về một bộ sưu tập các phần tử, ngay cả khi chỉ có một phần tử với tên đó.

html Copy
<form>
  <input type="radio" name="gender" value="male"> Nam
  <input type="radio" name="gender" value="female"> Nữ
  <input type="radio" name="gender" value="other"> Khác
</form>
javascript Copy
// JavaScript
const genderInputs = document.getElementsByName('gender');
console.log(genderInputs.length); // 3

// Lặp qua bộ sưu tập
genderInputs.forEach((input, index) => {
    console.log(`Input ${index + 1}: ${input.value}`);
});
// Kết quả:
// Input 1: male
// Input 2: female  
// Input 3: other

// Kiểm tra cái nào được chọn
const selectedGender = Array.from(genderInputs).find(input => input.checked);
console.log(selectedGender?.value); // Giá trị của nút radio được chọn

- Bằng CSS Selectors

Các phần tử cũng có thể được lấy bằng cách sử dụng CSS selectors thông qua phương thức document.querySelector(selector). Phương thức này trả về phần tử đầu tiên trong tài liệu khớp với selector CSS đã chỉ định. Nếu không tìm thấy phần tử nào phù hợp, nó trả về null.

html Copy
<!-- HTML -->
<div class="container">
  <h1 id="main-title">Chào mừng</h1>
  <p class="intro">Đoạn văn đầu tiên</p>
  <p class="intro">Đoạn văn thứ hai</p>
  <button type="submit" data-action="save">Lưu</button>
  <input type="email" placeholder="Nhập email">
</div>
javascript Copy
// Chọn theo ID (giống như getElementById)
const title = document.querySelector('#main-title');
console.log(title.textContent); // "Chào mừng"

// Chọn theo class (trả về phần tử phù hợp đầu tiên)
const firstIntro = document.querySelector('.intro');
console.log(firstIntro.textContent); // "Đoạn văn đầu tiên"

// Chọn theo thẻ phần tử
const firstParagraph = document.querySelector('p');
console.log(firstParagraph.textContent); // "Đoạn văn đầu tiên"

// Chọn theo thuộc tính
const submitButton = document.querySelector('[type="submit"]');
const saveButton = document.querySelector('[data-action="save"]');
console.log(submitButton.textContent); // "Lưu"

// Chọn theo selector CSS phức tạp
const emailInput = document.querySelector('input[type="email"]');
console.log(emailInput.placeholder); // "Nhập email"

// Selector tổ tiên
const containerTitle = document.querySelector('.container h1');
console.log(containerTitle.textContent); // "Chào mừng"

// Nếu selector không khớp với bất kỳ phần tử nào
const nonExistent = document.querySelector('.not-found');
console.log(nonExistent); // null

Các Thực Hành Tốt Nhất Khi Làm Việc Với DOM

  • Sử Dụng querySelectorquerySelectorAll: Khi bạn cần chọn một phần tử hoặc nhiều phần tử, hãy sử dụng querySelector cho phần tử đầu tiên và querySelectorAll cho tất cả các phần tử phù hợp. Điều này giúp mã của bạn dễ đọc hơn và linh hoạt hơn với các lựa chọn CSS.
  • Tránh Sử Dụng innerHTML: Khi thao tác với nội dung HTML, tránh sử dụng innerHTML vì nó có thể gây ra các vấn đề bảo mật như XSS (Cross-Site Scripting). Thay vào đó, sử dụng các phương thức như textContent hoặc appendChild để thêm hoặc sửa đổi nội dung một cách an toàn hơn.
  • Lưu Trữ Các Phần Tử Trong Biến: Để tối ưu hóa hiệu suất, hãy lưu trữ các phần tử DOM trong biến thay vì truy cập chúng nhiều lần. Điều này giảm thiểu số lần trình duyệt phải tìm kiếm trong DOM.

Những Cạm Bẫy Thường Gặp

  • Không Kiểm Tra null: Khi sử dụng getElementById, querySelector, hoặc các phương thức khác, hãy luôn kiểm tra giá trị trả về không phải là null trước khi sử dụng nó.
  • Làm Việc Với Các HTMLCollections: Như đã đề cập, các HTMLCollections không phải là mảng thực sự và không hỗ trợ tất cả các phương thức mảng. Hãy cẩn thận khi cố gắng sử dụng các hàm mảng trên chúng.

Mẹo Tối Ưu Hiệu Suất

  • Giảm Thiểu Số Lần Thao Tác DOM: Mỗi lần bạn thao tác với DOM (thêm, xóa, sửa đổi phần tử), hiệu suất có thể giảm. Cố gắng nhóm các thay đổi lại với nhau, sau đó thực hiện một lần thay vì thực hiện nhiều lần.
  • Sử Dụng Fragment: Khi bạn cần thêm nhiều phần tử vào DOM, hãy sử dụng DocumentFragment để giảm thiểu ảnh hưởng đến hiệu suất.

Giải Quyết Vấn Đề

Nếu bạn gặp vấn đề khi làm việc với DOM, hãy kiểm tra lại:

  • Console: Sử dụng console để kiểm tra giá trị của các phần tử bạn đang làm việc. Điều này giúp bạn xác định xem bạn đã chọn đúng phần tử hay chưa.
  • Debugger: Sử dụng công cụ phát triển của trình duyệt để theo dõi mã và kiểm tra các giá trị biến.

Kết Luận

Hiểu cách chọn các phần tử DOM là nền tảng của phát triển web bằng JavaScript. Dù bạn đang sử dụng getElementById() cho các phần tử duy nhất, getElementsByClassName() cho các bộ sưu tập, getElementsByName() cho các phần tử biểu mẫu, hay querySelector() cho lựa chọn linh hoạt dựa trên CSS, việc làm chủ các phương pháp này sẽ cung cấp cho bạn công cụ để tương tác với bất kỳ trang web nào.

Trong phần tiếp theo của chuỗi bài viết này, chúng ta sẽ khám phá cách thao tác với các phần tử đã chọn - sửa đổi nội dung, thuộc tính, kiểu dáng và cấu trúc của chúng. Chúng ta cũng sẽ đi sâu vào việc tạo các phần tử mới và xử lý sự kiện để xây dựng các ứng dụng web thực sự tương tác.
DOM có thể có vẻ phức tạp lúc đầu, nhưng một khi bạn hiểu những nguyên tắc cơ bản về lựa chọn này, bạn sẽ đi đúng hướng để trở nên thành thạo trong JavaScript thuần.

Và hãy nhớ, kiến thức này sẽ trực tiếp chuyển giao cho công việc với các framework - React, Vue và các framework khác đều được xây dựng dựa trên những nguyên tắc DOM này.

Hãy theo dõi phần 2, nơi chúng ta sẽ khám phá thêm về thao tác DOM.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào