背景:我们经常看官网的一些技术文档,当遇到不会的单词都需要查询一下,每次都需要打开一个翻译网站再粘贴对应的单词,翻译结束后再返回继续阅读文档,tab页之间的来回切换比较麻烦。后来发现了Chrome中的一些翻译插件,只要鼠标滑动既可以翻译对应单词,刚好符合需求。但Chrome翻译插件需要翻墙,这是痛点。 之后up主自己做了一款翻译插件,思路都是一样的。
如图所示
思路:在鼠标选中一段文字后弹出一个icon,点击即可翻译
问题:如何获取鼠标当前选中的文本,这就用到了今天的Selection对象
获取Selection对象
如图所示,用鼠标选中一段文字,然后控制台执行 window.getSelection()便可以获取到
var selection = window.getSelection();
console.log(selection);
Selection对象中的一些概念
先介绍三个概念,锚点,焦点,范围
锚点 (anchor)
蓝色选区的起点我们称为锚点 (anchor)。 锚点就是我们鼠标按下瞬间的那个点。 在用户拖动鼠标时,锚点是不会变的。
与锚点相关的有两个属性
- anchorNode: 该选区起点所在的节点
- anchorOffset 返回一个数字,其表示的是选区起点在 anchorNode 中的位置偏移量。 如果 anchorNode 是文本节点,那么返回的就是从该文字节点的第一个字开始,直到被选中的第一个字之间的字数(如果第一个字就被选中,那么偏移量为零)
焦点 (focus)
蓝色选取的终点我们称为焦点 (focus) 焦点是你的鼠标松开瞬间所记录的那个点。 随着用户拖动鼠标,焦点的位置会随着改变。
与焦点相关的也有两个属性
- focusNode 返回该选区终点所在的节点。
- focusOffset 返回一个数字,其表示的是选区终点在 focusNode 中的位置偏移量。 如果 focusNode 是文本节点,那么选区末尾未被选中的第一个字,在该文字节点中是第几个字(从 0 开始计),就返回它。
范围 (range)
整个蓝色区域就是范围 (range) 默认情况下,用户拖动鼠标选择文本,一个Selection对象只包含一个range。 但通过代码可以选中多个range
可以通过以下代码获取当前用户选中的区域
Selection.getRangeAt(0)
//返回当前第一个选区 一般用户只能选择一个选区
toString获取选中的文本
window.getSelection().toString()
可以获取选中区域的文本
翻译插件的小案例
思路
- 鼠标单击释放的时候,获取当前选中的文本,如果文本字符大于0,则在选中区域弹出一个icon,点击可以翻译
- 鼠标单击按下的时候,移除页面已有的icon小图标
对应的两个事件就是mousedown和mouseup
结果如图所示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Selection</title>
<script>
const containerDom = document.createElement("span");
containerDom.style.position = "fixed";
containerDom.style.transform = "translateX(-50%)";
containerDom.style.backgroundColor = "transparent";
containerDom.style.cursor = "pointer"
const icon = document.createElement("img");
icon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAACwElEQVQ4jY2Uz2tUVxTHP+e+N88kMzbTJP4gxWTGJIoGaTK4EQXTIrUIYipUUbpoUdwWFwX3Ltx0UXDpwr+gktDSLrpoMQpaGiIqRjv+IC4ScbRJnZnojO/d08X7MRM7why43PvuvefzvufHe3L8YnXOWsYUQEEBVSCabVBbmf6u50PaMGMtY6qRYwRQwEYzxssePv9ksi1Y7NzKwn2B1Poj7cBcpQn2Pqoxk7fvFHN9vZnkkrU2dNHkedqNcxXblh6lMxVu3X8moTbjZWcf1ycOZC2e56KqiMgamIhMuDSFeWqfz95hm4CvFQ2XZlwU4f6LLnavvKKnpxvHMWtAESwKU6EwYNk7bJm65TA151AYsMwvmeRycXk9tVqZSqVCJpPGmMZZko24AFt6ldU6TM057Bu2fDZq+faAz+R4gAJ17eDuooPvB/x09QaLpX8SSKzQjYWu1qDLg760UqoI80vCF+MBpXKsQJj6619+v34VJyX8ee8hjpsChInCKBOFUdw4YbMLhsnxgHOHfGaKhh2bw4OZv52kmesdeQq5RYYG+kCEK3/c5Ogne8h/tCkKMypmqSxc+CXFwstQUaen/PCby/ySNIrtZJh58JrbxQWePivRne5ix2A/3emuUPvh76ut20wbU5wTBTbJI/TlrxgHvvp8P7n+jRhjcF23oSzpt2hoCxAKy7afIFAUWKlUkwL4vt/I2bvK1kCaFjXtYM/O/eT7yvx8bRYRYWxbHlXFtUENcdatAbQKWZOl8PztZg4NZnEcw817RT4eyYVNG9TLZ8W8OfIupRlmK9UxVZsNXwhPqj5vdyu7hgYTVQBCG/bp0R8vi+n4GoWUazl9YgPbhz7A81yMEUTC8f9vooVZ60+LCJ4HZ05uZCSfIZUygDb/NdpTBnDw2JXlb77ckN2aS7POcxpqjMEYQ71ev/UfWKE92u2OneAAAAAASUVORK5CYII=";
icon.style.width = "19px";
containerDom.appendChild(icon);
const getSelectionRectDimensions = () => {
let width = 0,
height = 0,
dx = 0,
dy = 0,
text = '';
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0).cloneRange();
if (range.getBoundingClientRect) {
const rect = range.getBoundingClientRect();
width = rect.right - rect.left;
height = rect.bottom - rect.top;
dx = rect.left;
dy = rect.top;
text = selection.toString()
}
}
}
return { text, width, height, dx, dy };
};
const mouseupEvent = () => {
const rect = getSelectionRectDimensions();
console.log(rect)
if (rect.text === '') {
containerDom.style.display = "none";
if(document.body.contains(containerDom)){
document.body.removeChild(containerDom);
}
return;
}
containerDom.style.display = "block";
containerDom.style.top = rect.dy + rect.height + 5 + "px";
containerDom.style.left = rect.dx + rect.width / 2 + "px";
document.body.appendChild(containerDom);
};
const mousedownEvent = ()=>{
containerDom.style.display = "none";
if(document.body.contains(containerDom)){
document.body.removeChild(containerDom);
}
}
document.addEventListener("mouseup", mouseupEvent);
document.addEventListener("mousedown",mousedownEvent)
</script>
</head>
<body>
<div id="myDiv" style="color: red; margin-top: 100px">
Selection 对象所对应的是用户所选择的
ranges(区域),俗称拖蓝。默认情况下,该函数只针对一个区域
</div>
</body>
</html>
其他方法
Selection对象方法
- getRangeAt 返回选择的第n个区域
- Selection.getRangeAt(0),返回当前第一个选取 一般用户只能选择一个选区
- addRange() 向选区(Selection)中添加一个区域(Range)。
- removeRange,removeAllRanges 将所有的区域都从选区中移除。
- 将选区折叠为一个点
- collapseToStart,将当前的选区折叠到起始点。
- collapseToEnd 将当前的选区折叠到最末尾的一个点。
- collapse 将当前的选区折叠为一个点。
- containsNode 判断某一个 Node 是否为当前选区的一部分。
- deleteFromDocument从页面中删除选区中的内容。
- selectAllChildren 把指定元素的所有子元素设为选中区域,并取消之前的选中区域。
- extend(node, offset) 移动选中区的起点到指定的点。选中区的起点不会移动。选中区将从起点开始到新的终点
- modify 以通过简单的文本命令来改变当前选区或光标位置。
扩展
- 思路可以扩展为文本选中后调用讯飞接口,语音阅读,然后将选中的单词和结果存储到本地,形成本地词典
- 翻译接口和发音接口我们会调用第三方api,有的是收费的,形成本地词典后,相同的单词不再去服务端请求,直接本地获取(也可以用服务端缓存技术)
- 后期报表统计我们常用的单词查询频率,便于复习。
Chrome插件的开发流程就不赘述了,这个官网demo已经很多了