CodeGym /Java Course /Frontend SELF EN /Event Optimization

Event Optimization

Frontend SELF EN
Level 42 , Lesson 3
Available

9.1 Event Delegation

Dealing with events in JavaScript can require complex management to optimize performance and improve app architecture.

Optimizing event handling in JavaScript is critical for creating high-performance web applications, especially when dealing with a large number of elements. In this context, event delegation and avoiding unnecessary handlers are key players.

Event Delegation is a pattern where an event handler is set on a parent element instead of each individual child element. This allows efficient event management for a large number of child elements, since the handler is called only once for the parent, not for each item.

Example without event delegation:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>Event Delegation Example</title>
        </head>
        <body>
          <ul id="list">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 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 = "New item, event won't fire on it";
                list.appendChild(li);
                alert(`You clicked on: ${event.target.textContent}`);
              });
            });
          </script>
        </body>
      </html>
    
  

Problem: if the list of items grows dynamically, you'll have to add handlers for each new item.

Solution — Example with event delegation:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>Event Delegation Example</title>
        </head>
        <body>
          <ul id="list">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 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 = "New item, event will fire on it";
                list.appendChild(li);
                alert(`You clicked on: ${event.target.textContent}`);
              }
            });
          </script>
        </body>
      </html>
    
  

Benefits:

  • Handler is added only to the parent element
  • Dynamically added elements automatically support the event handler

9.2 Preventing Unnecessary Handlers

To avoid performance degradation, it's important to minimize the number of event handlers, especially if they're attached to many elements or fired frequently. Here are some optimization strategies:

1. Reducing Handlers Count: use event delegation to cut down on the number of handlers.

2. Using once in addEventListener: if an event handler should run only once, use the option { once: true } when adding the handler.

Example:

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>One-time Event Example</title>
        </head>
        <body>
          <button id="myButton">Click me!</button>
          <script>
            const button = document.getElementById('myButton');
            button.addEventListener('click', function(event) {
              alert('You clicked the button');
            }, { once: true });
          </script>
        </body>
      </html>
    
  

3. Debouncing and Throttling: these techniques are useful for optimizing event handlers that fire frequently, like scroll or resize.

9.3 Debouncing

Debouncing combines multiple sequential function calls into one, executed only after the event stream has ended.

Example:

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));
    
  

Try changing the widget width to see the result

Example of debouncing

In this example, the search function will only run once the user has stopped typing for 300 milliseconds.

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>Debouncing Example</title>
        </head>
        <body>
          <div style="min-height: 55px">
            <input type="text" id="searchInput" placeholder="Start typing a query...">
            <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 = 'Searching for: ' + query;
              // Simulating server request
              setTimeout(() => {
                results.textContent = 'You searched for: ' + query;
              }, 500);
            }

            const debouncedSearch = debounce(performSearch, 300);

            searchInput.addEventListener('input', debouncedSearch);
          </script>
        </body>
      </html>
    
  
HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>Debouncing Example</title>
        </head>
        <body>
          <input type="text" id="searchInput" placeholder="Start typing a query...">
          <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;
              // Simulating server request
              setTimeout(() => {
                results.textContent = 'You searched for: ' + query;
              }, 500);
            }

            const debouncedSearch = debounce(performSearch, 300);

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

9.4 Throttling

Trolling Throttling ensures that a function is called no more than once every specified interval of time.

Example:

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));
    
  

Another Example of Throttling

In this example, the scroll handler is called no more than every 200 milliseconds, which helps prevent overloading the browser during frequent scroll events.

HTML
    
      <!DOCTYPE html>
      <html>
        <head>
          <title>Throttling Example</title>
        </head>
        <body>
          <div id="scrollContainer" style="height: 200px; overflow-y: scroll;">
            <div style="height: 1000px;"></div>
          </div>
          <div id="status">Start scrolling to see the effect</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 = 'Scrolling... ' + new Date().toLocaleTimeString();
            }

            const throttledScroll = throttle(handleScroll, 200);

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

9.5 Event Optimization with Passive Listeners

Passive event listeners are used to improve performance, especially when handling scrolling events (scroll). When an event handler is set as passive, it means it won't call preventDefault(), which allows the browser to optimize performance.

Example:

JavaScript
    
      window.addEventListener('scroll', function(event) {
        console.log('Scrolling');
      }, { passive: true });
    
  
1
Task
Frontend SELF EN, level 42, lesson 3
Locked
Debouncing Input
Debouncing Input
1
Task
Frontend SELF EN, level 42, lesson 3
Locked
Scroll Throttling
Scroll Throttling
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION