Bài 5.2: Thao tác với DOM trong JavaScript

Chào mừng các bạn quay trở lại với hành trình làm chủ Front-end! Sau khi đã biết cấu trúc HTML là nền móng và CSS là lớp áo choàng lộng lẫy, giờ là lúc chúng ta thổi hồn cho trang web bằng JavaScript. Và "chiếc đũa thần" để làm điều đó chính là DOM - Document Object Model.

Hãy tưởng tượng trang web HTML của bạn là một ngôi nhà tĩnh lặng. DOM chính là bản đồ chi tiết của ngôi nhà đó, mô tả mọi thứ từ nền móng (thẻ <html>), các tầng (thẻ <body>, <head>), từng căn phòng (các <div>, <section>), cửa ra vào (thẻ <a>), cửa sổ (thẻ <img>), đồ đạc bên trong (các <p>, <h1>, <ul>, <li>, <button>, <input>, v.v.).

Quan trọng hơn, DOM không chỉ là bản đồ tĩnh. Nó là một giao diện lập trình ứng dụng (API) cho phép JavaScript truy cập và thay đổi mọi thứ trên trang web sau khi nó đã được tải lên trình duyệt.

Nói cách khác, DOM là cây cầu nối quyền năng giữa JavaScript và nội dung hiển thị trên trình duyệt của người dùng!

Với DOM, chúng ta có thể:

  • Thay đổi nội dung văn bản của bất kỳ đoạn nào.
  • Thêm hoặc xóa các phần tử (như thêm một mục mới vào danh sách, xóa một bình luận).
  • Thay đổi thuộc tính của phần tử (như đổi địa chỉ liên kết của thẻ <a>, thay đổi nguồn ảnh của thẻ <img>).
  • Thay đổi kiểu dáng (CSS) của phần tử (như đổi màu chữ, ẩn/hiện một khối).
  • Phản ứng lại các sự kiện của người dùng (như khi họ click chuột, gõ phím, di chuyển chuột).

Hãy cùng lặn sâu vào cách chúng ta sử dụng JavaScript để "điều khiển" ngôi nhà DOM này nhé!

Chọn lọc các phần tử - Tìm kiếm "kho báu" trong DOM

Trước khi làm bất cứ điều gì với một phần tử, chúng ta cần phải tìm và chọn lọc nó trong "cây" DOM rộng lớn. JavaScript cung cấp nhiều phương thức cực kỳ hữu ích cho việc này.

1. Chọn theo ID: getElementById()

Đây là cách đơn giảnhiệu quả nhất để lấy ra một phần tử duy nhất khi bạn biết chính xác id của nó. Nhớ rằng, id phải là duy nhất trên toàn bộ trang HTML.

<div id="phan-tu-dac-biet">Đây là một phần tử quan trọng!</div>
// Chọn phần tử có id="phan-tu-dac-biet"
const phanTuDacBiet = document.getElementById('phan-tu-dac-biet');

// Kiểm tra xem chúng ta đã lấy đúng chưa
console.log(phanTuDacBiet); // Xuất ra thẻ div đó

Giải thích: document.getElementById('...') tìm kiếm trong toàn bộ tài liệu HTML (được biểu diễn bởi đối tượng document) và trả về đối tượng phần tử đầu tiên có thuộc tính id khớp với chuỗi bạn cung cấp. Nếu không tìm thấy, nó sẽ trả về null.

2. Chọn theo Class Name: getElementsByClassName()

Khi bạn muốn chọn nhiều phần tử có cùng một lớp (class), đây là phương thức phù hợp. Nó sẽ trả về một tập hợp các phần tử.

<ul>
  <li class="muc-danh-sach">Mục 1</li>
  <li class="muc-danh-sach">Mục 2</li>
  <li class="muc-danh-sach khac">Mục 3</li>
</ul>
<div class="muc-danh-sach">Đây cũng có class này!</div>
// Chọn tất cả các phần tử có class="muc-danh-sach"
const cacMucDanhSach = document.getElementsByClassName('muc-danh-sach');

// Đây là một tập hợp (HTMLCollection), bạn có thể lặp qua nó
console.log(cacMucDanhSach); // Xuất ra một HTMLCollection
console.log(cacMucDanhSach[0]); // Truy cập phần tử đầu tiên
console.log(cacMucDanhSach.length); // Số lượng phần tử tìm được

// Lặp qua tập hợp
for (let i = 0; i < cacMucDanhSach.length; i++) {
  console.log(cacMucDanhSach[i].textContent);
}

Giải thích: document.getElementsByClassName('...') tìm và trả về một HTMLCollection (một dạng giống mảng, nhưng không phải mảng đầy đủ chức năng) chứa tất cả các phần tử có chứa tên lớp được chỉ định. Bạn có thể truy cập các phần tử bằng chỉ số hoặc lặp qua tập hợp này.

3. Chọn theo Tên Thẻ (Tag Name): getElementsByTagName()

Tương tự như chọn theo class, phương thức này trả về một tập hợp các phần tử dựa trên tên thẻ HTML của chúng (ví dụ: p, a, li, div).

<h1>Tiêu đề chính</h1>
<p>Đoạn văn bản thứ nhất.</p>
<p>Đoạn văn bản thứ hai.</p>
<span>Một đoạn span.</span>
// Chọn tất cả các thẻ <p>
const cacDoanVan = document.getElementsByTagName('p');

console.log(cacDoanVan); // Xuất ra một HTMLCollection chứa 2 thẻ <p>
console.log(cacDoanVan[1].textContent); // Truy cập nội dung đoạn thứ hai

Giải thích: document.getElementsByTagName('...') trả về một HTMLCollection chứa tất cả các phần tử có tên thẻ khớp với chuỗi được cung cấp.

4. Chọn theo CSS Selector (Đầu tiên): querySelector()

Đây là phương thức hiện đại và cực kỳ mạnh mẽ! Bạn có thể sử dụng bất kỳ bộ chọn CSS hợp lệ nào để tìm kiếm phần tử. Nó sẽ trả về phần tử đầu tiên khớp với bộ chọn đó.

<div class="container">
  <p id="intro">Đoạn giới thiệu.</p>
  <p class="text-content">Nội dung chính.</p>
  <a href="#" class="link">Liên kết 1</a>
  <a href="#" class="link">Liên kết 2</a>
</div>
// Chọn phần tử <p> đầu tiên bên trong div có class="container"
const doanGioiThieu = document.querySelector('.container p');
console.log(doanGioiThieu.textContent); // "Đoạn giới thiệu."

// Chọn phần tử có id="intro" (cách khác getElementById)
const introById = document.querySelector('#intro');
console.log(introById.textContent); // "Đoạn giới thiệu."

// Chọn thẻ <a> đầu tiên có class="link"
const linkDauTien = document.querySelector('a.link');
console.log(linkDauTien); // Xuất ra thẻ <a> đầu tiên

Giải thích: document.querySelector('css-selector') trả về đối tượng phần tử đầu tiên trong tài liệu khớp với bộ chọn CSS được cung cấp. Khả năng sử dụng bộ chọn CSS giúp bạn nhắm mục tiêu các phần tử một cách rất linh hoạt (ví dụ: div > p, input[type="text"], li:nth-child(2)). Nếu không tìm thấy, trả về null.

5. Chọn theo CSS Selector (Tất cả): querySelectorAll()

Tương tự như querySelector(), nhưng phương thức này trả về tất cả các phần tử khớp với bộ chọn CSS được cung cấp. Nó trả về một NodeList.

<ul>
  <li class="item">Apple</li>
  <li class="item selected">Banana</li>
  <li class="item">Cherry</li>
</ul>
// Chọn tất cả các thẻ <li> có class="item"
const allItems = document.querySelectorAll('li.item');

console.log(allItems); // Xuất ra một NodeList
console.log(allItems[1].textContent); // "Banana"

// NodeList cũng có thể lặp qua (thường dùng forEach tiện hơn HTMLCollection)
allItems.forEach(item => {
  console.log(item.textContent);
});

// Chọn tất cả các thẻ li có class="item" VÀ class="selected"
const selectedItems = document.querySelectorAll('li.item.selected');
console.log(selectedItems[0].textContent); // "Banana"

Giải thích: document.querySelectorAll('css-selector') trả về một NodeList chứa tất cả các đối tượng phần tử trong tài liệu khớp với bộ chọn CSS. NodeList thường hỗ trợ các phương thức lặp như forEach, giúp việc xử lý tập hợp phần tử trở nên dễ dàng hơn HTMLCollection.

Lưu ý nhỏ: HTMLCollectionNodeList đều là các tập hợp giống mảng, nhưng có một số khác biệt nhỏ (ví dụ: NodeList từ querySelectorAll thường "tĩnh", trong khi HTMLCollection từ getElementsBy... thường "động"). Với hầu hết các tác vụ cơ bản, sự khác biệt này không quá quan trọng, bạn chỉ cần biết cách lặp qua chúng (với for hoặc forEach).

Thay đổi Nội dung - "Viết lại" câu chuyện trên trang của bạn

Sau khi đã chọn được phần tử, bạn có thể dễ dàng thay đổi nội dung hiển thị bên trong nó.

1. textContent

Thay đổi hoặc lấy ra nội dung chỉ là văn bản thuần túy. Bất kỳ thẻ HTML nào bên trong sẽ bị loại bỏ.

<div id="van-ban">
  Xin chào, <strong>thế giới</strong>!
</div>
const divVanBan = document.getElementById('van-ban');

// Lấy nội dung văn bản hiện tại
console.log(divVanBan.textContent); // Xuất ra: "Xin chào, thế giới!" (loại bỏ thẻ strong)

// Thay đổi nội dung
divVanBan.textContent = 'Nội dung mới chỉ có văn bản.';
console.log(divVanBan.innerHTML); // Bây giờ chỉ là "Nội dung mới chỉ có văn bản."

Giải thích: element.textContent truy cập vào nội dung văn bản bên trong phần tử. Khi gán giá trị mới, nó sẽ ghi đè toàn bộ nội dung cũ và đảm bảo nội dung mới được coi là văn bản thuần túy.

2. innerHTML

Thay đổi hoặc lấy ra nội dung bao gồm cả cấu trúc HTML. Sử dụng cẩn thận vì nó có thể gây ra lỗ hổng bảo mật (Cross-Site Scripting - XSS) nếu bạn chèn nội dung từ người dùng mà không làm sạch.

<div id="html-noi-dung">
  Đoạn văn bản cũ.
</div>
const divHtml = document.getElementById('html-noi-dung');

// Lấy nội dung HTML hiện tại
console.log(divHtml.innerHTML); // Xuất ra: "  Đoạn văn bản cũ.\n" (bao gồm khoảng trắng và dòng mới)

// Thay đổi nội dung, chèn cả thẻ HTML
divHtml.innerHTML = '<h3>Tiêu đề phụ mới</h3><p>Đoạn nội dung <em>in nghiêng</em>.</p>';
// Trang web sẽ hiển thị tiêu đề h3 và đoạn p với chữ in nghiêng

Giải thích: element.innerHTML truy cập vào toàn bộ nội dung bên trong phần tử, bao gồm cả các thẻ HTML con. Khi gán giá trị mới, trình duyệt sẽ phân tích chuỗi HTML đó và tạo ra cấu trúc DOM tương ứng bên trong phần tử đích. Hãy cẩn trọng khi sử dụng innerHTML với dữ liệu không tin cậy.

Thao tác với Thuộc tính - Điều chỉnh "đặc điểm" của phần tử

Các phần tử HTML có nhiều thuộc tính như src (cho ảnh), href (cho liên kết), class, id, disabled, value, v.v. JavaScript cho phép bạn thay đổi hoặc đọc các thuộc tính này.

1. Truy cập trực tiếp (Đối với các thuộc tính phổ biến)

Nhiều thuộc tính HTML phổ biến có thể được truy cập trực tiếp như một thuộc tính của đối tượng DOM.

<a id="link-trang" href="https://old-website.com">Truy cập trang cũ</a>
<img id="anh-minh-hoa" src="old-image.jpg" alt="Ảnh cũ">
<input type="text" id="truong-nhap" value="Giá trị mặc định">
const linkTrang = document.getElementById('link-trang');
const anhMinhHoa = document.getElementById('anh-minh-hoa');
const truongNhap = document.getElementById('truong-nhap');

// Đọc thuộc tính
console.log(linkTrang.href); // "https://old-website.com" (thường là URL đầy đủ)
console.log(anhMinhHoa.src); // "old-image.jpg" (thường là URL đầy đủ)
console.log(truongNhap.value); // "Giá trị mặc định"

// Thay đổi thuộc tính
linkTrang.href = 'https://new-website.com'; // Đổi đích liên kết
anhMinhHoa.src = 'new-image.png';       // Đổi ảnh
truongNhap.value = 'Giá trị đã thay đổi'; // Đổi giá trị của input

// Thay đổi thuộc tính đặc biệt như disabled (cho input, button...)
const nutBam = document.createElement('button');
nutBam.textContent = 'Click Me';
document.body.appendChild(nutBam); // Thêm nút vào body

nutBam.disabled = true; // Vô hiệu hóa nút
console.log(nutBam.disabled); // true

Giải thích: Với các thuộc tính HTML chuẩn và phổ biến, đối tượng DOM tương ứng thường có một thuộc tính JavaScript cùng tên hoặc gần giống để bạn truy cập (ví dụ: href trong HTML tương ứng với href trong JS, class tương ứng với className hoặc classList, for tương ứng với htmlFor). Việc truy cập trực tiếp này rất tiện lợi.

2. setAttribute(), getAttribute(), removeAttribute()

Các phương thức này cho phép bạn làm việc với bất kỳ thuộc tính nào, kể cả các thuộc tính tùy chỉnh của riêng bạn.

<div id="phan-tu-du-lieu" data-id="123" data-type="user">Thông tin người dùng</div>
const phanTuDuLieu = document.getElementById('phan-tu-du-lieu');

// Lấy giá trị của một thuộc tính
console.log(phanTuDuLieu.getAttribute('id'));      // "phan-tu-du-lieu"
console.log(phanTuDuLieu.getAttribute('data-id'));   // "123"
console.log(phanTuDuLieu.getAttribute('data-type')); // "user"

// Thay đổi giá trị của một thuộc tính
phanTuDuLieu.setAttribute('data-id', '456');
phanTuDuLieu.setAttribute('title', 'Đây là phần tử chứa dữ liệu'); // Thêm thuộc tính title mới

// Kiểm tra lại
console.log(phanTuDuLieu.getAttribute('data-id')); // "456"
console.log(phanTuDuLieu.getAttribute('title'));  // "Đây là phần tử chứa dữ liệu"

// Xóa một thuộc tính
phanTuDuLieu.removeAttribute('data-type');
console.log(phanTuDuLieu.getAttribute('data-type')); // null

Giải thích:

  • element.getAttribute('attribute-name') trả về giá trị của thuộc tính được đặt tên.
  • element.setAttribute('attribute-name', 'new-value') đặt hoặc cập nhật giá trị của thuộc tính.
  • element.removeAttribute('attribute-name') xóa thuộc tính khỏi phần tử. Các phương thức này rất hữu ích khi bạn làm việc với các thuộc tính ít phổ biến hoặc các thuộc tính data-* tùy chỉnh.

Biến hóa Giao diện với Style và Class - Làm đẹp cho trang web

Thay đổi giao diện của phần tử là một trong những ứng dụng phổ biến nhất của DOM manipulation.

1. Thay đổi Style trực tiếp (Inline Style)

Bạn có thể truy cập thuộc tính style của phần tử, trả về một đối tượng chứa tất cả các thuộc tính CSS inline của nó.

<p id="doan-mau">Đoạn văn bản này sẽ đổi màu.</p>
const doanMau = document.getElementById('doan-mau');

// Thay đổi màu chữ (lưu ý cách viết camelCase trong JS thay vì kebab-case trong CSS)
doanMau.style.color = 'red';

// Thay đổi cỡ chữ
doanMau.style.fontSize = '20px';

// Thêm đường viền
doanMau.style.border = '1px solid blue';

// Các thuộc tính CSS có dấu gạch ngang (ví dụ: background-color)
doanMau.style.backgroundColor = 'yellow';

Giải thích: element.style trả về một đối tượng CSSStyleDeclaration. Bạn có thể truy cập và gán giá trị cho các thuộc tính CSS bằng cách sử dụng cú pháp camelCase (ví dụ: backgroundColor thay vì background-color, fontSize thay vì font-size).

Tuy nhiên, việc thao tác style trực tiếp như thế này thường không phải là cách tốt nhất vì nó làm trộn lẫn logic (JavaScript) với trình bày (CSS) và khó quản lý khi có nhiều thay đổi style phức tạp. Cách khuyến khích là sử dụng CSS class.

2. Thao tác với CSS Class (classList)

Đây là cách đúng đắn và mạnh mẽ hơn để thay đổi giao diện dựa trên trạng thái hoặc hành động. Bạn định nghĩa các style cho từng class trong file CSS, sau đó dùng JavaScript để thêm/xóa các class đó khỏi phần tử. Đối tượng classList cung cấp các phương thức tiện lợi.

<style>
  .normal-text {
    color: black;
    font-size: 16px;
  }
  .highlight-text {
    color: red;
    font-weight: bold;
    border: 2px solid yellow;
  }
  .hidden {
    display: none;
  }
</style>

<div id="hop-van-ban" class="normal-text">Đây là một đoạn văn bản bình thường.</div>
const hopVanBan = document.getElementById('hop-van-ban');

// Thêm một class mới
hopVanBan.classList.add('highlight-text'); // Bây giờ nó có cả class "normal-text" và "highlight-text"

// Xóa một class
hopVanBan.classList.remove('normal-text'); // Chỉ còn class "highlight-text"

// Kiểm tra xem có class nào đó không
console.log(hopVanBan.classList.contains('highlight-text')); // true
console.log(hopVanBan.classList.contains('normal-text'));    // false

// Chuyển đổi class (thêm nếu chưa có, xóa nếu đã có)
hopVanBan.classList.toggle('hidden'); // Thêm class "hidden" (phần tử bị ẩn)
hopVanBan.classList.toggle('hidden'); // Xóa class "hidden" (phần tử hiện lại)

// Lấy danh sách các class hiện có (dưới dạng DOMTokenList)
console.log(hopVanBan.classList);

Giải thích: element.classList trả về một đối tượng DOMTokenList đại diện cho danh sách các class của phần tử. Các phương thức add(), remove(), toggle(), contains() giúp bạn quản lý danh sách class một cách dễ dàng. Đây là phương pháp ưu tiên để thay đổi giao diện bằng JavaScript vì nó giữ cho CSS và JS tách biệt, dễ bảo trì hơn.

Tạo và Xóa Phần tử - Xây dựng và Dọn dẹp "ngôi nhà" DOM

Bạn không chỉ thay đổi những gì đã có sẵn mà còn có thể tạo ra các phần tử mới hoặc loại bỏ những phần tử không cần thiết.

1. Tạo Phần tử Mới: document.createElement()

Phương thức này tạo ra một đối tượng phần tử mới trong bộ nhớ, nhưng nó chưa xuất hiện trên trang web cho đến khi bạn thêm nó vào cây DOM hiện có.

// Tạo một thẻ <div> mới
const divMoi = document.createElement('div');

// Tạo một thẻ <p> mới
const pMoi = document.createElement('p');

// Tạo một thẻ <img> mới
const imgMoi = document.createElement('img');

Giải thích: document.createElement('tagName') tạo ra một node (nút) kiểu Element với tên thẻ được chỉ định. Nó chưa có nội dung hay thuộc tính gì.

2. Thêm Phần tử vào DOM: appendChild(), insertBefore()

Sau khi tạo phần tử, bạn cần "đính" nó vào một vị trí nào đó trong cây DOM để nó hiển thị trên trang.

<div id="container-add">
  <p>Đây là nội dung cũ trong container.</p>
</div>
const container = document.getElementById('container-add');

// Tạo một đoạn văn bản mới
const doanMoi = document.createElement('p');
doanMoi.textContent = 'Đây là đoạn văn bản mới được thêm vào.';

// Thêm đoạn văn bản mới vào cuối container
container.appendChild(doanMoi); // Đoạn mới sẽ xuất hiện sau đoạn cũ

// Tạo một tiêu đề mới
const tieuDeMoi = document.createElement('h3');
tieuDeMoi.textContent = 'Tiêu đề được thêm vào đầu.';

// Thêm tiêu đề mới vào ĐẦU container (trước phần tử con đầu tiên)
container.insertBefore(tieuDeMoi, container.firstChild); // Tham số 1: phần tử cần thêm, Tham số 2: phần tử con ĐÍCH để thêm VÀO TRƯỚC nó.

// Nếu muốn thêm vào trước một phần tử con cụ thể khác:
// const phanTuConThuHai = container.children[1]; // Lấy đoạn p cũ
// container.insertBefore(tieuDeMoi, phanTuConThuHai); // Thêm tiêu đề mới vào trước đoạn p cũ

Giải thích:

  • parentNode.appendChild(childNode): Thêm childNode vào làm con cuối cùng của parentNode.
  • parentNode.insertBefore(newNode, referenceNode): Thêm newNode vào làm con của parentNode, ngay trước referenceNode. referenceNode phải là một phần tử con hiện có của parentNode.
3. Xóa Phần tử khỏi DOM: removeChild(), element.remove()

Khi không cần một phần tử nào đó nữa, bạn có thể xóa nó đi.

<ul id="danh-sach-xoa">
  <li id="muc-1">Mục 1</li>
  <li id="muc-2">Mục 2 (sẽ bị xóa)</li>
  <li id="muc-3">Mục 3</li>
</ul>
const danhSach = document.getElementById('danh-sach-xoa');
const mucCanXoa = document.getElementById('muc-2');

// Cách truyền thống: Xóa phần tử con thông qua phần tử cha
danhSach.removeChild(mucCanXoa); // Xóa mucCanXoa ra khỏi danhSach

// Cách hiện đại: Phần tử tự xóa chính nó (phổ biến hơn)
const muc3 = document.getElementById('muc-3');
muc3.remove(); // Xóa trực tiếp muc3 khỏi DOM

Giải thích:

  • parentNode.removeChild(childNode): Phương thức này được gọi trên phần tử cha và yêu cầu bạn truyền vào phần tử con mà bạn muốn xóa.
  • element.remove(): Phương thức này được gọi trực tiếp trên phần tử mà bạn muốn xóa. Đây là cách đơn giảnđược ưa chuộng hơn trong các kịch bản hiện đại.

Bắt Sự kiện - Lắng nghe "tiếng nói" của người dùng

Trang web động là trang web phản hồi lại các hành động của người dùng. Đây là lúc Event Handling (Xử lý sự kiện) tỏa sáng, và DOM đóng vai trò trung tâm.

1. addEventListener()

Đây là phương thức tiêu chuẩn và khuyến khích để gắn các hàm xử lý sự kiện vào các phần tử. Nó cho phép bạn thêm nhiều trình xử lý cho cùng một sự kiện trên cùng một phần tử và quản lý chúng một cách tốt hơn.

<button id="nut-click">Bấm vào đây!</button>
<div id="ket-qua">Chưa có gì xảy ra.</div>
const nutClick = document.getElementById('nut-click');
const divKetQua = document.getElementById('ket-qua');

// Định nghĩa hàm sẽ chạy khi sự kiện xảy ra
function xuLyClick() {
  divKetQua.textContent = 'Bạn vừa bấm nút!';
  // Có thể làm nhiều thứ khác ở đây...
  nutClick.style.backgroundColor = 'lightgreen';
}

// Gắn hàm xử lý vào sự kiện 'click' của nút
nutClick.addEventListener('click', xuLyClick);

// Bạn có thể thêm nhiều hàm xử lý khác cho cùng một sự kiện
nutClick.addEventListener('click', () => {
  console.log('Nút đã được bấm lần nữa!');
});

// Ví dụ sự kiện khác: mouseover (khi di chuột vào)
divKetQua.addEventListener('mouseover', () => {
    divKetQua.style.color = 'blue';
});

// Ví dụ sự kiện khác: mouseout (khi di chuột ra)
divKetQua.addEventListener('mouseout', () => {
    divKetQua.style.color = 'black'; // Trở lại màu ban đầu
});

Giải thích: element.addEventListener('eventName', handlerFunction):

  • eventName: Tên của sự kiện bạn muốn lắng nghe (chuỗi, ví dụ: 'click', 'mouseover', 'keydown', 'submit', 'load').
  • handlerFunction: Hàm (function) sẽ được thực thi khi sự kiện xảy ra trên phần tử đó. Hàm này tự động nhận một đối tượng Event làm tham số đầu tiên, chứa thông tin chi tiết về sự kiện.

Các sự kiện phổ biến:

  • Mouse Events: click, dblclick, mousedown, mouseup, mouseover, mouseout, mousemove.
  • Keyboard Events: keydown, keypress, keyup.
  • Form Events: submit, input, change, focus, blur.
  • Document Events: DOMContentLoaded, load.
  • CSS Events: transitionend, animationend.
Đối tượng Event (event hoặc e)

Khi hàm xử lý sự kiện được gọi, nó nhận một đối tượng Event. Đối tượng này chứa rất nhiều thông tin hữu ích về sự kiện vừa xảy ra.

<button id="nut-su-kien">Xem thông tin sự kiện</button>
<p id="thong-tin-su-kien"></p>
const nutSuKien = document.getElementById('nut-su-kien');
const pThongTin = document.getElementById('thong-tin-su-kien');

nutSuKien.addEventListener('click', (event) => {
  console.log(event); // Xem toàn bộ đối tượng sự kiện trong console

  // Lấy loại sự kiện
  pThongTin.textContent = `Loại sự kiện: ${event.type}. `;

  // Lấy phần tử kích hoạt sự kiện (rất hữu ích trong Event Delegation)
  pThongTin.textContent += `Phần tử đích: ${event.target.tagName}. `;

  // Tọa độ chuột (cho sự kiện chuột)
  if (event.clientX && event.clientY) {
     pThongTin.textContent += `Tọa độ: (${event.clientX}, ${event.clientY}).`;
  }

  // Ngăn chặn hành động mặc định của trình duyệt (ví dụ: ngăn link chuyển trang)
  // event.preventDefault();
});

Giải thích: Đối tượng event (thường được đặt tên ngắn gọn là e) chứa các thuộc tính như type (tên sự kiện), target (phần tử mà sự kiện ban đầu xảy ra), clientX, clientY (tọa độ chuột), key (phím bấm), v.v. Phương thức preventDefault() rất quan trọng để ngăn chặn hành vi mặc định của trình duyệt (ví dụ: form gửi đi, link chuyển hướng trang).

Một vài Mẹo và Thực tiễn Tốt

  • Tránh thao tác DOM quá nhiều và liên tục: Mỗi lần thay đổi DOM có thể khiến trình duyệt phải tính toán lại và vẽ lại giao diện (repaint/reflow), tốn tài nguyên. Cố gắng nhóm các thay đổi lại hoặc sử dụng các kỹ thuật tối ưu (như tạo một fragment rồi thêm vào DOM một lần).
  • Sử dụng classList thay vì style trực tiếp: Như đã nói, điều này giúp tách biệt style và logic, dễ quản lý hơn.
  • Sử dụng Event Delegation: Thay vì thêm trình xử lý sự kiện cho từng phần tử con (ví dụ: mỗi mục trong danh sách <li>), hãy thêm một trình xử lý duy nhất vào phần tử cha (<ul>). Khi sự kiện xảy ra trên phần tử con, nó sẽ "nổi bọt" lên phần tử cha, và bạn có thể kiểm tra event.target để xem phần tử con nào đã kích hoạt nó. Kỹ thuật này hiệu quả hơn cho danh sách lớn hoặc các phần tử được thêm vào sau.
  • Chờ DOM sẵn sàng: Đảm bảo mã JavaScript của bạn chạy sau khi cây DOM đã được tải đầy đủ. Cách phổ biến nhất là đặt mã JS ở cuối thẻ <body> hoặc sử dụng sự kiện DOMContentLoaded.
// Cách phổ biến nhất: Đặt script cuối body
<body>
  <!-- ... HTML content ... -->
  <script src="your-script.js"></script>
</body>

// Hoặc dùng sự kiện DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
  // Mã JS thao tác với DOM của bạn ở đây
  const phanTu = document.getElementById('some-id');
  // ...
});

Comments

There are no comments at the moment.