事件優化

Frontend SELF TW
等級 42 , 課堂 3
開放

9.1 事件委派

在JavaScript中處理事件可能需要複雜的管理來優化性能和改進應用程序架構。

在JavaScript中優化事件處理對於創建高性能的Web應用程序至關重要,特別是在需要處理大量元素時。在這個背景下,事件委派和防止不必要的處理器起著關鍵作用。

事件委派 是一種模式,其中事件處理器設置在父元素上,而不是每個單獨的子元素上。這允許對大量子元素的事件進行有效管理,因為處理器只為父元素調用一次,而不是每個元素都調用。

沒有事件委派的例子:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>事件委派例子</title>
        </head>
        <body>
          <ul id="list">
            <li>元素 1</li>
            <li>元素 2</li>
            <li>元素 3</li>
          </ul>
          <script>
            const list = document.querySelector('ul');
            const items = document.querySelectorAll('#list li');

            items.forEach(item => {
              item.addEventListener('click', function(event) {
                const li = document.createElement('li');
                li.innerText = "新的元素, 事件不會在上面觸發";
                list.appendChild(li);
                alert(`你點擊了: ${event.target.textContent}`);
              });
            });
          </script>
        </body>
      </html>
    
  

問題: 如果列表元素是動態增加的,則需要為每個新元素添加處理程序。

解決方案 —— 事件委派的例子:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>事件委派例子</title>
        </head>
        <body>
          <ul id="list">
            <li>元素 1</li>
            <li>元素 2</li>
            <li>元素 3</li>
          </ul>
          <script>
            const list = document.getElementById('list');
            list.addEventListener('click', function(event) {
              if (event.target.tagName === 'LI') {
                const li = document.createElement('li');
                li.innerText = "新的元素, 事件會在上面觸發";
                list.appendChild(li);
                alert(`你點擊了: ${event.target.textContent}`);
              }
            });
          </script>
        </body>
      </html>
    
  

優勢:

  • 處理程序僅添加到父元素上
  • 動態添加的元素將自動支持事件處理程序

9.2 防止不必要的處理程序

為了避免性能下降,重要的是要最小化事件處理程序的數量,特別是如果它們附加到多個元素或經常調用的話。幾種優化方法:

1. 減少處理程序的數量: 使用事件委派來減少處理程序的數量。

2. 在 addEventListener 中使用 once: 如果事件處理程序只需觸發一次,請在添加處理程序時使用選項 { once: true }

例子:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>單次事件例子</title>
        </head>
        <body>
          <button id="myButton">點擊我!</button>
          <script>
            const button = document.getElementById('myButton');
            button.addEventListener('click', function(event) {
              alert('你點擊了按鈕');
            }, { once: true });
          </script>
        </body>
      </html>
    
  

3. 防抖(debouncing) 和 節流(throttling): 這些技術對於優化頻繁調用的事件處理程序非常有用,如 scrollresize

9.3 防抖(debouncing)

防抖(debouncing) 將多個連續的函數調用合併為一個,並且只在事件流停止後執行。

例子:

JavaScript
    
      function debounce(func, wait) {
        let timeout;
        return function(...args) {
          const context = this;
          clearTimeout(timeout);
          timeout = setTimeout(() => func.apply(context, args), wait);
        };
      }

      window.addEventListener('resize', debounce(() => {
        console.log('Window resized');
      }, 300));
    
  

試著改變窗口寬度以查看結果

防抖例子

在這個例子中,搜索函數只有在用戶停止輸入文本300毫秒後才會被調用。

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>防抖例子</title>
        </head>
        <body>
          <div style="min-height: 55px">
            <input type="text" id="searchInput" placeholder="開始輸入搜索...">
            <div id="results"></div>
          </div>

          <script>
            const searchInput = document.getElementById('searchInput');
            const results = document.getElementById('results');

            function debounce(func, delay) {
              let timeout;
              return function(...args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                  func.apply(this, args);
                }, delay);
              };
            }

            function performSearch(event) {
              const query = event.target.value;
              results.textContent = '搜尋: ' + query;
              // 模擬服務器請求
              setTimeout(() => {
                results.textContent = '你搜尋了: ' + query;
              }, 500);
            }

            const debouncedSearch = debounce(performSearch, 300);

            searchInput.addEventListener('input', debouncedSearch);
          </script>
        </body>
      </html>
    
  
HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>防抖例子</title>
        </head>
        <body>
          <input type="text" id="searchInput" placeholder="開始輸入搜索...">
          <div id="results"></div>

          <script>
            const searchInput = document.getElementById('searchInput');
            const results = document.getElementById('results');

            function debounce(func, delay) {
              let timeout;
              return function(...args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                  func.apply(this, args);
                }, delay);
              };
            }

            function performSearch(event) {
              const query = event.target.value;
              results.textContent = 'Searching for: ' + query;
              // 模擬服務器請求
              setTimeout(() => {
                results.textContent = '你搜尋了: ' + query;
              }, 500);
            }

            const debouncedSearch = debounce(performSearch, 300);

            searchInput.addEventListener('input', debouncedSearch);
          </script>
        </body>
      </html>
    
  

9.4 節流(throttling)

拖車節流(throttling) 確保函數不會被頻繁地調用,限制在指定的時間間隔內最多調用一次。

例子:

JavaScript
    
      function throttle(func, limit) {
        let inThrottle;
        return function(...args) {
          const context = this;
          if (!inThrottle) {
            func.apply(context, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
          }
        };
      }

      window.addEventListener('scroll', throttle(() => {
        console.log('Window scrolled');
      }, 300));
    
  

另一個節流的例子

在這個例子中,滾動處理程序的調用頻率不超過每200毫秒一次,這有助於防止在頻繁滾動的事件中使瀏覽器過載。

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>節流例子</title>
        </head>
        <body>
          <div id="scrollContainer" style="height: 200px; overflow-y: scroll;">
            <div style="height: 1000px;"></div>
          </div>
          <div id="status">開始滾動以查看效果</div>

          <script>
            const scrollContainer = document.getElementById('scrollContainer');
            const status = document.getElementById('status');

            function throttle(func, limit) {
              let lastFunc;
              let lastRan;
              return function(...args) {
                const context = this;
                if (!lastRan) {
                  func.apply(context, args);
                  lastRan = Date.now();
                } else {
                  clearTimeout(lastFunc);
                  lastFunc = setTimeout(() => {
                    if ((Date.now() - lastRan) >= limit) {
                      func.apply(context, args);
                      lastRan = Date.now();
                    }
                  }, limit - (Date.now() - lastRan));
                }
              };
            }

            function handleScroll(event) {
              status.textContent = '滾動中... ' + new Date().toLocaleTimeString();
            }

            const throttledScroll = throttle(handleScroll, 200);

            scrollContainer.addEventListener('scroll', throttledScroll);
          </script>
        </body>
      </html>
    
  

9.5 使用被動監聽器優化事件

被動事件監聽器 (passive event listeners) 用於提升性能,特別是在處理滾動(scroll)事件時。當事件處理器設置為被動時,表示它不會調用preventDefault(),這允許瀏覽器優化性能。

例子:

JavaScript
    
      window.addEventListener('scroll', function(event) {
        console.log('Scrolling');
      }, { passive: true });
    
  
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION