Tìm Hiểu Về DOM Javascript

Trong bài viết hôm nay chúng ta sẽ cùng nhau đi vào tim hiểu DOM (Document Object Model). Đây là một yếu tố chính trong việc xây dựng những ứng dụng web tương tác bằng Javascript. Nào bây giờ chúng ta hãy cùng nhau đi vào tìm hiểu nhé!
DOM Là Gì?
DOM (Document Object Model) là một giao diện lập trình ứng dụng (API) cho các tài liệu HTML, XML. Nó cung cấp một cấu trúc dạng cây, trong đó mỗi nút (node) là một đối tượng đại diện cho một phần tử của trang web.
Trước khi có DOM, các trang web chỉ có nội dung tĩnh. Tuy nhiên nhờ vào sự xuất hiện của DOM thì nó đã cho phép lập trình viên tạo ra các trang web có khả năng tương tác giúp người dùng có trải nghiệm tốt hơn. Các ứng dụng web hiện đại như hệ thống quản lý, trang thương mại điện tử… thì đều dựa vào DOM để thay đổi giao diện theo thời gian thực mà không cần tải lại trang.
Cấu Trúc Cây DOM
DOM được tổ chức dưới dạng cây phân cấp với các “nút” (nodes). Mỗi nút có thể là:
- Element Node: Đại diện cho các phần tử HTML như
<div>
,<h1>
,<p>
,… - Text Node: Chứa nội dung văn bản bên trong các phần tử.
- Attribute Node: Chứa các thuộc tính của phần tử như
class
,id
,src
,…
Ví dụ, với đoạn mã HTML đơn giản:
<!DOCTYPE html>
<html>
<head>
<title>Chào mừng đến với Cửa Hàng Cà Phê</title>
</head>
<body>
<h1>Giới thiệu về Cà Phê Phin</h1>
<p>Đến với cửa hàng của Nguyễn Văn A để thưởng thức những ly cà phê đặc sản.</p>
</body>
</html>
Khi trình duyệt tải trang này, nó sẽ tạo ra một cây DOM như sau:
document (node gốc)
└── <html>
├── <head>
│ └── <title>
│ └── Text: "Chào mừng đến với Cửa Hàng Cà Phê"
└── <body>
├── <h1>
│ └── Text: "Giới thiệu về Cà Phê Phin"
└── <p>
└── Text: "Đến với cửa hàng của Nguyễn Văn A để thưởng
thức những ly cà phê đặc sản."
Quan Hệ Cha – Con, Anh Em
Mỗi phần tử trong DOM có thể có:
- Parent Node (nút cha): Phần tử bao quanh nó.
- Child Node (nút con): Các phần tử bên trong phần tử đó.
- Sibling Node (nút anh em): Các phần tử cùng cấp trong cây DOM.
Ví dụ, trong đoạn mã trên, thẻ <body> là cha của <h1> và <p>, đồng thời <h1> và <p> là anh em của nhau. Nhờ mối quan hệ này, ta có thể dễ dàng truy xuất và thao tác các phần tử dựa vào vị trí của chúng trong cây DOM.
Truy Xuất và Thao Tác DOM Bằng JavaScript
JavaScript cung cấp rất nhiều phương thức để truy xuất các phần tử trong DOM. Dưới đây là một số phương pháp phổ biến mà chúng ta sẽ thường hay sử dụng.
Phương Thức getElementById
Phương thức này dùng để lấy một phần tử có thuộc tính id xác định. Ví dụ:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Quản Lý Khách Hàng</title>
</head>
<body>
<h1 id="storeName">Cửa hàng của Nguyễn Văn A</h1>
<p id="storeDesc">Chuyên bán cà phê Phin và trà sữa đặc sản.</p>
<button id="btnChange">Cập nhật thông tin</button>
<script>
// Truy xuất phần tử bằng id
const storeName = document.getElementById('storeName');
const btnChange = document.getElementById('btnChange');
// Xử lý sự kiện click
btnChange.addEventListener('click', function() {
storeName.innerText = 'Cửa hàng của Nguyễn Văn A - Ưu đãi đặc biệt!';
});
</script>
</body>
</html>
Giải thích:
- Khi trang được tải, JavaScript sẽ truy xuất phần tử có id=”storeName” và id=”btnChange”.
- Khi người dùng click vào nút, nội dung của phần tử <h1> sẽ được thay đổi thành chuỗi mới.
See the Pen
Phương Thức getElementById by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Phương Thức querySelector và querySelectorAll
Hai phương thức này mạnh mẽ hơn, cho phép bạn sử dụng các bộ chọn (selectors) tương tự như CSS.
Ví dụ với querySelector
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Danh Sách Sản Phẩm</title>
</head>
<body>
<ul class="product-list">
<li class="product-item">Cà phê Phin</li>
<li class="product-item">Trà sữa Trân Châu</li>
<li class="product-item">Sinh tố Xoài</li>
</ul>
<button id="highlightBtn">Nhấn để làm nổi bật sản phẩm đầu tiên</button>
<script>
const productItem = document.querySelector('.product-item');
const highlightBtn = document.getElementById('highlightBtn');
highlightBtn.addEventListener('click', function() {
productItem.style.backgroundColor = 'yellow';
});
</script>
</body>
</html>
Giải thích:
- querySelector cho phép chọn phần tử đầu tiên phù hợp với CSS được chỉ định.
- Khi người dùng click vào nút, phần tử đầu tiên sẽ có màu nền được đổi thành vàng.
See the Pen
Ví dụ với querySelector by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Ví dụ với querySelectorAll
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Cập Nhật Giá Sản Phẩm</title>
</head>
<body>
<ul class="product-list">
<li class="product-item" data-price="50000">Bánh mì Sài Gòn</li>
<li class="product-item" data-price="75000">Phở bò Hà Nội</li>
<li class="product-item" data-price="60000">Bún chả Nha Trang</li>
</ul>
<button id="updatePrice">Cập nhật giá</button>
<script>
const productItems = document.querySelectorAll('.product-item');
const updatePrice = document.getElementById('updatePrice');
updatePrice.addEventListener('click', function() {
productItems.forEach(item => {
// Lấy giá trị ban đầu từ thuộc tính data-price
let price = parseInt(item.getAttribute('data-price'));
// Tăng giá 10%
price = Math.round(price * 1.1);
// Cập nhật nội dung hiển thị
item.innerText += ` - Giá mới: ${price} VND`;
});
});
</script>
</body>
</html>
Giải thích:
- Ở ví dụ trên, chúng ta dùng querySelectorAll để truy xuất tất cả các phần tử có class product-item.
- Khi click nút, giá của mỗi sản phẩm được tăng thêm 10% và được cập nhật vào nội dung hiển thị.
See the Pen
Untitled by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Thay Đổi Thuộc Tính và Nội Dung Của Phần Tử
Các thuộc tính của phần tử có thể được truy xuất và thay đổi qua JavaScript. Một số ví dụ thông dụng bao gồm:
- Thay đổi nội dung: Dùng thuộc tính innerText hoặc innerHTML
- innerText chỉ thay đổi văn bản thuần.
- innerHTML có thể thay đổi cả nội dung HTML bên trong phần tử.
- Thay đổi thuộc tính: Dùng setAttribute hoặc trực tiếp truy cập thuộc tính của đối tượng.
Ví dụ thay đổi hình ảnh:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Cập Nhật Ảnh Sản Phẩm</title>
</head>
<body>
<img id="productImage" src="https://images.unsplash.com/photo-1515442261605-65987783cb6a?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="Cà phê cũ" width="300">
<button id="changeImage">Thay đổi ảnh sản phẩm</button>
<script>
const productImage = document.getElementById('productImage');
const changeImage = document.getElementById('changeImage');
changeImage.addEventListener('click', function() {
// Thay đổi src và alt của hình ảnh
productImage.setAttribute('src', 'https://images.unsplash.com/photo-1506372023823-741c83b836fe?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D');
productImage.setAttribute('alt', 'Cà phê mới');
});
</script>
</body>
</html>
Giải thích:
- Khi nút được click, hình ảnh thay đổi từ “Cà phê cũ” sang “Cà phê mới”, đồng thời cập nhật thuộc tính alt cho hình ảnh.
See the Pen
Untitled by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Xử Lý Sự Kiện
Một trong những điểm mạnh của DOM là khả năng xử lý sự kiện. JavaScript cho phép bạn sử dụng event listener để lắng nghe và phản hồi lại các hành động của người dùng như click chuột, di chuyển chuột…
Cơ Chế Xử Lý Sự Kiện
Mỗi phần tử trong DOM có thể lắng nghe (listen) và phản hồi (respond) lại các sự kiện. Một số sự kiện phổ biến gồm:
- click: khi người dùng click vào phần tử.
- mouseover và mouseout: khi chuột di chuyển vào hoặc ra khỏi phần tử.
- keydown và keyup: khi người dùng nhấn hoặc thả phím.
Ví dụ đơn giản:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xử Lý Sự Kiện</title>
</head>
<body>
<button id="btnDemo">Nhấn vào đây!</button>
<p id="demoText">Chưa có sự kiện nào xảy ra.</p>
<script>
const btnDemo = document.getElementById('btnDemo');
const demoText = document.getElementById('demoText');
btnDemo.addEventListener('click', function() {
demoText.innerText = 'Bạn vừa click vào nút!';
});
</script>
</body>
</html>
Giải thích:
- Khi người dùng click vào nút, đoạn văn được cập nhật nội dung, thông báo rằng nút đã được click.
See the Pen
Xử Lý Sự Kiện by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Delegation
Khi có nhiều phần tử cần xử lý cùng một loại sự kiện, thay vì xử lý cho từng phần tử thì bạn có thể sử dụng “delegation”. Nó cho phép bạn đăng ký hàm xử lý trên phần tử cha và sau đó mới xác định phần tử con nào sẽ phát sinh sự kiện.
Ví dụ: Giả sử bạn có một danh sách các sản phẩm và muốn xử lý sự kiện click cho từng sản phẩm.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Delegation Sự Kiện</title>
</head>
<body>
<ul id="productList">
<li class="product-item">Bánh mì Sài Gòn</li>
<li class="product-item">Phở bò Hà Nội</li>
<li class="product-item">Bún chả Nha Trang</li>
</ul>
<script>
const productList = document.getElementById('productList');
productList.addEventListener('click', function(event) {
// Kiểm tra xem phần tử được click có thuộc class "product-item" hay không
if (event.target && event.target.matches('.product-item')) {
alert(`Bạn vừa chọn: ${event.target.innerText}`);
}
});
</script>
</body>
</html>
Giải thích:
- Thay vì gắn sự kiện cho từng li, chúng ta gắn sự kiện cho danh sách (ul).
- Khi một phần tử con (li) được click, hàm xử lý sẽ kiểm tra xem phần tử đó có thuộc class product-item hay không, rồi thực hiện hành động tương ứng.
See the Pen
Delegation by haycuoilennao19 (@haycuoilennao19)
on CodePen.
CácThao Tác DOM Nâng Cao
Tạo Phần Tử Mới
JavaScript cho phép bạn tạo ra các phần tử mới và thêm chúng vào DOM. Ví dụ, tạo một phần tử <div> mới để hiển thị thông báo khuyến mãi:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Thông Báo Khuyến Mãi</title>
</head>
<body>
<button id="createBanner">Tạo thông báo</button>
<div id="bannerContainer"></div>
<script>
const createBanner = document.getElementById('createBanner');
const bannerContainer = document.getElementById('bannerContainer');
createBanner.addEventListener('click', function() {
// Tạo một phần tử div mới
const banner = document.createElement('div');
banner.innerText = 'Chương trình khuyến mãi: Giảm giá 20% cho tất cả sản phẩm!';
banner.style.backgroundColor = '#ffeb3b';
banner.style.padding = '15px';
banner.style.textAlign = 'center';
banner.style.marginTop = '10px';
// Thêm phần tử vào container
bannerContainer.appendChild(banner);
});
</script>
</body>
</html>
Giải thích:
- Dùng document.createElement để tạo phần tử mới.
- Áp dụng các thuộc tính CSS thông qua JavaScript.
- Thêm phần tử vào DOM bằng appendChild.
See the Pen
Tạo Phần Tử Mới by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Xóa Phần Tử
Đôi khi, bạn cần loại bỏ một phần tử khỏi trang. Dùng removeChild hoặc phương thức remove:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xóa Phần Tử</title>
</head>
<body>
<div id="notification">
<p>Thông báo: Cửa hàng sắp đóng cửa vào lúc 22:00.</p>
<button id="closeNotification">Đóng</button>
</div>
<script>
const notification = document.getElementById('notification');
const closeNotification = document.getElementById('closeNotification');
closeNotification.addEventListener('click', function() {
// Xóa thông báo khỏi DOM
notification.remove();
});
</script>
</body>
</html>
Giải thích:
- Khi người dùng click nút “Đóng”, phần tử thông báo sẽ bị loại bỏ khỏi cây DOM.
See the Pen
Xóa Phần Tử by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Di Chuyển (Clone) Phần Tử
Bạn cũng có thể sao chép một phần tử bằng cách sử dụng cloneNode
.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sao Chép Phần Tử</title>
</head>
<body>
<div id="original">
<p>Đây là thông tin gốc của cửa hàng.</p>
</div>
<button id="cloneBtn">Sao chép thông tin</button>
<div id="cloneContainer"></div>
<script>
const original = document.getElementById('original');
const cloneBtn = document.getElementById('cloneBtn');
const cloneContainer = document.getElementById('cloneContainer');
cloneBtn.addEventListener('click', function() {
// Sao chép phần tử gốc, với đối số true để sao chép cả nội dung con
const clone = original.cloneNode(true);
cloneContainer.appendChild(clone);
});
</script>
</body>
</html>
Giải thích:
- Phương thức cloneNode(true) tạo một bản sao hoàn chỉnh của phần tử cùng với các phần tử con.
See the Pen
Di Chuyển (Clone) Phần Tử by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Thay Đổi CSS Inline
Bạn có thể thay đổi các thuộc tính CSS trực tiếp thông qua thuộc tính style của phần tử.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Thay Đổi Giao Diện</title>
</head>
<body>
<p id="greeting">Xin chào, bạn đã đến với website của chúng tôi!</p>
<button id="changeStyle">Thay đổi giao diện</button>
<script>
const greeting = document.getElementById('greeting');
const changeStyle = document.getElementById('changeStyle');
changeStyle.addEventListener('click', function() {
greeting.style.color = '#d32f2f';
greeting.style.fontSize = '24px';
greeting.style.fontWeight = 'bold';
});
</script>
</body>
</html>
Giải thích:
- Khi nút “Thay đổi giao diện” được click, thuộc tính CSS của đoạn văn sẽ được cập nhật ngay lập tức.
See the Pen
Thay Đổi CSS Inline by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Thêm/Xóa Class CSS
Một cách hiệu quả để thay đổi giao diện là thao tác với các class CSS thông qua thuộc tính classList.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Thao Tác Class CSS</title>
<style>
.highlight {
background-color: #ffeb3b;
padding: 10px;
border-radius: 5px;
}
</style>
</head>
<body>
<p id="info">Đây là thông tin cần làm nổi bật.</p>
<button id="toggleHighlight">Bật/Tắt làm nổi bật</button>
<script>
const info = document.getElementById('info');
const toggleHighlight = document.getElementById('toggleHighlight');
toggleHighlight.addEventListener('click', function() {
info.classList.toggle('highlight');
});
</script>
</body>
</html>
Giải thích:
- Sử dụng classList.toggle giúp chuyển đổi class của phần tử một cách linh hoạt, tạo hiệu ứng bật/tắt cho giao diện.
See the Pen
Thêm/Xóa Class CSS by haycuoilennao19 (@haycuoilennao19)
on CodePen.
Lưu Ý Khi Làm Việc Với DOM Javascript
Tối Ưu Hiệu Suất
- Tránh thao tác DOM quá nhiều: Mỗi khi bạn thay đổi DOM, trình duyệt sẽ phải render lại trang. Hãy cố gắng gom các thay đổi lại với nhau hoặc sử dụng kỹ thuật “document fragment” để giảm thiểu số lần render.
- Sử dụng Delegation: Như đã đề cập ở phần xử lý sự kiện, thay vì gắn hàm xử lý cho từng phần tử, hãy sử dụng delegation để giảm tải bộ nhớ.
Sử Dụng Các Framework và Thư Viện
Nếu dự án của bạn trở nên quá phức tạp, có thể xem xét sử dụng các framework như React, Vue hay Angular. Những framework này giúp quản lý DOM ảo (virtual DOM) và tối ưu hóa việc cập nhật giao diện.
Kiểm Tra Trình Duyệt
Mặc dù hầu hết các trình duyệt hiện nay đều hỗ trợ đầy đủ các phương thức của DOM, nhưng đôi khi sự khác biệt về cách thức render hoặc xử lý sự kiện giữa các trình duyệt cũ và mới vẫn cần được kiểm tra. Luôn kiểm tra trên nhiều trình duyệt khác nhau để đảm bảo trải nghiệm người dùng luôn được đảm bảo nhé.
Tổng Kết
Trong bài viết này, chúng ta đã cùng nhau khám phá chi tiết về DOM – Document Object Model – từ định nghĩa, cấu trúc cây, cách thức truy xuất và thao tác DOM bằng JavaScript. Mong bài viết này sẽ hữu ích với bạn. Chúc bạn thành công!