You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:35:23