如何比较两个文本字段内容并突出显示 ReactJS 中更改的字符?

How to compare two textfield contents and highlight the characters that are changed in ReactJS?

我需要比较两个 material-ui 文本字段内容并突出显示两者中更改的字符。这基本上与此处提出的问题相同,但对于 ReactJS 而不是 C# Windows Forms:

How to compare two rich text box contents and highlight the characters that are changed?

如果您可以提供一些您的代码,那么您将更容易理解您的代码当前是如何设置的以及如何实现该功能。我尝试按照您提到的内容创建一些东西(沙盒:https://codesandbox.io/s/sam-cards-forked-jyrlv?file=/src/App.js:0-1786

解释:

首先,我们需要区分一种查找两个字符串之间差异的方法。这很简单,可以通过逐个字符地迭代每个字符串并将两者比较来完成。

const findDiff = (string1, string2) => {
  // we will use this to keep an eye on the two strings
  let index = 0
  
  while (index < string1.length || index < string2.length) {
    const left_char = string1[index]
    const right_char = string2[index]
    if (left_char !== right_char) {
        // they are different
    } else {
      // they are the same character
    }
    index++
  }
    return // return something here
}

findDiff(string1, string2);

现在,我们有将它们渲染到屏幕并突出显示它们的问题。一种方法(虽然丑陋但确实如此)是:

  1. 跟踪不同字符的索引并将它们设置为状态
  2. 将字符串转为数组
  3. 通过数组映射,如果在状态中找到当前索引(意味着它是一个“已更改”值),return 一个 css 修改的跨度以“突出显示”它

我将其设置为在单击按钮时显示,您可以在这里看到: https://codesandbox.io/s/sam-cards-forked-jyrlv?file=/src/App.js:0-1786

import "./styles.css";
import { useState, useEffect } from "react";

const string1 = "abcdefg";
const string2 = "adcelig123";
const strar1 = string1.split("");
const strar2 = string2.split("");

export const YourComponent = () => {
  const [isHighlightActive, setIsHighlightActive] = useState(false);
  const [wrongCharIndexes] = useState(new Set());

  // re-calculate the differences between strings whenever they change
  useEffect(() => {
    findDiff(string1, string2);
  }, [string1, string2]);

  const findDiff = (string1, string2) => {
    // we will use this to keep an eye on the two strings
    let index = 0;

    while (index < string1.length || index < string2.length) {
      const left_char = string1[index];
      const right_char = string2[index];
      if (left_char === right_char) {
        wrongCharIndexes.add(index);
      }
      index++;
    }
    return;
  };

  return (
    <div className="App">
      {isHighlightActive ? (
        // map through the two strings and render the highlighted character or regular character
        <>
          <p className="flex">
            {strar1.map((char, index) => {
              return wrongCharIndexes.has(index) ? (
                <span className="highlighted">{char}</span>
              ) : (
                <>{char}</>
              );
            })}
          </p>
          <p className="flex">
            {strar2.map((char, index) => {
              return wrongCharIndexes.has(index) ? (
                <span className="highlighted">{char}</span>
              ) : (
                <>{char}</>
              );
            })}
          </p>
        </>
      ) : (
        <div>
          <p>{string1}</p>
          <p>{string2}</p>
        </div>
      )}
      <button onClick={() => setIsHighlightActive((prev) => !prev)}>
        Click me
      </button>
    </div>
  );
};

非常感谢回复的人。我有趣地找到了解决方案。希望这可以帮助其他人解决这个问题。

概念是我有两个带有内容的可编辑文本框,我强调了两者之间的区别。首先,让我们解决突出显示问题。这很简单。我使用 Squiggs 的方法通过使用以下包比较两个字符串:

https://github.com/kpdecker/jsdiff

利用 Diff 包,我创建了一个比较两个字符串的函数,returns 结果:

function compareStrings(string1, string2) {
  let results = Diff.diffChars(string1, string2);

  let output = "";
  results.forEach((item) => {

    if (item.removed) {
      output += `<span style="background-color:yellow">${item.value}</span>`;
    } else if (!item.added) {
      output += `${item.value}`;
    }
  });

  return output;
}

当使用Diff包时,它将识别出的不同点和相似点分成不同的项目。它使研究结果的处理相对容易。在上面的代码中,迭代 results 中的结果。如果某项 removed 设置为 true,则它是 string1 中的一项,但不在 string2 中。如果 added 设置为 true,它是 string2 中不在 string1 中的项目。如果两者都没有设置为 true,则它是两个字符串中的一个项目。我的函数的输出将包含 string1 的所有字符。如果某个项目的 added 设置为 true,则该项目将被忽略。例如,如果 string1catstring2bat,则 output 将是 cat,但 c 以黄色突出显示.

那么如何将突出显示的文本插入 Material-Ui 文本框?好吧,我选择了使用 contentEditable 控件的方式。您可以将 p 标签(和其他标签)转换为呈现 HTML 的可编辑文本框。这不是我喜欢的解决方案,因为它带来了处理 XSS 的麻烦,但这似乎是不可避免的。如果有人遵循此解决方案,我恳请您实施客户端和服务器端输入卫生以防止 XSS。后者尤为重要,因为可以通过各种方法轻松绕过客户端验证,例如使用检查元素修改页面或使用数据包制作工具。

HTML 中的 contentEditable 文本框如下所示:

<p
    id="textbox1"
    className="textbox"
    variant="outlined" 
    contentEditable={true}
/>

从这里,我可以将compareStrings的结果分配给它:

document.getElementById("textbox1").innerHTML = htmlData

因此最终结果将有两个 contentEditable 文本框。我将不得不调用 compareStrings 两次并将结果应用于两者。

当然,我只是详细介绍了解决这个问题的要点。还有其他生活质量功能,例如复制和粘贴,详情如下: