如何只访问 'input' 事件的更改数据,而不管文本删除或插入(如后者的粘贴、键入或恢复文本)?

How to access just the changed data of an 'input' event regardless of text deletion or insertion like pasted or typed or restored text for the latter?

我有一个文本区域,想要 return 对文本的最新更改以用于 avia JavaScript 功能。这将包括:

例如:

我对此还是个新手,完全不知道如何调用 added/removed 文本以在 JavaScript 函数中使用。

阅读关于 InputEvent, InputEvent.inputType and InputEvent.data might be a good starting point. In combination with selectionStart / selectionEnd as with e.g. HTMLInputElements and/or HTMLTextAreaElement 的文章几乎可以 cover/solve OP 的任务。

检测输入中(文本)变化的可能方法 data/value 可能...

  • 利用WeakMap实例作为文本区域and/orinput-element基于存储 此类元素的最新(文本)值。

  • 通过跟踪此类元素的 'input' 事件 启用对 data/value 更改的精确检测,基于最近和当前(文本)值以及 selectionEnd,有时基于输入事件的 data 值。

  • 如果已验证基于 input-event 的 text-value 更改 将创建并调度 custom event。因此现在可以直接监听和处理textarea and/or input-element相关的'input:datachange'事件.

在新的自定义 'input:datachange' 事件类型之上,'input''change' 事件都不会收到所有附加信息,OP现在将能够直接访问更改的值,无论是删除and/or插入(后者是粘贴、输入或恢复)。

// the node reference based storage of most recent element values.
const mostRecentValueStorage = new WeakMap;

function getDataChangeFromDeleteOrPaste({ currentTarget, data }) {
  const recentValue = mostRecentValueStorage.get(currentTarget);
  const { value: currentValue, selectionEnd } = currentTarget;

  let deletionStart = selectionEnd;
  let leadingValue = currentValue.slice(0, deletionStart);

  while ((leadingValue !== '') && !recentValue.startsWith(leadingValue)) {
    leadingValue = leadingValue.slice(0, --deletionStart);
  }
  const deletionLength =
    (selectionEnd - deletionStart) + (recentValue.length - currentValue.length);

  const deleted = recentValue.slice(deletionStart, (deletionStart + deletionLength));

  const insertLength = currentValue.length + deleted.length - recentValue.length;
  const insertStart = selectionEnd - insertLength;

  const inserted = (insertStart < selectionEnd)
    && currentValue.slice(insertStart, selectionEnd)
    || null;

  return {
    currentValue,
    recentValue,
    deleted: (deleted === '') ? null : deleted,
    deletionStart: (deleted === '') ? null : deletionStart,
    deletionLength: (deleted === '') ? null : deletionLength,
    inserted,
    insertStart: (inserted === null) ? null : insertStart,
    insertLength: (inserted === null) ? null : insertLength,
  };
}
function getDataChangeFromInsertText({ currentTarget, data }) {
  const recentValue = mostRecentValueStorage.get(currentTarget);
  const { value: currentValue, selectionEnd } = currentTarget;

  const insertLength = data.length;

  const deletionStart = (selectionEnd - insertLength);
  const deletionLength = (recentValue.length - currentValue.length + insertLength);

  const deleted = recentValue.slice(deletionStart, (deletionStart + deletionLength));

  return {
    currentValue,
    recentValue,
    deleted: (deleted === '') ? null : deleted,
    deletionStart: (deleted === '') ? null : deletionStart,
    deletionLength: (deleted === '') ? null : deletionLength,
    inserted: data,
    insertStart: (selectionEnd - insertLength),
    insertLength,
  };
}

function handleCustomInputDataChange(evt) {
  const { currentTarget } = evt;

  if (currentTarget.value !== mostRecentValueStorage.get(currentTarget)) {
    const { data } = evt;

    const dataChange = (
      (typeof data === 'string') && getDataChangeFromInsertText(evt)
    ) || (
      (data === null) && getDataChangeFromDeleteOrPaste(evt)
    ) || null;

    // put the most recent element value into an object based storage.
    mostRecentValueStorage.set(currentTarget, currentTarget.value);

    currentTarget
      .dispatchEvent(
        new CustomEvent('input:datachange', {
          bubbles: true,
          detail: {
            dataChange,
            inputEvent: evt,
          },
        })        
      );
  }
}

function enableCustomInputDataChangeHandling(elmNode) {
  // put the initial element value into an object based storage.
  mostRecentValueStorage.set(elmNode, elmNode.defaultValue);

  elmNode
    .addEventListener('input', handleCustomInputDataChange);
}
function initializeCustomInputDataChangeHandling() {
  document
    // for every element which features a
    // `data-handle-input-data-change` attribute ...
    .querySelectorAll('[data-handle-input-data-change]')

    // ... enable the handling of a custom
    // 'input:datachange' event.
    .forEach(enableCustomInputDataChangeHandling);
}

function main() {
  initializeCustomInputDataChangeHandling();

  document
    // subscribe to the custom 'input:datachange' event wherever it is needed. 
    .addEventListener(
      'input:datachange',
      ({ target, detail: { dataChange } }) => console.log({ /*target, */dataChange })
    );
}
main();
body { margin: 0; }
.as-console-wrapper { left: auto!important; width: 76%; min-height: 100%!important; }
<textarea
  data-handle-input-data-change
  cols="16"
  rows="12"
>The quick brown fox jumps over the lazy dog ... edit text in whichever way.</textarea>