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

2023年末:修改textarea/input内容且保留撤销重做功能的方案

这个问题已被多次提出,但所有现有答案都使用了现已过时(即不再有效)或已废弃(即可能仍可用但随时可能失效)的API。

截至2023年末,如何在不丢失撤销/重做功能的前提下修改textarea/input/可编辑元素的文本?(无需自行实现繁琐的撤销/重做逻辑)

以下是已被提出的3种不同方案,外加一个自定义撤销缓冲区的尝试方案:

  • _simple() - 直接替换textarea值 - 会清空撤销缓冲区
  • _execCommand() - 运行正常,但execCommand已被废弃
  • _textEvent() - 完全无效,因“不可信”事件会被浏览器忽略
  • _simpleCustom() - 与_simple()逻辑相同,但引入自定义撤销缓冲区,实现不够完善,仅在单一操作系统/浏览器环境下测试过

代码示例

JavaScript 代码

function _simple(el = textarea)
{
  const data = getData(el);
  el.value = data.textStart + data.textInsert + data.textEnd;
  el.selectionStart = data.start + data.textInsert.length;
  el.selectionEnd = el.selectionStart;
}

function _execCommand()
{
  const data = getData(textarea);
  document.execCommand("insertText", null, data.textInsert);
}

function _textEvent()
{
  const data = getData(textarea);
  const event = document.createEvent('TextEvent');
  event.initTextEvent('textInput', true, true, null, data.textInsert);
  textarea.dispatchEvent(event);
}

function getData(el)
{
  el.focus();
  return {
    start: el.selectionStart,
    end: el.selectionEnd,
    textStart: el.value.slice(0, el.selectionStart),
    textEnd: el.value.slice(el.selectionEnd),
    textInsert: "" + ~~(Math.random() * 10)
  }
}


const _simpleCustom = (el =>
{
  const getState = () => ({
    value: el.value,
    start: el.selectionStart,
    end: el.selectionEnd
  });
  const undoBuffer = [getState()];
  const undoAdd = () =>
  {
    undoBuffer[++undoIndex] = getState();
    undoBuffer.length = undoIndex + 1; //确保当前状态是数组最后一项
  };
  const updateValue = data =>
  {
    el.value = data.value;
    el.selectionStart = data.start;
    el.selectionEnd = data.end;
  }
  let undoIndex = 0;

  el.addEventListener("input", undoAdd);
  el.addEventListener("keydown", evt =>
  {
    if (!evt.altKey && !evt.shiftKey && evt.ctrlKey && (evt.code === "KeyZ" || evt.code === "KeyY"))
    {
      evt.preventDefault();
      undoIndex += evt.code === "KeyZ" ? -1 : 1

      if (undoIndex >= undoBuffer.length)
        undoIndex = undoBuffer.length - 1;

      if (undoIndex < 0)
        undoIndex = 0;

      const data = undoBuffer[undoIndex];
      if (data)
        updateValue(data);

    }
  });
  return () => 
  {
    _simple(el);
    undoAdd();
  }
})(textarea2);

HTML 代码

<ol>
  <li>输入一些文本</li>
  <li>点击按钮</li>
  <li>按下CTRL+Z(撤销)</li>
</ol>
<textarea id="textarea"></textarea>
<br>
<button onclick="_simple()">_simple()</button>
<button onclick="_execCommand()">_execCommand()</button>
<button onclick="_textEvent()">_textEvent()</button>
<br>
<br>
<textarea id="textarea2"></textarea>
<br>
<button onclick="_simpleCustom()">_simpleCustom()</button>

内容的提问来源于stack exchange,提问作者vanowm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:06:03