<div class="container">
<h3>Contenteditable Element</h3>
<div contenteditable="true" id="contenteditable">This is the editable content.</div>
<div class="actions">
<button class="btn btn--undo" id="undo">Undo Change</button>
<button class="btn btn--save" id="save">Save Content</button>
</div>
</div>
@import url("https://fonts.googleapis.com/css2?family=Exo:wght@600&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
inline-size: 100%;
min-block-size: 100%;
font-family: "Exo", system-ui, sans-serif;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
gap: 20px;
padding: 40px;
}
.container {
background-color: #fff;
border: 1px solid #fff;
border-radius: 16px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 10px 50px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
min-width: 300px;
}
[contenteditable] {
position: relative;
box-sizing: border-box;
color: deeppink;
border-radius: 5px;
padding: 10px 16px;
min-height: 180px;
border: 1px solid deeppink;
box-shadow: rgba(0, 0, 0, 0.1) 0px 10px 15px -3px,
rgba(0, 0, 0, 0.05) 0px 4px 6px -2px;
font-size: 1.5rem;
width: 100%;
font-family: "Exo", system-ui, sans-serif;
overflow: hidden;
resize: vertical;
}
[contenteditable="true"] {
cursor: text;
}
.actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.btn {
display: inline-flex;
justify-content: center;
align-items: center;
min-height: 48px;
min-width: 140px;
font-family: "Exo", system-ui, sans-serif;
background: #ee0979;
font-weight: bold;
color: white;
border: 0 none;
border-radius: 10rem;
cursor: pointer;
padding: 10px;
transition: all 0.2s ease;
}
.btn.btn--undo {
background-color: #03a9f4;
}
.btn.btn--redo {
background-color: #ff5722;
}
.btn:hover {
box-shadow: 0 0 0 2px white, 0 0 0 3px #ee0979;
}
.btn:focus {
outline: none 0;
box-shadow: 0 0 0 2px white, 0 0 0 3px #03a9f4;
}
.btn:focus:not(:focus-visible) {
box-shadow: 0 0 0 2px white, 0 0 0 3px #ee0979;
}
[disabled] {
filter: grayscale(1);
}
[hidden] {
display: none;
}
const getById = (id_string) => {
return document.getElementById(id_string);
};
const insertAfter = (newEl, refEl) => {
refEl.parentNode.insertBefore(newEl, refEl.nextSibling);
};
const editElement = getById("contenteditable");
const undoBtn = getById("undo");
const saveBtn = getById("save");
console.log(editElement);
let originalContent = editElement.innerHTML;
let updatedContent = "";
console.log(originalContent);
// 如果用户刷新了页面,这些声明将确保一切回到初始状态
undoBtn.disabled = true;
saveBtn.disabled = true;
// 创建一个 redo 按钮
const redoBtn = document.createElement("button");
const redoLabel = document.createTextNode("Redo");
redoBtn.id = "redo";
redoBtn.className = "btn btn--redo";
redoBtn.hidden = true;
redoBtn.appendChild(redoLabel);
insertAfter(redoBtn, undo);
// 如果内容已被改变,启用 save 按钮
editElement.addEventListener("keypress", () => {
console.log(originalContent);
console.log(editElement.innerHTML);
if (editElement.innerHTML !== originalContent) {
saveBtn.disabled = false;
}
});
// 点击 save 按钮,将更新的内容保存到 updatedContent 变量中
saveBtn.addEventListener("click", () => {
updatedContent = getById("contenteditable").innerHTML;
if (updatedContent !== originalContent) {
undoBtn.disabled = false;
}
console.log(updatedContent);
});
// 如果点击 undo 按钮,将 contenteditable区域的innerHTML恢复到原来的内容
// 然后添加一个 redo 按钮,使编辑过的内容恢复原状
undoBtn.addEventListener("click", () => {
editElement.innerHTML = originalContent;
undoBtn.disabled = true;
redoBtn.hidden = false;
});
// 点击 redo 按钮,将 contenteditable区域的innerHTML回到撤销前状态
redoBtn.addEventListener("click", () => {
editElement.innerHTML = updatedContent;
redoBtn.hidden = true;
undoBtn.disabled = false;
undoBtn.focus();
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.