技术栈:React + Web Selection API
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Selection
需求描述
选中消息中的文字实现引用功能,最多显示两行,超出末尾显示省略号。
- 选中文字从第一个字符开始,到N个字符结束,显示【选中文字…】
- 选中文字从第N个开始,到最后一个字符,显示【…选中文字】
- 全选,显示【选中文字】
- 不能跨节点选中文字
实现效果
代码示例
<!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>