Coding, Insights, and Digital Discoveries πŸ‘©πŸ»β€πŸ’»

From Stay-at-Home Mom to a Developer After 50

Published on

Understanding Memory Usage in Web Applications

memory usage optimization

While building a To-Do List app, I encountered a common challenge: how to efficiently display and switch between active and completed tasks. This led me to explore the relationship between memory management and web performance.

At first, I had two possible approaches:

  • 1️⃣ Use a single <ul> and dynamically change its content when switching between active and completed tasks.
  • 2️⃣ Use two separate <ul> elements, one for active tasks and one for completed tasks, and toggle their visibility.

Each approach has its own pros and cons, but to make an informed decision, I had to revisit a crucial concept: How memory works in a web page and how it impacts performance.

Memory Management in Web Pages

DOM Memory

The Document Object Model (DOM) consumes memory in several ways:

  • Each element node occupies native browser memory (including attributes, styles, etc.)
  • JavaScript creates wrapper objects when accessing DOM elements
  • Additional overhead includes:
    • Computed styles and layouts
    • Event listener registrations
    • Associated resources (images, fonts)
  • The more elements in the DOM, the higher memory usage, leading to slower rendering and performance issues

JavaScript Memory

JavaScript's memory management affects performance through:

  • Variable storage (primitives, objects, DOM references)
  • Cached computations and query results
  • Event handler closures
  • Weak references (WeakMap/WeakSet) for temporary caching
  • Storing elements in variables instead of the DOM reduces rendering load and improves speed.
  • However, excessive caching can also lead to high memory usage if not managed well.

Implementation Approaches

1. Single List with Dynamic Content

In this approach, I keep only one list element (<ul>) and update its content dynamically when switching tabs.

function toggleList(tabName) {
    taskList.innerHTML = "";
    taskList.append(...(tabName === "active" ? activeItems : completedItems));
}

βœ… Pros:

  • Lower DOM memory usage
  • Cleaner initial page load

❌ Cons:

  • Frequent DOM updates can cause slight lag.
  • Potential performance impact during switches (Tasks need to be stored in JavaScript memory to reload them when switching tabs.)

2. Two Lists with Visibility Toggle

In this approach, I keep two <ul> elements (one for active tasks, one for completed tasks) and show/hide them based on the selected tab.

function toggleList(tabName) {
    activeList.style.display = tabName === "active" ? "block" : "none";
    completedList.style.display = tabName === "active" ? "none" : "block";
}

βœ… Pros:

  • Instant switching, no rebuild cost
  • Better performance for large lists because items stay in memory.

❌ Cons:

  • Higher memory usage
  • More elements slow down initial page load.

3. Optimized Hybrid Approach

A good compromise is to cache the task lists in JavaScript memory and update the DOM only when necessary:

class TaskManager {
  constructor() {
    this.lists = { active: [], completed: [] };
    this.container = document.querySelector('.task-list');
  }

  toggleList(tabName) {
    const fragment = document.createDocumentFragment();
    this.lists[tabName].forEach(task => {
      const li = document.createElement('li');
      li.textContent = task.text;
      fragment.appendChild(li);
    });
    
    this.container.replaceChildren(fragment);
  }
}

This reduces DOM memory usage while keeping quick tab switching.

Virtual DOM and Modern Frameworks

Modern frameworks like React solve this problem using Virtual DOM:

  • Maintains lightweight JavaScript UI representation
  • Performs efficient diff calculations
  • Batches DOM updates
  • Minimizes expensive reflow/repaint cycles

Performance Metrics

Monitor these metrics using Chrome DevTools:

  • Time to First Paint (TTP)
  • Memory consumption patterns
  • CPU utilization during state changes
  • Layout reflow frequency

Recommendations by List Size

Small Lists (less than 100 items)

  • Use two lists with visibility toggling
  • Simple to maintain
  • Negligible memory impact

Medium Lists (100-1000 items)

  • Use hybrid approach with document fragments
  • Consider implementing virtual scrolling
  • Monitor memory usage

Large Lists (>1000 items)

  • Implement virtual scrolling
  • Use framework with Virtual DOM
  • Consider pagination or infinite scroll

These patterns ensure optimal performance while maintaining code maintainability. The key is choosing the right approach based on your specific requirements and scale.

My decision

Since this is a To-Do List app with a limited number of items in each list, the two <ul> approach (toggling visibility) is the better choice. A small number of items won’t significantly impact memory usage or page speed, and keeping both lists in the DOM allows for faster switching while keeping the code simpler and more maintainable.

← See All Posts