1 Star 19 Fork 4

张鑫旭 / 划词评论交互实现

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
utils.js 7.13 KB
一键复制 编辑 原始数据 按行查看 历史
张鑫旭 提交于 2022-11-18 11:45 . update utils.js
/*
划词评论需要的一些方法
*/
// 特定元素相对于选区居中定位
// target 表示悬浮定位元素
// 表示选区所在目标元素,可以缺省
export function showSelectionPopover (target, paragraph) {
// 获得选区
const selection = document.getSelection();
let selectContent = selection.toString();
let selectContentTrim = selectContent.trim();
if (!selectContentTrim || !target) {
return;
}
// 如果有超出范围的内容
if (paragraph && paragraph.textContent.indexOf(selectContentTrim) == -1) {
console.warn('超出合法范围的选区');
return;
}
const range = selection.getRangeAt(0);
// 重新修改选区,不包括前后的空格
if (selectContent != selectContentTrim) {
let arrStartSpace = selectContent.match(/^\s+/g);
let arrEndSpace = selectContent.match(/\s+$/g);
if (arrStartSpace) {
range.setStart(range.startContainer, range.startOffset + arrStartSpace[0].length);
}
if (arrEndSpace) {
range.setEnd(range.endContainer, range.endOffset - arrEndSpace[0].length);
}
}
const boundRange = range.getClientRects()[0] || range.getBoundingClientRect();
// 定位处理
// ps: 这里只处理窗体滚动的定位
// 内部容器的滚动定位大家自行在这里修改处理
target.style.display = 'block';
target.style.top = (boundRange.top - target.clientHeight + window.pageYOffset - 5) + 'px';
// 居中对齐
target.style.left = (boundRange.left + boundRange.width / 2 - target.clientWidth / 2) + 'px';
}
// 隐藏悬浮元素
export function hideSelectionPopover (target) {
if (target) {
target.style.display = 'none';
}
}
// 选区高亮
// selector 表示选区容器元素的选择器
export function doRangeWrapHighLight () {
const selection = document.getSelection();
const range = selection.getRangeAt(0);
// 外面包裹标签
const surround = document.createElement('span');
surround.dataset.gid = '0';
surround.className = 'word active';
try {
range.surroundContents(surround);
} catch (e) {
console.error('选区不支持交叉覆盖');
return;
}
}
// 获取选区在元素内的起止索引值,以及选区内容
export function getContentAndIndex (selector = '.content') {
const selection = document.getSelection();
const range = selection.getRangeAt(0);
let startNode = range.startContainer;
let startOffset = range.startOffset;
// 当前选区所在的元素
let container = selection.anchorNode.parentElement.closest(selector);
if (!container) {
// 这个多半是双击框选
container = selection.anchorNode.parentElement.querySelector(selector);
if (!container && selection.anchorNode.matches && selection.anchorNode.matches(selector)) {
container = selection.anchorNode;
}
// 需要改变位置计算的起点
if (container) {
startNode = container.firstChild;
// 可能是空节点
if (!startNode.textContent) {
startNode = startNode.nextSibling;
}
startOffset = 0;
}
}
if (!container) {
console.error('不支持的选区');
return;
}
// 起始位置的计算
let startIndex = 0;
let loopIndex = function (dom) {
const nodes = [...dom.childNodes];
nodes.some(function (node, index) {
var text = node.textContent;
if (index == 0) {
text = text.trimStart();
}
if (index == nodes.length - 1) {
text = text.trimEnd();
}
if (!text) {
return;
}
// 节点匹配了
// 不再遍历
if (node == startNode) {
startIndex += startOffset;
return true;
}
if (startNode.parentNode == node) {
loopIndex(node);
return true;
}
startIndex += node.textContent.length;
});
};
// container是内容的容器元素
loopIndex(container);
const selectContent = selection.toString().trim();
// 结束索引
let endIndex = startIndex + selectContent.length;
// id结尾的data-*值
const objDataId = Object.fromEntries(Object.entries({
...container.dataset
}).filter(([key, val]) => /id$/.test(key)));
return {
...objDataId,
startIndex,
endIndex,
selectContent
}
}
// 基于 DOM 获取现在所有划词选区的起止点和内容
export function getContentAndIndexList (target, selector) {
if (!target) {
return;
}
const divTmp = document.createElement('div');
// 替换
divTmp.innerHTML = target.innerHTML;
// 最终返回的数据
let operateCommentsList = [];
// 遍历与匹配
const getRange = function () {
let eleWrod = divTmp.querySelector(selector);
if (!eleWrod) {
return;
}
let text = '';
[...divTmp.childNodes].some(function (node) {
if (node === eleWrod) {
const selectContent = node.textContent;
operateCommentsList.push({
selectContent: selectContent,
startIndex: text.length,
endIndex: text.length + selectContent.length,
gid: Number(node.dataset.gid)
});
// 节点替换
node.replaceWith.apply(node, [...node.childNodes]);
// 继续遍历
getRange();
return true;
}
text += node.textContent;
});
};
getRange();
return operateCommentsList;
}
// 反向高亮比较位置的实现
export function getNodeAndOffset(dom, start = 0, end = 0) {
const arrTextList = [];
const map = function(chlids){
[...chlids].forEach(el => {
if (el.nodeName === '#text') {
arrTextList.push(el)
} else if (el.textContent) {
map(el.childNodes)
}
})
}
map(dom.childNodes);
let startNode = null;
let startIndex = 0;
let endNode = null;
let endIndex = 0;
// 总的字符长度
let total = startIndex;
// 计算长度
arrTextList.forEach(function (node) {
if (startNode && endNode) {
return;
}
let length = node.textContent.length;
// 当前节点,总的长度范围
const range = [total, total + length];
// 看看,start和end有没有在其中
// start在这个范围中
// 可以确定startIndex了
if (!startNode && start >= range[0] && start < range[1]) {
startNode = node;
startIndex = start - total;
}
// '我要' (0, 2)
if (!endNode && end > range[0] && end <= range[1]) {
endNode = node;
endIndex = end - range[0];
}
total = total + length;
});
if (!startNode || !endNode) {
return null;
}
return [startNode, startIndex, endNode, endIndex];
};
JavaScript
1
https://gitee.com/zhangxinxu/word-comment.git
git@gitee.com:zhangxinxu/word-comment.git
zhangxinxu
word-comment
划词评论交互实现
master

搜索帮助