console
const editorEl = document.querySelector("#editor");
let html = "";
function initEditorContent(textContent) { }
editorEl.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
e.preventDefault();
return;
}
if (e.key === "Backspace") {
const sel = window.getSelection();
const range = sel.getRangeAt(0);
if (range.commonAncestorContainer.nodeName === "DIV") {
const startNode = range.startContainer;
const startOffset = range.startOffset;
let node = startNode;
while (node && node.nodeType === Node.ELEMENT_NODE) {
if (node.getAttribute("contenteditable") === "false") {
break;
}
node = node.childNodes[startOffset - 1];
}
if (node && node.nodeType === Node.ELEMENT_NODE) {
e.preventDefault();
editorEl.removeChild(node);
computedInput();
}
}
}
});
editorEl.addEventListener("input", (e) => {
const { inputCount } = computedInput();
if (inputCount > 50 && e.target.innerHTML.length > html.length) {
e.preventDefault();
e.target.innerHTML = html;
setFocus(editorEl);
computedInput();
return false;
}
html = e.target.innerHTML;
});
editorEl.addEventListener("paste", (e) => {
e.preventDefault();
});
function computedInput() {
const textContent = editorEl.textContent;
const symbolReg = /\【(.*?)\】/g;
const symbolMatch = textContent.match(symbolReg) || [];
const symbolLength = symbolMatch.reduce(
(pre, item) => pre + item.length,
0
);
const textContentLength = textContent.length;
const charLength = textContentLength - symbolLength;
editorEl.setAttribute(
"data-input-count",
symbolMatch.length * 5 + charLength
);
return {
symbolMatch,
charLength,
inputCount: symbolMatch.length * 5 + charLength,
};
}
function insertSymbol(symbol) {
const sel = window.getSelection();
let range = sel.getRangeAt(0);
if (
range.commonAncestorContainer !== editorEl &&
!editorEl.contains(range.commonAncestorContainer)
) {
return;
}
range.deleteContents();
const el = document.createElement("div");
el.innerHTML = `<span class="symbol" contenteditable="false">${symbol}</span>`;
let frag = document.createDocumentFragment();
let node;
let lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
setFocus(editorEl);
const { inputCount } = computedInput();
if (inputCount > 50) {
editorEl.innerHTML = html;
setFocus(editorEl);
computedInput();
return;
}
html = editorEl.innerHTML;
}
function setFocus(el) {
el.focus();
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
if (sel.anchorOffset != 0) {
return;
}
sel.removeAllRanges();
sel.addRange(range);
}
<h3>短信模板编辑器</h3>
<ol>
<li>点击按钮可以在光标处插入变量</li>
<li>可以在编辑框内输入文本内容</li>
</ol>
<p>已知问题</p>
<ol>
<li>变量和输入的普通文本混杂时,删除变量可能会按两下,删除文本可能会连续删除</li>
</ol>
<div id="editor" data-input-count="0" contenteditable="true"></div>
<button onclick="insertSymbol('【姓名】')">【姓名】</button>
<button onclick="insertSymbol('【手机号码】')">【手机号码】</button>
<button onclick="insertSymbol('【年龄】')">【年龄】</button>
#editor {
position: relative;
width: 400px;
height: 200px;
border: 1px solid #000;
padding: 20px;
}
#editor::after {
position: absolute;
right: 5px;
bottom: 5px;
content: attr(data-input-count) "/50";
}
.symbol {
user-select: none;
color: orangered;
}