概述
撤销回退这个功能,其实非常的常见,前端里面可能比较常见的地方就是低代码设计里面,今天看了一个开源的低代码平台的撤销回退,这里用简洁的思路和代码实现一遍这个功能。
原理
本质上,撤销回退的实现关键就在于栈,可以定义两个栈,一个入栈,一个出栈,撤销push操作后的数据到入栈队列,回退就pop入栈到出栈里面,当有新的操作Push到入栈的时候就清空出栈,当入栈和出栈的数据为空的时候,直接禁用,无法操作撤销回退。
效果

实现
这里代码实现的过程直接采用一个栈实现,通过一个索引控制当前的步骤的位置关系,本质上还是入栈出栈的思路,对于后面如果有类似的项目需求,大体思路其实都一致,根据各种需求进一步定制化改进。
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button class="redo">撤销</button>
<button class="undo">回退</button>
<div>
<input type="text" class="text-input" />
<button class="pushData">插入数据</button>
</div>
<div class="wrap-content"></div>
<script>
let data = [];
let redoBtn = document.querySelector('.redo');
let undoBtn = document.querySelector('.undo');
let pushData = document.querySelector('.pushData');
let textInput = document.querySelector('.text-input');
let res = {
stepIndex: -1, //当前步骤索引
stepData: [] //维护操作数组
};
// 渲染
function createDesigner() {
let ul = document.createElement('ul');
let wrapContent = document.querySelector('.wrap-content');
wrapContent.innerHTML = '';
for (let i = 0; i < data.length; i++) {
let li = document.createElement('li');
li.innerHTML = data[i];
ul.appendChild(li);
}
wrapContent.appendChild(ul);
}
// 撤销
redoBtn.addEventListener('click', () => {
console.log('res--', res);
if (res.stepIndex >= 0) {
res.stepIndex--;
data = res.stepIndex >= 0 ? res.stepData[res.stepIndex] : [];
createDesigner();
} else {
console.log('没有数据了');
}
});
// 回退
undoBtn.addEventListener('click', () => {
console.log('res--', res);
if (res.stepIndex < res.stepData.length - 1) {
res.stepIndex++;
data = res.stepData[res.stepIndex];
createDesigner();
} else {
console.log('没有数据了');
}
});
// 插入数据
pushData.addEventListener('click', () => {
const valueData = textInput.value;
// 引用数据一定要备份,避免数据之前互相干扰
const backups = JSON.parse(JSON.stringify(data));
if (valueData) {
backups.push(valueData);
res.stepData.length = res.stepIndex + 1;
res.stepData.push(JSON.parse(JSON.stringify(backups)));
res.stepIndex++;
data = res.stepData[res.stepIndex];
}
createDesigner();
console.log('res--', res);
});
</script>
</body>
</html>