<!-- HTML -->
<input id="title" placeholder="標題">
<iframe id="editor" name="Editor" src="editor.html"></iframe>
<div id="autosave_status">60秒</div>
<a id="autosave_ctrl" href="javascript:;" onclick="startTimer()">開始計時</a>
<div id="draft_box"></div>
<script>
let timerId, countdown = 60;
function countdownTick(sec) {
clearTimeout(timerId);
document.getElementById('autosave_status').innerText = sec + '秒';
if (sec > 0) {
timerId = setTimeout(() => countdownTick(sec - 1), 1000);
} else {
const title = document.getElementById('title').value.trim();
if (title) autoSavePost();
else document.getElementById('autosave_status').innerText = '無需儲存';
}
}
function autoSavePost() {
const title = document.getElementById('title').value;
const content = window.frames['Editor'].document.body.innerHTML; // 視編輯器而定
const postTime = document.getElementById('postTime').value;
const data = new URLSearchParams({title, content, postTime, act: 'autosave'});
fetch('ajax.php', {method: 'POST', body: data})
.then(r => r.text())
.then(txt => {
const box = document.getElementById('autosave_status');
if (txt.includes('autosave_ok')) {
box.innerText = '儲存成功,可載入草稿';
loadDraft();
} else if (txt.includes('autosave_error')) {
box.innerText = '無內容,無需儲存';
}
});
}
function loadDraft() {
fetch('ajax.php?act=loaddraft')
.then(r => r.text())
.then(html => document.getElementById('draft_box').innerHTML = html);
}
function clearDraft() {
fetch('ajax.php?act=cleardraft', {method: 'POST'})
.then(r => r.text())
.then(html => document.getElementById('draft_box').innerHTML = html);
}
function startTimer() { countdownTick(60); }
function stopTimer() { clearTimeout(timerId); }
</script>
<?php
session_start();
$act = $_GET['act'] ?? $_POST['act'] ?? '';
switch ($act) {
case 'autosave':
$title = trim($_POST['title'] ?? '');
$content = $_POST['content'] ?? '';
if ($title === '' && $content === '') {
exit('<autosave_error>無內容</autosave_error>');
}
// 存入資料庫/Session/檔案
$_SESSION['draft'] = [
'title' => $title,
'content' => $content,
'time' => $_POST['postTime'] ?? time(),
];
exit('<autosave_ok>儲存成功</autosave_ok>');
case 'loaddraft':
$d = $_SESSION['draft'] ?? null;
if ($d) {
echo "<button onclick=\"restoreDraft()\">載入草稿 ({$d['time']})</button>";
echo "<script>function restoreDraft(){document.getElementById('title').value='".addslashes($d['title'])."'; window.frames['Editor'].document.body.innerHTML='".addslashes($d['content'])."';}</script>";
} else {
echo '無草稿';
}
break;
case 'cleardraft':
unset($_SESSION['draft']);
echo '草稿已清除';
break;
}
| 點 | 說明 |
|---|---|
| 編輯器取值 | 範例用 ` |
| 防抖/節流 | 倒計時機制本身即為節流;亦可監聽 `input` 事件重置計時器。 |
| 跨域/CSRF | 同源無跨域問題;若分離部署需加 CORS 標頭與 CSRF Token。 |
| 草稿存儲 | Demo 用 Session,生產建議資料表 drafts(user_id, post_id, title, content, updated_at)。 |
| 併發衝突 | 多頁籤編輯同一文章時,以 `post_id` 區分草稿,載入時提示版本衝突。 |
自動儲存草稿 = 前端計時觸發 AJAX + 後端持久化 + 前端狀態反饋。核心在於「何時存」(防抖節流)、「存什麼」(標題+內容+元數據)、「怎麼還原」(載入按鈕+JS 恢復編輯器)。上方代碼為最小可行實現,可直接整合至 CMS、部落格、後台編輯器。