feat: 选中消息中的文字引用

技术栈:React + Web Selection API
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Selection

需求描述

选中消息中的文字实现引用功能,最多显示两行,超出末尾显示省略号。

  1. 选中文字从第一个字符开始,到N个字符结束,显示【选中文字…】
  2. 选中文字从第N个开始,到最后一个字符,显示【…选中文字】
  3. 全选,显示【选中文字】
  4. 不能跨节点选中文字

实现效果

在这里插入图片描述

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>测试一大堆文字引用</title>
  <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <style>
    div {
      /* 只允许当前选中节点选择文本,其他节点禁止选择 */
      user-select: none;
      margin-bottom: 20px;
    }
    #root {
      padding-top: 50px;
    }
    #root > div {
      padding: 10px;
      border: 1px solid #ccc;
      box-sizing: border-box;
      width: 500px;
      height: 200px;
    }
    .ellipsis {
      /* 限制超出两行显示省略号 */
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  </style>
</head>
<body>
  <div id="root"></div>
</body>
<script type="text/babel">
  const {useState, useEffect} = React
  const SelectedText = () => {
    const [text, setText] = useState('测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用测试一大堆文字引用')
    const [curSelectId, setCurSelectId] = useState(0) // 当前选中的节点ID,用于控制当前选中的节点是否可选
    const [curFormatText, setCurFormatText] = useState('') // 当前选中的文本(格式化后的文本)

    const onSelectionChange = () => {
      const selection = window.getSelection()
      
      if (selection && selection.type === 'Range' && selection.rangeCount > 0) {
        const fullText = selection.anchorNode.textContent  // 获取元素中所有文本内容
        const selectedText = selection.toString()  // 获取选中文本
        const range = selection.getRangeAt(0)  // 获取选区
        const startIndex = fullText.indexOf(selectedText, range.startOffset) // 起始位置
        const endIndex = startIndex + selectedText.length // 结束位置

        // 1、全选文本
        if (startIndex === 0 && endIndex === fullText.length) setCurFormatText(selectedText)
        // 2、选中文本在中间位置
        else if (startIndex > 0 && endIndex < fullText.length) setCurFormatText(`...${selectedText}...`)
        // 3、选中文本在元素最后一个位置
        else if (endIndex === fullText.length) setCurFormatText(`...${selectedText}`)
        // 4、选中文本在元素第一个位置
        else setCurFormatText(`${selectedText}...`)
      }
    }

    useEffect(() => {
      document.addEventListener('selectionchange', onSelectionChange)  // 监听选区改变事件
      return () => document.removeEventListener('selectionchange', onSelectionChange) // 移除监听
    }, [])

    return (
      <>
        {[{ id: 111 }, { id: 222 }].map(item => (
          <div
            key={item.id}
            className="myText"
            style={{ userSelect: item.id === curSelectId ? 'text' : 'none'}}
            onMouseDown={() => setCurSelectId(item.id)}
          >
            {text}
          </div>
        ))}
        <div>
          <div>展示格式化引用文本内容:</div>
          <div className="ellipsis">{curFormatText}</div>
        </div>
      </>
    )
  }

  ReactDOM.render(<SelectedText />, document.getElementById("root"))
</script>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值