用到的基础web API
使用window.getSelection()并不能返回所有选中节点, 通过遍历所有选中节点的最小公共节点下的所有子节点, 并判断遍历到的节点是不是选中的节点, 可以找到所有选中的节点。
直接遍历document下的所有子节点也可以, 但使用commonAncestorContainer获取所有选中节点的最近公共节点可以缩小遍历范围。
选择的开头和结尾部分,可能是半个单词, 即半个文本节点, 需要使用splitText将文本节点切割
源码实现
js
import { v4 as uuidV4 } from 'uuid';
// 获取指定节点下的所有子节点
export function getAllNodes(rootNode, callback) {
if (!callback) {
var nodes = [];
getAllNodes(rootNode, function (node) {
nodes.push(node);
});
return nodes;
}
if (false === callback(rootNode)) return false;
if (rootNode.hasChildNodes()) {
let nodes = rootNode.childNodes;
for (var i = 0, l = nodes.length; i < l; ++i)
if (false === getAllNodes(nodes[i], callback)) return;
}
}
// 监听mouseup事件
export function listenMouseUp() {
document.addEventListener('mouseup', listenMouseUpCallback);
}
// 移除mouseup事件
export function removeListenMouseUp() {
document.removeEventListener('mouseup', listenMouseUpCallback);
}
// mouseup事件的回调函数
function listenMouseUpCallback() {
const selected = window.getSelection();
const range = selected.getRangeAt(0);
// range.insertNode(newNode()); // 在Range开头插入一个节点, 这是另外一种分隔方式
const startContainer = range.startContainer;
const startOffset = range.startOffset;
startContainer.splitText(startOffset); // 将选中范围的结束位置处的文本节点分隔为两个节点
const endContainer = range.endContainer;
const endOffset = range.endOffset;
endContainer.splitText(endOffset);
const nodes = getAllNodes(range.commonAncestorContainer);
let uuid = uuidV4();
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (selected.containsNode(node)) {
addClass(node, uuid);
}
}
}
// 给选择区域增加背景色
function addClass(node, uuid) {
if (node.nodeType === 3) {
// 将文本节点外面加一层<span class="light-hight"></span>
let newNode = document.createElement('span'); // 创建一个新的element节点
newNode.classList.add('light-hight');
newNode.classList.add('new-span');
newNode.setAttribute('data-mark-id', uuid);
node.parentNode.insertBefore(newNode, node); // 将新的节点插入到文本节点前
newNode.appendChild(node); // 将文本节点移动到新的节点里面
} else {
node.classList.add('light-hight');
node.setAttribute('data-mark-id', uuid);
}
}