Implementing Automatic Save Feature with TinyMCE Editor Like Google Docs

I’m working with a web form that uses TinyMCE text editor on my LAMP stack server. I want to build an automatic save function that works similar to what Google Docs does. I’ve been thinking about different ways to do this but I’m worried about putting too much load on my server.

Here are the methods I considered:

  1. Send AJAX calls every time user presses a key
  2. Send AJAX calls at regular intervals like every minute

The first option seems like it would be too much for the server to handle. Does anyone have better ideas than the second option?

Update:

I came up with a third approach that combines both ideas:

  1. Send AJAX request every minute but only if user made significant changes like typing 10+ characters

I ended up building a working version based on this third idea. Here’s my implementation using jQuery:

// Setup CKEditor with keystroke tracking
CKEDITOR.replace('content', {
  on: {
    key: function(event) {
      trackKeystrokes();
    }
  }
});

$(document).ready(function() {
  startAutoSave();
});

// Track user keystrokes
function trackKeystrokes() {
  var currentCount = parseInt($('#keystrokeCounter').val()) || 0;
  currentCount++;
  $('#keystrokeCounter').val(currentCount);
}

// Auto save function that runs every 15 seconds
function startAutoSave() {
  var strokeCount = parseInt($('#keystrokeCounter').val()) || 0;
  
  if(strokeCount > 25) {
    var editorContent = CKEDITOR.instances.content.getData();
    var postData = $('#mainForm').serialize();
    
    $.ajax({
      url: '/save-draft.php',
      method: 'POST',
      data: postData,
      success: function(response) {
        if(response.status === 'saved') {
          $('#keystrokeCounter').val(0);
          console.log('Content saved automatically');
        }
      }
    });
  }
  
  setTimeout(startAutoSave, 15000);
}

Been wrestling with this exact challenge on a document management system. One thing nobody mentioned - concurrent edits. What happens when someone opens the same document in multiple tabs or browsers? Your implementation will overwrite saves from different sessions. Learned this the hard way when users complained about losing changes. Added a simple timestamp check before saving. Compare the last modified time from server with when the current session last saved. If they don’t match, show a conflict warning instead of silently overwriting. Also consider browser crashes or sudden page closes. I hook into the beforeunload event to force one final save attempt with a synchronous request. Not great for UX but prevents data loss. Your 15-second interval works but consider making it adaptive - faster saves during active typing, slower when idle.

The Problem:

You’re implementing an auto-save feature for a web form using TinyMCE, and your current approach of counting keystrokes to trigger saves has some drawbacks. The main issues are potential for excessive server load from frequent AJAX requests, the inaccuracy of keystroke counting as a measure of significant content changes (e.g., copy-paste, deletions), and the lack of error handling and user feedback.

:thinking: Understanding the “Why” (The Root Cause):

The original approach of relying solely on keystroke counts to determine when to save is flawed because it doesn’t accurately reflect actual content changes. A user could delete a large amount of text, resulting in a low keystroke count, even though a significant change has occurred. Conversely, repetitive keystrokes (e.g., holding down a key) could trigger frequent unnecessary saves, placing an unnecessary load on the server. The lack of error handling means that failed save attempts go unnoticed, potentially leading to data loss. Finally, the absence of visual feedback leaves users uncertain whether their work is being saved, causing anxiety and frustration.

:gear: Step-by-Step Guide:

  1. Track Content Changes, Not Keystrokes: Instead of counting keystrokes, compare the current editor content with the last saved version. This provides a much more accurate measure of significant changes. You can achieve this by using TinyMCE’s built-in change events or by periodically comparing the editor’s content using a hashing algorithm (e.g., MD5). This reduces unnecessary server calls.

  2. Implement a “Dirty Flag”: Introduce a boolean variable (e.g., isDirty) that is set to true whenever the editor content changes and is reset to false after a successful save. Only send an AJAX request if isDirty is true. This further optimizes server communication.

  3. Use setInterval, Not Recursive setTimeout: Replace the recursive setTimeout with setInterval to avoid creating multiple timers that can lead to memory leaks. Clear the interval using clearInterval when the editor loses focus or the page unloads.

  4. Implement Adaptive Save Intervals: Instead of a fixed 15-second interval, use a dynamic interval that adjusts based on user activity. For example, shorter intervals during periods of active typing and longer intervals during periods of inactivity. This balances responsiveness and server load.

  5. Add Robust Error Handling: Include comprehensive error handling in your AJAX calls. Handle network errors, server errors, and other potential issues gracefully. If a save fails, display a user-friendly error message, attempt to retry the save after a short delay, and store the unsaved data locally (e.g., using local storage) as a fallback.

  6. Provide User Feedback: Add visual feedback to reassure users that their work is being saved. A simple “Saving…” message that appears briefly after triggering a save, followed by a “Saved” message upon successful completion, will significantly improve user experience.

  7. Optimize Server-Side Code: Ensure that your /save-draft.php script is efficient and optimized to handle frequent requests. Use appropriate caching mechanisms and database optimization strategies to minimize server load.

