Skip to Content
Documentation17. Save Lifecycle Deep Dive

Save Lifecycle Deep Dive

Understanding Hyperclay’s save lifecycle is crucial for building robust applications. This guide explores how saves work, when they trigger, and how to control the process.

The Save Lifecycle

  1. DOM Change Detected → Debounce Timer
  2. beforeSave Callbacks → Run cleanup functions
  3. onbeforesave Attributes → Execute element-specific cleanup
  4. Strip Admin Elements → Remove edit-mode-only content
  5. Serialize DOM → Convert to HTML string
  6. POST to Server → Send to Hyperclay
  7. Save Complete → Callback executed

When Saves Trigger

Automatic Triggers

  • DOM mutations (structure changes)
  • Attribute changes
  • Text content changes
  • Form value changes (with persist)

Manual Triggers

  • Keyboard: Cmd/Ctrl+S
  • Elements with trigger-save
  • Calling hyperclay.savePage()

What Doesn’t Trigger Saves

  • Elements with mutations-ignore
  • Elements with save-ignore
  • Transient UI states (hover, focus)
  • CSS-only changes

The onbeforesave Attribute

Execute JavaScript before the page is saved to clean up or transform the DOM.

Basic Examples

<!-- Remove temporary UI --> <div class="notification" onbeforesave="this.remove()"> Temporary message </div> <!-- Reset form values --> <form onbeforesave="this.reset()"> <input type="text" value="temp"> </form> <!-- Clean attributes --> <div class="modal" open onbeforesave="this.removeAttribute('open')"> Modal content </div>

Common Patterns

<!-- Conditional cleanup --> <div onbeforesave="if (this.dataset.status === 'draft') this.remove()"> Draft content </div> <!-- Sort before saving --> <ul onbeforesave="[...this.children].sort((a,b) => a.textContent.localeCompare(b.textContent)).forEach(el => this.appendChild(el))"> <li>Item C</li> <li>Item A</li> <li>Item B</li> </ul> <!-- Save scroll position --> <div class="scrollable" onbeforesave="this.dataset.scrollTop = this.scrollTop"> Long content... </div>

Global beforeSave Hook

For application-wide transformations:

hyperclay.beforeSave = function(doc) { // Remove all temporary elements doc.querySelectorAll('[data-temp]').forEach(el => el.remove()); // Clean empty containers doc.querySelectorAll('.container:empty').forEach(el => el.remove()); // Add save timestamp doc.body.dataset.lastSaved = new Date().toISOString(); };

Common Save Patterns

Progressive Enhancement

<!-- Base content (what gets saved) --> <article class="post"> <h2>Title</h2> <p>Content...</p> </article> <script> // Add edit UI only in edit mode if (hyperclay.isEditMode()) { document.querySelectorAll('.post').forEach(post => { const toolbar = document.createElement('div'); toolbar.innerHTML = '<button>Edit</button>'; toolbar.setAttribute('onbeforesave', 'this.remove()'); post.prepend(toolbar); }); } </script>

Form State Management

<form onbeforesave="this.querySelectorAll('input[type=password]').forEach(i => i.value = ''); this.classList.remove('error', 'success')"> <input type="text" name="username" persist> <input type="password" name="password"> <div class="error" onbeforesave="this.remove()"></div> </form>
<div class="modal" onbeforesave="this.classList.remove('open'); this.style.display = ''"> Modal content </div>

Dynamic Content Cleanup

<!-- Clear search results --> <div class="search-results" onbeforesave="this.innerHTML = ''"> <!-- Populated via JS --> </div> <!-- Reset preview --> <div class="preview" onbeforesave="this.textContent = 'Preview area'"> <!-- Dynamic preview --> </div>

Advanced Techniques

Version Tracking

<div data-version="1" onbeforesave="if (this.dataset.changed) { this.dataset.version++; delete this.dataset.changed; }"> <p oninput="this.parentElement.dataset.changed = true"> Versioned content </p> </div>

Conditional Saving

hyperclay.beforeSave = function(doc) { const isDraft = doc.body.dataset.status === 'draft'; // Remove elements based on status doc.querySelectorAll(isDraft ? '[data-published-only]' : '[data-draft-only]') .forEach(el => el.remove()); };

Batch Operations

<div onbeforesave="this.querySelectorAll('[data-changed]').forEach(el => delete el.dataset.changed)"> <!-- Multiple items with data-changed tracking --> </div>

Performance Tips

Debounce Expensive Saves

let saveTimeout; function debouncedSave() { clearTimeout(saveTimeout); saveTimeout = setTimeout(() => hyperclay.savePage(), 2000); } // Use for sliders, color pickers, etc. slider.addEventListener('input', debouncedSave);

Selective Cleanup

hyperclay.beforeSave = function(doc) { // Only clean specific zones doc.querySelectorAll('[data-cleanup]').forEach(zone => { // Targeted cleanup based on zone type }); };

Debugging Saves

// Monitor what gets saved const originalBeforeSave = hyperclay.beforeSave; hyperclay.beforeSave = function(doc) { console.log('Pre-save:', doc.body.innerHTML.length); if (originalBeforeSave) originalBeforeSave(doc); console.log('Post-save:', doc.body.innerHTML.length); };

Best Practices

  1. Clean Up Temporary UI - Remove admin controls and error messages
  2. Preserve User Data - Keep content, remove only UI chrome
  3. Use onbeforesave Locally - Put cleanup logic near affected elements
  4. Test Both States - Verify edit and view mode appearance
  5. Handle Edge Cases - Consider empty states and errors
  6. Document Complex Logic - Comment non-obvious transformations

Master the save lifecycle to build apps that seamlessly blend editing and viewing!

Last updated on