高效手動操作dom 注意點

清空div 使用

replaceChildren
div.replaceChildren()

  • 直接清空所有子節(jié)點,性能高效。
  • 不會影響 div 本身的樣式、類、ID 或其他屬性。
  • 保留占位(div 本身仍在 DOM 樹中)。
  • 比 innerHTML = '' 更安全,不會觸發(fā) HTML 解析。
    不推薦使用以下的
 div.textContent= '' 
 div.innerHTML = '' 
// 循環(huán)刪除子節(jié)點 效率低下
 while (div.firstChild) {
    div.removeChild(div.firstChild);
 }

DocumentFragment

使用document.querySelector() docuemnt.querySelectorAll()

替代 getElementById getElementsByTagName
querySelectorquerySelectorAll 比傳統(tǒng)的 getElementByIdgetElementsByTagName 更靈活。

const items = document.querySelectorAll('.item');
items.forEach(item => item.classList.add('active'));

緩存查詢的dom

重復查詢 DOM 元素會增加開銷,建議緩存結(jié)果。

// 低效
document.querySelector('.container').innerHTML = 'Content';
document.querySelector('.container').classList.add('show');

// 高效
const container = document.querySelector('.container');
container.innerHTML = 'Content';
container.classList.add('show');

使用closest 和matches

closest 用于查找最近的祖先元素,matches 用于檢查元素是否符合某個選擇器,簡化事件委托。

document.addEventListener('click', e => {
  if (e.target.matches('.btn')) {
    console.log('Button clicked');
  }
  const parent = e.target.closest('.container');
  if (parent) {
    console.log('Found parent container');
  }
});

使用模板字符串+ insertAdjacentHTML/innerHTML

  • 使用dataSet classList insertAdjacentHTML
    比直接操作 className 更安全、簡潔。

    element.classList.add('active');
    element.classList.remove('inactive');
    element.classList.toggle('visible');
    
    element.dataset.id = '123';
    console.log(element.dataset.id); // '123'
    

    innerHTML 更靈活,允許在指定位置插入 HTML。

    element.insertAdjacentHTML('beforeend', '<div>New content</div>');
    
  • repalceChidren
    快速替換所有子節(jié)點,性能優(yōu)于清空 innerHTML 再添加。

    const parent = document.querySelector('.container');
    const newChild = document.createElement('div');
    parent.replaceChildren(newChild);
    

異步操作DOM

對于大型 DOM 操作,使用 requestAnimationFramesetTimeout 分割任務,避免阻塞主線程。

function updateLargeList(items) {
  let index = 0;
  function processChunk() {
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100 && index < items.length; i++, index++) {
      const li = document.createElement('li');
      li.textContent = items[index];
      fragment.appendChild(li);
    }
    document.querySelector('ul').appendChild(fragment);
    if (index < items.length) {
      requestAnimationFrame(processChunk);
    }
  }
  requestAnimationFrame(processChunk);
}

事件委托

通過事件冒泡,將事件監(jiān)聽器綁定到父元素,而不是每個子元素,減少內(nèi)存占用。

document.querySelector('ul').addEventListener('click', e => {
  if (e.target.tagName === 'LI') {
    console.log(e.target.textContent);
  }
});

批量修改樣式

避免逐個修改樣式,而是通過修改 CSS 類或使用 style.cssText 一次性設置。

// 低效
element.style.color = 'red';
element.style.fontSize = '16px';

// 高效
element.classList.add('new-class');
// 或
element.style.cssText = 'color: red; font-size: 16px;';

避免不必要的 DOM 操作

  • 檢查元素是否存在
    在操作元素前,確保其存在,避免錯誤。

    const element = document.querySelector('.item');
    if (element) {
      element.textContent = 'Updated';
    }
    
  • 避免重復操作
    檢查是否需要重復執(zhí)行相同的操作。

    if (!element.classList.contains('active')) {
      element.classList.add('active');
    }
    

使用 Web Components 或框架

對于復雜的 DOM 操作,考慮使用 Web Components 或前端框架(如 React、Vue、Svelte)。它們通過封裝和虛擬 DOM 優(yōu)化了性能和代碼可維護性。

MutationObserve

/**
 * 觀察指定容器中是否出現(xiàn)包含特定類名的元素,并執(zhí)行回調(diào)函數(shù)
 * @param {HTMLElement} container - 要觀察的容器元素
 * @param {string} targetClass - 要檢測的類名
 * @param {Function} callback - 檢測到目標元素時執(zhí)行的回調(diào)函數(shù),接收找到的元素作為參數(shù)
 * @param {MutationObserverInit} [config] - 可選的 MutationObserver 配置,默認為 { childList: true, subtree: true }
 * @returns {MutationObserver} - 返回 MutationObserver 實例,以便后續(xù)可手動斷開觀察
 */
export function observeClassInContainer(container, targetClass, callback, config = { childList: true, subtree: true }) {
  // 驗證輸入
  if (!(container instanceof HTMLElement)) {
      throw new Error('container 必須是一個有效的 HTML 元素');
  }
  if (typeof targetClass !== 'string' || targetClass.trim() === '') {
      throw new Error('targetClass 必須是一個非空字符串');
  }
  if (typeof callback !== 'function') {
      throw new Error('callback 必須是一個函數(shù)');
  }

  // 創(chuàng)建 MutationObserver 回調(diào)
  const observerCallback = (mutationsList, observer) => {
      for (const mutation of mutationsList) {
          if (mutation.type === 'childList') {
              mutation.addedNodes.forEach(node => {
                  if (node.nodeType === Node.ELEMENT_NODE) {
                      // 檢查當前節(jié)點
                      if (node.classList.contains(targetClass)) {
                          callback(node);
                      }
                      // 檢查所有子代
                      const elements = node.querySelectorAll(`.${targetClass}`);
                      elements.forEach(element => callback(element));
                  }
              });
          }
      }
  };

  // 初始化并啟動 MutationObserver
  const observer = new MutationObserver(observerCallback);
  observer.observe(container, config);

  return observer;
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容