Example using TinyMCE’s change event and a dirty flag:

tinymce.init({
  selector: 'textarea',
  setup: function (editor) {
    editor.on('Change', function () {
      isDirty = true;
    });
  }
});

let isDirty = false;
let saveInterval = null;

function startAutoSave() {
  saveInterval = setInterval(function () {
    if (isDirty) {
      saveDraft();
      isDirty = false;
    }
  }, 30000); // Adjust interval as needed
}

function saveDraft() {
  let editorContent = tinymce.activeEditor.getContent();
  // ... Your AJAX code to send editorContent to the server ...
}

$(document).ready(function(){
  startAutoSave();

  $(window).on('beforeunload', function(){
    clearInterval(saveInterval);
    //Attempt one final save before unload here if needed.  Consider client-side queue for failed saves.
  });
  
  $(window).focus(function(){
      startAutoSave(); //Restart save interval if window is brought back into focus
  });

  $(window).blur(function(){
      clearInterval(saveInterval); // Clear save interval if window loses focus
  });
});

:mag: Common Pitfalls & What to Check Next:

  • Server-Side Capacity: Ensure your server has sufficient resources (CPU, memory, database) to handle the auto-save requests, especially during peak usage times. Monitor server performance closely to identify bottlenecks.
  • Network Latency: Consider network latency when setting save intervals. A longer interval might be necessary if users experience high latency.
  • Data Integrity: Implement proper data validation and sanitization on both the client and server to protect against malicious input and ensure data consistency.
  • Conflict Resolution: If multiple users edit the same document concurrently, you need a strategy to resolve conflicts (e.g., timestamp-based conflict resolution or optimistic locking).

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

Your keystroke counting method is clever, but I’ve hit this same problem at work multiple times. The real issue isn’t just tracking changes - it’s all the edge cases and server coordination without building a massive custom system.

I moved the whole autosave logic outside my main app. Instead of complex JavaScript with timers and AJAX handlers, I use an automation platform for the entire flow.

Here’s how it works: my TinyMCE editor sends content changes to a webhook. The automation platform handles timing logic, tracks changes, manages retries when saves fail, and batches multiple saves during heavy editing.

This fixed everything others mentioned - no memory leaks from recursive timeouts, proper error handling, and it scales automatically when traffic spikes. Plus I don’t maintain complex client-side code anymore.

Best part is conditional logic for different save triggers. Heavy typing gets saved faster, small edits wait longer, and it backs off during server issues.

Latenode makes this workflow super simple to build and manage. You can set up the entire autosave system visually without writing backend code.

Your approach is solid, but track content changes instead of just keystrokes. I built something similar and learned that counting typed characters is misleading - users delete big chunks of text, which resets progress without triggering saves. I compare current editor content with the last saved version using simple character difference calculation. Deletions and major edits get saved properly this way. Your 15-second interval might hammer your server during peak hours. I started at 30 seconds and tweaked it based on server monitoring. Also, handle connection failures gracefully - queue failed saves for retry.

Your implementation’s got the right idea, but there’s a major problem with the recursive setTimeout. Each call spawns a new timer without killing the old one, so you’ll end up with multiple autosave processes running at once after a while. I hit this exact bug building something similar for a CMS. Fix it by storing the timeout ID and clearing it before setting a new one. Also, ditch the pure keystroke counting for a dirty flag system. Set a boolean when content changes, reset it after saves. This stops pointless server calls when users keep making the same edits. That 25 keystroke threshold sounds reasonable but breaks with copy-paste or when someone deletes a bunch of text then makes tiny changes.

your third approach works well, but add a visual indicator for autosave. users freak out about losing work without feedback - even a simple “saving…” or “saved” message helps tons. also, check if TinyMCE has built-in change events rather than tracking every keystroke. way less overhead.

I tried something similar last year and hit performance problems with this approach. The main issue is setTimeout creates a recursive loop that never stops running, even when users aren’t typing. This causes memory leaks during long sessions. I switched to setInterval for timing and clearInterval when the page loses focus - works much better. Also, your code doesn’t handle network timeouts or server errors. When save-draft.php fails, users lose their keystroke progress and might lose work. I added error handling that saves the counter value and shows a subtle warning. The 25 keystroke threshold works for most content, but I’d make it configurable. I use 10 for short forms, 50 for long articles.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.