contenteditable DIV输入事件卡顿 寻求JavaScript代码优化方案
优化contenteditable输入性能,解决快速输入卡顿问题
原代码在快速输入时出现UI卡顿,核心原因是频繁的DOM操作、冗余计算和全量重绘,以下是针对性的优化方案:
核心性能瓶颈分析
- 循环内反复执行
mainDiv.innerHTML = ""和innerHTML += spans.outerHTML,每次都会触发浏览器重排重绘,这是性能消耗的重灾区 placeCaretAtEnd函数在循环内部重复定义,每次输入都创建新函数实例,浪费内存- 对所有文本进行全量对比和DOM重建,哪怕用户只输入了一个字符,也重新处理全部内容
- fetch回调中每次循环都执行
split和设置textContent,属于重复冗余操作
优化方案与代码实现
优化后的完整代码
const mainDiv = document.querySelector("#mainDiv"); const placeHolderDiv = document.querySelector("#placeholder"); let placeHolder = ""; let placeHolderArr; // 提取光标定位函数到外部,避免重复定义 function placeCaretAtEnd(el) { el.focus(); if (typeof window.getSelection !== "undefined" && typeof document.createRange !== "undefined") { const range = document.createRange(); range.selectNodeContents(el); range.collapse(false); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (typeof document.body.createTextRange !== "undefined") { const textRange = document.body.createTextRange(); textRange.moveToElementText(el); textRange.collapse(false); textRange.select(); } } fetch("https://type.fit/api/quotes") .then((data) => data.json()) .then((result) => { // 先累加所有文本,再统一处理,减少重复操作 for (let quote = 0; quote < 10; quote++) { placeHolder += result[quote].text + " "; } placeHolderArr = placeHolder.trim().split(" "); placeHolderDiv.textContent = placeHolder.trim(); }); mainDiv.addEventListener("input", (e) => { // 避免未加载完placeholder时执行逻辑 if (!placeHolderArr) return; const mainText = mainDiv.textContent.trim(); const mainDivArr = mainText ? mainText.split(" ") : []; const fragment = document.createDocumentFragment(); // 只处理已输入的单词,增量对比 for (let num = 0; num < mainDivArr.length; num++) { const span = document.createElement("span"); const currentWord = mainDivArr[num].trim(); // 对比当前单词,设置样式 if (placeHolderArr[num] && placeHolderArr[num].trim() === currentWord) { span.textContent = currentWord + " "; } else { span.style.color = "red"; span.textContent = currentWord + " "; } fragment.appendChild(span); } // 一次性替换DOM,减少重排 mainDiv.innerHTML = ""; mainDiv.appendChild(fragment); // 最后统一设置光标位置 placeCaretAtEnd(mainDiv); });
body { height: 90vh; display: flex; justify-content: center; align-items: center; font-family: sans-serif; font-size: 1.5rem; } #content { position: relative; color: grey; width: 70vw; } #mainDiv { position: absolute; top: 0; left: 0; color: #000; width: 100%; border: none; outline: none; }
<div id="content"> <div id="mainDiv" autofocus contenteditable></div> <div id="placeholder"></div> </div>
关键优化点说明
- 批量DOM操作:使用
DocumentFragment一次性插入所有span元素,将多次DOM操作合并为一次,大幅减少浏览器重排重绘次数 - 增量处理:只对比用户已输入的单词,不再全量处理所有文本,减少不必要的循环计算
- 函数复用:把光标定位函数提取到外部,避免每次输入都重新定义函数
- 冗余操作消除:fetch回调中先完成所有文本累加,再统一执行split和设置placeholder,减少重复计算
- 简化逻辑:移除原代码中嵌套的循环和不必要的条件判断,让逻辑更简洁高效
内容的提问来源于stack exchange,提问作者simar
相关产品推荐
相关产品推荐

