使用 JavaScript 在字符串中突出显示单词的数据结构

Posted

技术标签:

【中文标题】使用 JavaScript 在字符串中突出显示单词的数据结构【英文标题】:Data structure for highlights of words in a string with JavaScript 【发布时间】:2018-07-31 08:05:59 【问题描述】:

我有一个字符串

const string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent tristique elit in volutpat iaculis. Proin a tincidunt turpis, et condimentum libero. Duis convallis nulla eu mattis porta. Nulla facilisi. Proin nec viverra orci. Nunc aliquam enim orci, ut dictum ipsum auctor ut. Quisque consectetur vestibulum tortor, mollis hendrerit velit hendrerit vel. In hac habitasse platea dictumst. Morbi volutpat lectus purus, eu sagittis odio viverra in. Phasellus vel volutpat felis. Proin a metus sit amet ipsum congue faucibus nec faucibus nisl. Aenean eu nisl ac velit dapibus vulputate non efficitur urna. Phasellus laoreet suscipit dictum. Curabitur sit amet justo at nisi dictum dapibus."

我希望能够突出显示一些连贯的单词序列并在悬停单词时显示工具提示。

数据结构会寻找什么?

我想应该是这样的

id, wordIndexStart, wordIndexEnd, note
=======================================
1, 0, 5, Some note

为了突出前6个单词,给我类似

Lorem ipsum dolor sit amet, consectetur adipiscing elit。 Praesent tristique elit in volutpat iaculis (...)

但是如何在 React 或类似的东西中返回这个带有突出显示文本的字符串?

通常,我会用

打印我的文本
<p>string</p>

但我想应该是这样的

<p>string.split(" ").map(word => <span>word).join(" ")</p>

但是我如何围绕例如前 6 个单词创建一个&lt;span&gt;

【问题讨论】:

我认为你真的很接近,我仍然会创建一个简单的组件来将每个单词包装在一个跨度中:jsfiddle.net/48ydpzb5 【参考方案1】:

我建议包括您打算突出显示的短语。我不确定您为什么要在数据结构中使用 id,也许这在数据库中是有意义的,但在视图中它们当然不需要包含在内。


    phrase: string,
    note: string

这将是您解析短语的输入句子所需的全部内容,并在匹配的地方将其替换为悬停时会显示注释的元素。

至于实现,查找短语就像获取索引sentence.indexOf(phrase) 然后使用该索引sentence.substr(0,index) + note + sentence.substr(index+phrase.length) 使用您创建的支持注释的html 来修改句子一样简单。由于此实现的大部分内容将取决于笔记的创建、支持或挂钩方式,因此我将把这部分留给您决定。

【讨论】:

【参考方案2】:

这个怎么样:

const rules = 
  start: 0,
  end: 5,
  tooltip: "Some note"
;
const Highlight = ( words ) => 
  const portion = words.split(" ").slice(rules.start, rules.end).join(" ");
  const rest = words.split(" ").slice(rules.end).join(" ");

  return (
    <span>
      <span className="highlight">portion</span>
      <span className="standard">rest</span>
    </span>
  );
;
const App = () => 
  const sentence = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent tristique elit in volutpat iaculis.';
  
  return (
    <div>
      <Highlight words=sentence />
     </div>
  )
;

ReactDOM.render(<App />, document.getElementById("root"));
.highlight 
  font-weight: bold;
  color: red;


.standard 
  color: grey;
  <div id="root"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

【讨论】:

为什么是沙盒链接?为什么不把它设为snippet?【参考方案3】:

在使用 React 时,您会希望将突出显示逻辑移到 render 函数之外

状态形状:


    content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent tristique elit in volutpat iaculis. Proin a tincidunt turpis, et condimentum libero. Duis convallis nulla eu mattis porta. Nulla facilisi. Proin nec viverra orci. Nunc aliquam enim orci, ut dictum ipsum auctor ut. Quisque consectetur vestibulum tortor, mollis hendrerit velit hendrerit vel. In hac habitasse platea dictumst. Morbi volutpat lectus purus, eu sagittis odio viverra in. Phasellus vel volutpat felis. Proin a metus sit amet ipsum congue faucibus nec faucibus nisl. Aenean eu nisl ac velit dapibus vulputate non efficitur urna. Phasellus laoreet suscipit dictum. Curabitur sit amet justo at nisi dictum dapibus.",
    highlights: [
        id: 1,
        wordIndexStart: 0,
        wordIndexEnd: 5,
        note: "Some note"
    , 
        id: 2,
        wordIndexStart: 6,
        wordIndexEnd: 10,
        note: "Some other note"
    ]

已连接组件的 mapStateToProps:

function mapStateToProps(state, ownProps) 
    const  content, highlights  = state;

    // Move this logic to ReSelect? https://github.com/reactjs/reselect
        const words = content.split(" ");

        const wordsWithHighlights = words.map((word, index) => 

            const highlightStart = highlights.find((highlightItem) => 
                return highlightItem.wordIndexStart == index;
            );
            if (highlightStart) 
                return `<span title=$highlightStart.note>$word`;
            

            const highlightEnd = highlights.find((highlightItem) => 
                return highlightItem.wordIndexEnd == index;
            );
            if (highlightEnd) 
                return `$word</span>`;
            

            return word;
        );

    return 
        content: wordsWithHighlights.join(" ")
    ;

然后是渲染函数:

render() 
    const  content  = this.props;
    return (
        <p dangerouslySetInnerHTML= __html: content  />
    )

正如@jordan 指出的那样,您不能在 React 中将 HTML 作为字符串添加,并且根据此 answer 您可能希望使用 dangerouslySetInnerHTML 属性,如上所示

【讨论】:

我认为这不会像你想象的那样奏效。 React 将转义那些 HTML 字符串。 你正在使用来自 redux 的 mapStateToProps 正确,将逻辑移至组件将是微不足道的

以上是关于使用 JavaScript 在字符串中突出显示单词的数据结构的主要内容,如果未能解决你的问题,请参考以下文章

使用 Javascript 突出显示页面上的文本

突出显示和编辑长字符串中的文本

使用python在单个字符串中突出显示并保存多个单词

在 JavaFX TextArea 中突出显示字符串

在 word addin 中查找并突出显示问题

如何使用 swift ui 显示突出显示的单词