简单记录下Selection与Range的实操记录
- 点击插入关键词(关键词高亮)按钮,插入关键词到contentEditable属性为true的元素中
- 点击可编辑元素中的高亮关键词,中间可编辑,如果光标插入关键词尾部,不可编辑,文字颜色不能与关键词颜色一致
- 如果光标移动到可编辑元素的尾部,可插入关键词,插入后输入文字颜色也不能与关键词颜色一致
import { type FC } from "react";
import { useRef } from "react";
import Page from "@/components/Page";
import { Button } from "antd-mobile";
const TestPage: FC = () => {
const elRef = useRef<HTMLDivElement>(null);
const onClick = () => {
const selection = window.getSelection();
const range = selection?.getRangeAt(0);
const newNode = document.createElement("span");
newNode.setAttribute("style", "color: red");
newNode.setAttribute("data-highlight", "1");
newNode.appendChild(document.createTextNode("hello world"));
range?.insertNode(document.createTextNode("\u200B"));
range?.insertNode(newNode);
range?.collapse();
selection?.removeAllRanges();
};
const onContentEditableClick = () => {
const selection = window.getSelection();
const range = selection?.getRangeAt(0);
const { endContainer } = range || {};
if (
endContainer?.parentNode &&
(endContainer?.parentNode as HTMLSpanElement).hasAttribute(
"data-highlight",
)
) {
if (selection?.focusOffset === endContainer?.nodeValue?.length) {
const nodes = elRef.current?.childNodes || [];
const lastNode = nodes[nodes.length - 1];
const tagNode =
lastNode?.nodeType === 1 ? lastNode : lastNode.previousSibling;
if (
tagNode?.nodeType === 1 &&
(tagNode as HTMLSpanElement)?.hasAttribute?.("data-highlight")
) {
(tagNode as HTMLSpanElement).setAttribute("contentEditable", "false");
return;
}
selection?.removeAllRanges();
const newRange = new Range();
(endContainer?.parentNode as HTMLSpanElement).setAttribute(
"contentEditable",
"false",
);
newRange.setEndBefore(endContainer?.parentNode.nextSibling as Node);
newRange.collapse();
selection?.addRange(newRange);
} else {
(endContainer?.parentNode as HTMLSpanElement).setAttribute(
"contentEditable",
"true",
);
}
}
};
return (
<Page className="box-border !bg-none">
<div
ref={elRef}
suppressContentEditableWarning
contentEditable
onClick={onContentEditableClick}
>
useRequest 是一个强大的异步数据管理的 Hooks,React
项目中的网络请求场景使用 useRequest 就够了。
</div>
<Button onClick={onClick}>插入关键词</Button>
</Page>
);
};
export default TestPage;