iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
JavaScript

入門JavaScript系列 第 23

JavaScript與DOM (2)

  • 分享至 

  • xImage
  •  

JavaScript 與 DOM 的進階應用能夠讓網頁更加動態、互動,並提升網頁的性能和使用者體驗。進階的 DOM 操作通常包括事件委派、動態樣式管理、模板化渲染、虛擬 DOM 技術、性能優化等。以下是一些 JavaScript 與 DOM 進階應用的概念和範例。

1. 事件委派(Event Delegation)

事件委派是一種高效的事件處理技術。它利用事件冒泡機制,只在父元素上監聽事件,並根據事件的來源進行具體處理。這在處理大量動態生成的子元素時非常有用,能顯著提高性能,減少事件監聽器的數量。

例子:事件委派處理動態列表項

<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> 單獨設置監聽器。

2. 虛擬 DOM(Virtual DOM)

虛擬 DOM 是一個輕量級的 JavaScript 表示,反映了實際的 DOM 結構。它允許我們在 JavaScript 中先操作虛擬 DOM,然後將最小的改變同步到真實 DOM。這可以避免直接頻繁操作 DOM 導致的性能問題。React 就是基於虛擬 DOM 概念的前端框架。

虛擬 DOM 的基本思路

  • 建立虛擬 DOM 樹:JavaScript 生成虛擬 DOM 樹。
  • 進行 DOM 差異計算:比較新舊虛擬 DOM 樹,找到需要更新的部分。
  • 更新真實 DOM:僅對需要改變的部分進行 DOM 操作,從而提高性能。

範例:手動模擬簡單的虛擬 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 樹進行比較,僅更新需要變更的部分。

3. 性能優化:批量操作 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 個新元素一次性添加到頁面中,大幅提升性能。

4. 模板化渲染

在處理大量動態內容時,經常需要使用模板化渲染技術。這能夠讓我們以更清晰、易於維護的方式來生成 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 中。這種方式能夠保持程式碼結構簡潔、明確。

5. Intersection Observer API

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 屬性中,從而實現懶加載功能。

6. 自訂事件(Custom Events)

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 的自訂事件,並在按鈕上手動觸發它。這種自訂事件對於在不同模組或組件之間進行通信非常有用。

7. Shadow DOM

Shadow DOM 是 Web Components 中的一部分,它允許你將 DOM 的某些部分封裝起來,避免樣式和 JavaScript 變量的

衝突。這可以用來創建獨立、可重用的組件。

範例:簡單的 Shadow DOM 組件

<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 來改善用戶體驗,這些技術都是現代前端開發中的重要工具。


上一篇
JavaScript與DOM (1)
下一篇
JavaScript與CSS(1)
系列文
入門JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言