携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
💻文档地址:ahooks.js.org/zh-CN/
👨💻github地址:github.com/alibaba/hoo…
先来看一个 API
Window.getSelection:返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。
const selection = window.getSelection() ;
selection 是一个 Selection 对象。 如果想要将 selection 转换为字符串,可通过连接一个空字符串("")或使用 String.toString() 方法。
Example
function foo() {
let selObj = window.getSelection();
console.log(selObj);
let selRange = selObj.getRangeAt(0);
let selectedText = selObj.toString();
// 其他代码
}
浏览器兼容性
useTextSelection
初始化 useTextSelection,返回的数据包括选中的文本text,选中区域的top, left, bottom, right, height, width。
const initRect: Rect = {
top: NaN,
left: NaN,
bottom: NaN,
right: NaN,
height: NaN,
width: NaN,
};
const initState: State = {
text: '',
...initRect,
};
function useTextSelection(target?: BasicTarget<Document | Element>): State {
const [state, setState] = useState(initState);
const stateRef = useRef(state);
stateRef.current = state;
return state;
},选中之前,如果有文本,则进行初始化。
export default useTextSelection;
在 useEffect 中,监听 mousedown 事件,任意点击都需要清空之前的信息,选中之前,如果有文本,则进行初始化。
function useTextSelection(target?: BasicTarget<Document | Element>): State {
// ....
useEffect(() => {
// 任意点击都需要清空之前的 range
const mousedownHandler = () => {
if (!window.getSelection) return;
// 选中之前,如果有文本,则进行初始化
if (stateRef.current.text) {
setState({ ...initState });
}
const selObj = window.getSelection();
if (!selObj) return;
selObj.removeAllRanges();
};
document.addEventListener('mousedown', mousedownHandler);
return () => {
document.removeEventListener('mousedown', mousedownHandler);
};
}, [])
return state;
}
export default useTextSelection;
监听鼠标的 mouseup 事件,当鼠标松开的时候,调用 window.getSelection() API,获取 selection 对象。调用 selObj.toString() 获取文本。
selection.rangeCount:返回一个数字,表示该选区所包含的连续范围的数量。一般为1,因为通常情况下用户只能选择一个范围。
selection.getRangeAt(0):返回选区开始的节点(Node)。因为通常情况下用户只能选择一个范围,所以只有一个选区(range),此方法一般为getRangeAt(0)
Element.getBoundingClientRect():方法返回一个DOMRect对象,其提供了元素的大小及其相对于视口的位置。
function getRectFromSelection(selection: Selection | null): Rect {
if (!selection) {
return initRect;
}
if (selection.rangeCount < 1) {
return initRect;
}
const range = selection.getRangeAt(0);
const { height, width, top, left, right, bottom } = range.getBoundingClientRect();
return {
height,
width,
top,
left,
right,
bottom,
};
}
// 鼠标松开
const mouseupHandler = () => {
let selObj: Selection | null = null;
let text = '';
let rect = initRect;
if (!window.getSelection) return;
selObj = window.getSelection();
text = selObj ? selObj.toString() : '';
if (text) {
rect = getRectFromSelection(selObj);
setState({ ...state, text, ...rect });
}
};
document.addEventListener('mouseup', mouseupHandler);
Example
import React from 'react';
import { useTextSelection } from 'ahooks';
export default () => {
const { text } = useTextSelection();
return (
<div>
<p>整个页面可以选择任何文本</p>
<p>{text}</p>
</div>
);
};
监听特定区域文本选择
将 document.addEventListener('mouseup', mouseupHandler); 中的 document 替换为传入的 DOM 节点。没有传入 target,el -> document。
function useTextSelection(target?: BasicTarget<Document | Element>): State {
useEffect(() => {
const el = getTargetElement(target, document);
// 没有传入 target,el -> document
if (!el) {
return;
}
el.addEventListener('mouseup', mouseupHandler);
}, [])
return state;
}
export default useTextSelection;
ahooks 中使用的是 useEffectWithTarget,没有仔细去看,不过看名字,应该是根据传入的 Target 去变化的吧