A learning project exploring the challenges and limitations of treating the DOM as the single source of truth, with a planned evolution toward modern state management patterns.
This project is an intentional learning exercise to understand:
- DOM as State Pain Points - Why managing application state directly through DOM manipulation becomes problematic
- State Management Complexity - How to handle multiple modes (edit, delete), confirmations, and side effects
- Frontend Framework Requirements - Why frameworks like React exist and what problems they solve
- DOM Diffing Concepts - How minimal diffing algorithms work before adopting a full framework
- Architecture Evolution - The migration path from DOM-as-state to state-first to component-based approaches
Phase 1: DOM as State (Current)
↓
Phase 2: State-First with Minimal DOM Diffing
↓
Phase 3: React Implementation (Same Functionality)
- Add Items: Add single or multiple items (comma-separated input)
- Edit Items: Click to edit, with fallback to previous value on cancel
- Delete Items: Toggle delete mode, select items, confirm deletion
- Clear List: Clear all items with confirmation dialog
- Normal Mode: Default interaction mode
- Edit Mode: Inline editing with original text preservation
- Delete Mode: Multi-select mode with visual feedback
- Real-time feedback messages with color coding
- Input validation and sanitization (alphanumeric + spaces)
- Confirmation dialogs for destructive actions
- Visual indicators for active modes (color changes)
assets/
├── css/
│ └── edit-mode.css # Minimal CSS for edit mode visibility
├── js/
│ ├── stateVariables.js # Global state variables
│ ├── stateManager.js # State management functions
│ ├── domRefferences.js # Cached DOM element references
│ ├── functions.js # Core utility functions
│ ├── modes.js # Mode management (edit, delete)
│ └── eventListeners.js # Event delegation and handlers
└── index.html # Simple HTML structure
// Item being edited
editingItem // null | HTMLLIElement
originalText // null | string
// Mode flags
isInEditMode // boolean
isInDeleteMode // boolean
// Confirmation flags
confirmClear // boolean
confirmDelete // boolean- Some state in global variables (
editingItem,isInEditMode) - Some state in DOM attributes (
data-marked) - Some state in DOM structure (nested editRow inside li)
- Difficult to see the big picture at a glance
- Can't change UI without changing state logic
- State updates are scattered throughout event handlers
- No clear data flow or lifecycle
- Must manually keep DOM in sync with global variables
- Editing requires moving DOM elements (
editRow) between parents - Deleting requires updating classes, styles, and attributes
- No single source of truth
// These states must all be manually managed and kept in sync:
- isInEditMode, editingItem, originalText, editRow visibility
- isInDeleteMode, marked data attribute, color styling
- confirmClear vs confirmDelete flags with different behaviors- State is implicit (hidden in DOM/JS variables)
- Hard to trace state changes
- Debugging requires inspecting multiple locations
- Event handlers have side effects on global state
- Manual DOM manipulation for every state change
- No automatic diffing - must remember all side effects
- Style changes, attribute updates, and structural changes are separate
- Business logic scattered across event listeners
- No centralized place to enforce rules
- Easy to miss edge cases (e.g., edit while deleting)
Declares global state flags, but state is incomplete (doesn't include DOM state like root.querySelectorAll('li');
Caches DOM references to avoid repeated querySelector calls - good performance optimization with bad side effect: tight coupling
Simple setter/getter functions, but they don't actually "manage" state since state is global and modified directly everywhere
Contains business logic, but mixed with DOM manipulation. Hard to reuse without the DOM layer
Event handlers directly manipulate global state and DOM. Complex conditionals for mode management.
The next phase will introduce:
// Explicit state object
const appState = {
items: ['item1', 'item2'],
editingIndex: null,
editingOriginal: null,
isEditMode: false,
isDeleteMode: false,
markedForDeletion: new Set(),
confirmClear: false,
confirmDelete: false
};
// Pure functions (no side effects)
function addItem(state, itemName) {
// return new state
}
function updateItem(state, index, newText) {
// return new state
}
// Minimal DOM diffing
function render(prevState, nextState) {
// Only update DOM nodes that changed
// Similar to how frameworks like React work with Virtual DOM
}The final phase will convert to React showing:
- Components as reusable UI units
- Props for data flow
- Hooks for state management (useState, useCallback)
- JSX as declarative UI
- Automatic re-rendering based on state changes
- Cleaner event handling
- Open
index.htmlin a modern web browser - Start adding, editing, and deleting items to explore the functionality
- Examine the DevTools Console to trace state changes
- Review the code to understand the current DOM-as-State approach
- Adding new features (undo/redo, persistence) becomes exponentially harder
- Team members must understand the scattered state locations
- Refactoring is risky (easy to miss related state updates)
- Performance debugging requires understanding multiple systems
- React: One source of truth (state), predictable re-renders via virtual DOM
- Vue: Reactive data binding, computed properties for derived state
- Svelte: Compiler approach, minimal runtime overhead
Regardless of framework choice, modern apps use:
- Explicit State - Clear data structure
- Pure Functions - Logic without side effects
- Unidirectional Data Flow - State → Render → Events → State
- Efficient Updates - Only update what changed
Open DevTools and add breakpoints. Try to answer:
- Where does
editingItemget set? - How many places update the DOM?
- What happens if you edit while deleting?
Try adding a search/filter feature to find items by partial text match:
- You'll quickly see why adding state is painful
- You'll need to update multiple files
- You'll face edge cases with existing modes (what happens if you filter while editing?)
After implementing Phase 2 & 3:
- Note the reduction in event handler complexity
- Observe the clarity of state flow
- See how business logic separates from UI
By the end of this learning journey, you'll understand:
- ✅ Why DOM as State doesn't scale
- ✅ How state management separates concerns
- ✅ Why React's component model is powerful
- ✅ How virtual DOM diffing improves performance
- ✅ The fundamental patterns in modern frontend development
Status: Phase 1 - DOM as State ✅ | Phase 2 - Coming Soon ⏳ | Phase 3 - Coming Soon ⏳