JavaScript 與 DOM 的進階應用能夠讓網頁更加動態、互動,並提升網頁的性能和使用者體驗。進階的 DOM 操作通常包括事件委派、動態樣式管理、模板化渲染、虛擬 DOM 技術、性能優化等。以下是一些 JavaScript 與 DOM 進階應用的概念和範例。
事件委派是一種高效的事件處理技術。它利用事件冒泡機制,只在父元素上監聽事件,並根據事件的來源進行具體處理。這在處理大量動態生成的子元素時非常有用,能顯著提高性能,減少事件監聽器的數量。
<ul id="itemList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<button id="addItem">Add Item</button>
<script>
const itemList = document.getElementById('itemList');
let itemCount = 3;
// 使用事件委派:在父元素 <ul> 上監聽點擊事件
itemList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
alert(event.target.textContent); // 顯示被點擊的列表項
}
});
// 動態添加新列表項
document.getElementById('addItem').addEventListener('click', function() {
itemCount++;
const newItem = document.createElement('li');
newItem.textContent = `Item ${itemCount}`;
itemList.appendChild(newItem);
});
</script>
在這個例子中,無論列表項有多少個,我們只需要在 <ul>
元素上綁定一次點擊事件,而不用為每個 <li>
單獨設置監聽器。
虛擬 DOM 是一個輕量級的 JavaScript 表示,反映了實際的 DOM 結構。它允許我們在 JavaScript 中先操作虛擬 DOM,然後將最小的改變同步到真實 DOM。這可以避免直接頻繁操作 DOM 導致的性能問題。React 就是基於虛擬 DOM 概念的前端框架。
function createVirtualElement(tag, props, ...children) {
return { tag, props, children };
}
function render(virtualElement) {
const element = document.createElement(virtualElement.tag);
for (const prop in virtualElement.props) {
element.setAttribute(prop, virtualElement.props[prop]);
}
virtualElement.children.forEach(child => {
const childNode = typeof child === 'string' ? document.createTextNode(child) : render(child);
element.appendChild(childNode);
});
return element;
}
const oldVirtualTree = createVirtualElement('div', { id: 'container' },
createVirtualElement('h1', {}, 'Hello World'),
createVirtualElement('p', {}, 'This is a paragraph')
);
const newVirtualTree = createVirtualElement('div', { id: 'container' },
createVirtualElement('h1', {}, 'Hello Virtual DOM'),
createVirtualElement('p', {}, 'Updated paragraph')
);
function diffAndPatch(parent, oldTree, newTree) {
if (!oldTree) {
parent.appendChild(render(newTree)); // 全部重新渲染
} else if (!newTree) {
parent.removeChild(parent.firstChild); // 移除節點
} else if (oldTree.tag !== newTree.tag) {
parent.replaceChild(render(newTree), parent.firstChild); // 替換節點
} else {
// 進一步比較並更新子節點
}
}
const root = document.getElementById('app');
diffAndPatch(root, oldVirtualTree, newVirtualTree);
這個例子展示了虛擬 DOM 的基本運作方式,將新舊虛擬 DOM 樹進行比較,僅更新需要變更的部分。
直接頻繁修改 DOM 會導致性能下降,因為每次操作都可能觸發重繪和重排。優化方法之一是將多個 DOM 操作合併到一起執行,或使用 documentFragment
來避免重複渲染。
documentFragment
批量操作const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const newDiv = document.createElement('div');
newDiv.textContent = `Item ${i}`;
fragment.appendChild(newDiv);
}
document.body.appendChild(fragment); // 一次性將所有新元素添加到 DOM
這種方法避免了每次添加元素都導致頁面重新渲染,將 1000 個新元素一次性添加到頁面中,大幅提升性能。
在處理大量動態內容時,經常需要使用模板化渲染技術。這能夠讓我們以更清晰、易於維護的方式來生成 HTML 結構。
<ul id="userList"></ul>
<script>
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 28 }
];
function generateUserTemplate(user) {
return `<li>${user.name} - ${user.age} years old</li>`;
}
const userList = document.getElementById('userList');
userList.innerHTML = users.map(generateUserTemplate).join('');
</script>
這裡利用模板函數 generateUserTemplate()
,動態生成使用者列表的 HTML,並插入到 DOM 中。這種方式能夠保持程式碼結構簡潔、明確。
Intersection Observer API
允許我們監測一個元素是否進入或離開可視區域,這對於懶加載(lazy loading)圖片或無限滾動的應用程式特別有用。
<img class="lazy" data-src="image1.jpg" alt="Lazy Image 1">
<img class="lazy" data-src="image2.jpg" alt="Lazy Image 2">
<script>
const lazyImages = document.querySelectorAll('img.lazy');
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 加載圖片
img.classList.remove('lazy');
observer.unobserve(img); // 停止觀察這個元素
}
});
});
lazyImages.forEach(img => observer.observe(img));
</script>
Intersection Observer API
可以監視圖片元素是否進入可視區域,當圖片進入時再將 data-src
資料加載到 src
屬性中,從而實現懶加載功能。
JavaScript 允許我們創建自訂事件,並透過 dispatchEvent()
方法在 DOM 上觸發。這可以用來實現更靈活的事件通信。
const button = document.querySelector('button');
// 創建自訂事件
const customEvent = new Event('customClick');
// 綁定事件處理器
button.addEventListener('customClick', () => {
console.log('Custom event triggered!');
});
// 手動觸發自訂事件
button.dispatchEvent(customEvent);
在這個例子中,我們創建了一個名為 customClick
的自訂事件,並在按鈕上手動觸發它。這種自訂事件對於在不同模組或組件之間進行通信非常有用。
Shadow DOM
是 Web Components 中的一部分,它允許你將 DOM 的某些部分封裝起來,避免樣式和 JavaScript 變量的
衝突。這可以用來創建獨立、可重用的組件。
<div id="shadowHost"></div>
<script>
const host = document.getElementById('shadowHost');
const shadowRoot = host.attachShadow({ mode: 'open' });
const shadowContent = document.createElement('p');
shadowContent.textContent = 'This is inside the shadow DOM';
shadowRoot.appendChild(shadowContent);
</script>
在這個範例中,shadowRoot
中的內容是完全獨立的,外部的樣式或 JavaScript 都不會影響它,這使得組件更加健壯和可重用。
進階的 DOM 操作與技術能夠提升應用的可維護性、性能及互動性,無論是透過事件委派、虛擬 DOM 技術來優化效能,還是使用 Shadow DOM 或 Intersection Observer 來改善用戶體驗,這些技術都是現代前端開發中的重要工具。