Interactive Components System
Project Overview
The Interactive Post Elements framework is a custom JavaScript system built for the QED website to make mathematical concepts more accessible through interactive visualizations, simulations, and games embedded directly within educational articles. The goal is to help readers understand complex topics through animations and hands-on interaction rather than relying solely on static text and images.
Each interactive element is a self-contained JavaScript class that can be instantiated multiple times on a page. The framework uses a plugin architecture where new element types can be added without modifying the core code.
Core Capabilities & Lifecycle Management
The framework manages the entire lifecycle of interactive elements, from initialization to cleanup.
Script Evaluation & Registration
The system parses JavaScript strings using extractAndEvaluateScripts, identifies class definitions, and maps them to a global window.interactiveElements registry. This allows dynamic loading while maintaining namespace isolation.
Lifecycle Stages
Each interactive element follows a specific lifecycle:
-
Initialization (
start): Elements are activated by placeholders (.interactive-placeholder) containingdata-script,data-params,data-instance-name, anddata-optionsattributes. Thestartmethod is async, allowing elements to load assets or fetch data before starting. It receives the container DOM element, initialization parameters, and optional saved state. -
Execution Loop: If a class defines a
main()method, the framework calls it repeatedly usingsetTimeoutwith 10ms intervals (100 FPS), providing smooth animations while remaining CPU-friendly. -
Pause/Resume: Elements can be paused automatically when scrolled out of view or manually via user controls. The framework tracks whether a pause is user-initiated or automatic.
-
Reset: Re-runs
start()with original parameters and fresh state. -
Stop: Deletes the instance from the
windowobject and removes DOM nodes, preventing memory leaks.
Instance Isolation
Each element instance stores its state in window[instanceName], preventing conflicts when multiple instances of the same type exist on a page.
Configuration System
Authors configure elements using data attributes and comma-separated options.
Available Options
| Option | Description |
|---|---|
autoPlay | Starts immediately without requiring user interaction |
autoPauseOnScroll | Pauses when element is outside viewport to save CPU/battery |
allowPause | Adds manual pause/resume controls |
allowFullscreen | Enables fullscreen mode with cross-browser support |
saveStates | Saves progress to localStorage across sessions |
noControls | Hides the control bar entirely |
noStop | Removes the stop button |
openControls | Shows controls panel by default |
h=X | Sets explicit container height (converted to rem) |
Performance Optimizations
Mathematical simulations, especially fractals, are computationally expensive. The framework uses several strategies to maintain performance:
Viewport-Aware Execution
The framework uses getBoundingClientRect and scroll listeners with 100px margins to detect element visibility. When autoPauseOnScroll is enabled, the execution loop stops when elements leave the viewport, preventing unnecessary CPU usage. This is particularly effective on pages with multiple interactive elements.
Memory Management
When an element stops, the framework explicitly deletes its instance from the global namespace and removes all DOM nodes. This prevents memory leaks even when users interact with many elements in a single session.
Computation Caching
Complex computations cache their results to avoid redundant calculations. For example, ComplexFractal stores iteration results in a 2D array (this.cache), allowing instant re-rendering during resize operations or color changes without recalculating Mandelbrot/Julia set iterations.
Grid Snapping
Draggable elements use grid-based positioning to minimize continuous coordinate updates. Collision detection uses Set lookups and breadth-first search for finding closest free positions, maintaining O(n) performance.
Design Philosophy & UI/UX
The interface is designed to stay out of the way and not distract from the mathematical content.
Theme Integration
The system uses a centralized color palette that automatically adapts to the website’s light/dark mode:
- Background:
gray-100/gray-700 - Main:
gray-300/gray-500 - Contrast:
blue-600/blue-200
SVG lines, grid patterns, and UI buttons use these colors to remain legible and consistent. Visual elements respond to theme changes via CSS custom properties and class-based styling.
Interactive Overlays
Elements show a play button overlay before activation, preventing scripts from running until the user clicks. This reduces initial page load and gives users control over when resources are used. A loading animation provides feedback during initialization.
Responsive Scaling
Every element handles window resize events. The framework recalculates canvas dimensions, SVG viewboxes, and grid layouts to maintain correct rendering on different screen sizes. For example, the Esther Klein simulation dynamically repositions points when the viewport changes.
Internationalization
Button labels are localized based on the page language (detected from #current-lang-icon). Currently supports English and Spanish for stop, reset, pause, resume, and clear data controls.
Accessibility & Keyboard Navigation
Interactive puzzles like crosswords, KenKen, and Numbrix support arrow key navigation for moving between cells without a mouse. Touch targets are at least 20px for mobile usability. Semantic HTML helps screen readers, and high contrast colors work in both light and dark themes.
Collapsible Controls
The control panel uses CSS transitions for smooth height changes. An arrow icon rotates 180° when toggling. Controls are generated dynamically based on enabled options, reducing clutter.
Fullscreen Support
The framework implements the fullscreen API with vendor prefixes for Firefox, Chrome, Safari, and IE/Edge. SVG icons switch between enter/exit states, and border radius adjusts to maintain visual consistency.
State Management
LocalStorage Persistence
When saveStates is enabled, elements can save user progress across sessions using this structure:
{
"article-url": {
"ElementClassName": {
"instanceName": savedStateData
}
}
}
The framework provides saveData(data) and clearData() methods, handling nested object updates and automatic cleanup when entries become empty.
State Recovery
On initialization, if saved state exists for the current URL, script name, and instance name, it’s passed to the element’s start() method. This allows puzzles like crosswords to resume from where users left off. Language-dependant elements like crosswords can have different instance names for each language, while elements that don’t depend on language like Esther Klein can share the same insteance name, allowing data transfer.
Extensibility
New interactive elements implement this interface:
class CustomElement {
async start(container, params, savedState) {
// Initialize DOM, parse parameters, restore state
// Set up event listeners and create structure
}
async main() {
// Called every ~10ms when running
// Update animations, physics, or game state
}
}
The framework automatically provides:
- Lifecycle management (pause/resume/reset/stop)
- Control generation
- Theme integration through color constants
- State persistence API
- Fullscreen capabilities
- Viewport visibility detection