新浦京娱乐场官网-301net-新浦京娱乐www.301net
做最好的网站

真做完发现其实并不复杂

应用Javascript获取选择文本所在的句子详解,javascript详解

前言

前段时间收受三个 issue 期望能在划词的时候还要保留单词的上下文和根源网址。这么些成效实在十分久此前就想过,但认为糟糕完结直接拖延没做。真做完开掘其实并不复杂,完整代码在此地,或许接二连三往下阅读分析。话相当的少说了,来二头看看详细的牵线吧。

原理剖析

收获接纳文本

通过 window.getSelection() 就能够获得一个 Selection 对象,再利用 .toString()就可以获取选取的公文。

锚节点与焦节点

在 Selection 对象中还保留了五个基本点音信,anchorNode 和 focusNode,分别表示选取暴发那一刻的节点和抉择甘休时的节点,而 anchorOffset 和 focusOffset 则保留了选拔在那五个节点里的偏移值。

这会儿你也许即刻就悟出第三个方案:那不就好办了么,有了全进度节点和偏移,就足以取得句子的头顶和尾巴,再把挑选文本作为中间,整个句子不就出去了么。

当然不会那样轻巧哈stuck_out_tongue。

重申一下

貌似景色下,anchorNode 和 focusNode 都以 Text 节点(何况因为此地管理的是文本,所以任何情况也会直接忽略),能够虚拟这种地方:

<strong>Saladict</strong> is awesome!

若果选用的是“awesome”,那么 anchorNode 和 focusNode 都以 is awesome!,所以取不到前面的 “Saladict”。

除此以外还大概有嵌套的事态,也是同等的主题素材。

Saladict is <strong><a href="#" rel="external nofollow" >awesome</a></strong>!

据此大家还必要遍历兄弟和父节点来取得完整的句子。

遍历到哪?

于是乎接下正是减轻遍历边界的标题了。遍历到如什么地点方停止吧?小编的论断标准是:跳过 inline-level 成分,遇到 block-level 成分结束。而决断多个因素是 inline-level 照旧 block-level 最标准的章程应该是用 window.getComputedStyle() 。但本身认为这样做太重了,也无需从严的准头,所以用了宽广的 inline 标签来判别。

const INLINE_TAGS = new Set([
 // Inline text semantics
 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i',
 'kbd', 'mark', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'small',
 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr'
])

规律总计

句子由三块组成,选取文本作为中间,然后遍历兄弟和父节点获取首尾补上。

实现

慎选文本

先获得文本,若无则脱离

const selection = window.getSelection()
const selectedText = selection.toString()
if (!selectedText.trim()) { return '' }

获得首部

对于 anchorNode 只思考 Text 节点,通过 anchorOffset 获取选取在 anchorNode 的前半段内容。

接下来先导补全在 anchorNode 此前的男生节点,最终补全在 anchorNode 父成分以前的男士元素。注意后边是因素,那样能够缩小遍历的次数,何况考虑到有的被埋伏的开始和结果无需获得,用 innerText 实际不是 textContent 属性。

let sentenceHead = ''
const anchorNode = selection.anchorNode
if (anchorNode.nodeType === Node.TEXT_NODE) {
 let leadingText = anchorNode.textContent.slice(0, selection.anchorOffset)
 for (let node = anchorNode.previousSibling; node; node = node.previousSibling) {
 if (node.nodeType === Node.TEXT_NODE) {
 leadingText = node.textContent   leadingText
 } else if (node.nodeType === Node.ELEMENT_NODE) {
 leadingText = node.innerText   leadingText
 }
 }

 for (
 let element = anchorNode.parentElement;
 element && INLINE_TAGS.has(element.tagName.toLowerCase()) && element !== document.body;
 element = element.parentElement
 ) {
 for (let el = element.previousElementSibling; el; el = el.previousElementSibling) {
 leadingText = el.innerText   leadingText
 }
 }

 sentenceHead = (leadingText.match(sentenceHeadTester) || [''])[0]
}

最后从提取句子首部用的正则是其一

// match head   a.b is ok chars that ends a sentence
const sentenceHeadTester = /((.(?![ .]))|[^.?!。?!…rn]) $/

前面的 ((.(?![ .])) 主假设为着跳过 a.b 这样的特别是在技艺文章中常见的写法。

得到后面部分

跟首部同理,换到今后遍历。最终的正则保留了标点符号

// match tail       for "..."
const sentenceTailTester = /^((.(?![ .]))|[^.?!。?!…rn]) (.)3{0,2}/

减去换行

东拼西凑完句子之后压缩多个换行为三个空白行,以及去除每行起头结尾的空白符

return (sentenceHead   selectedText   sentenceTail)
 .replace(/(^s )|(s $)/gm, 'n') // allow one empty line & trim each line
 .replace(/(^s )|(s $)/g, '') // remove heading or tailing n

总体代码

const INLINE_TAGS = new Set([
 // Inline text semantics
 'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', 'em', 'i',
 'kbd', 'mark', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'small',
 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr'
])

/**
* @returns {string}
*/
export function getSelectionSentence () {
 const selection = window.getSelection()
 const selectedText = selection.toString()
 if (!selectedText.trim()) { return '' }

 var sentenceHead = ''
 var sentenceTail = ''

 const anchorNode = selection.anchorNode
 if (anchorNode.nodeType === Node.TEXT_NODE) {
 let leadingText = anchorNode.textContent.slice(0, selection.anchorOffset)
 for (let node = anchorNode.previousSibling; node; node = node.previousSibling) {
 if (node.nodeType === Node.TEXT_NODE) {
 leadingText = node.textContent   leadingText
 } else if (node.nodeType === Node.ELEMENT_NODE) {
 leadingText = node.innerText   leadingText
 }
 }

 for (
 let element = anchorNode.parentElement;
 element && INLINE_TAGS.has(element.tagName.toLowerCase()) && element !== document.body;
 element = element.parentElement
 ) {
 for (let el = element.previousElementSibling; el; el = el.previousElementSibling) {
 leadingText = el.innerText   leadingText
 }
 }

 sentenceHead = (leadingText.match(sentenceHeadTester) || [''])[0]
 }

 const focusNode = selection.focusNode
 if (selection.focusNode.nodeType === Node.TEXT_NODE) {
 let tailingText = selection.focusNode.textContent.slice(selection.focusOffset)
 for (let node = focusNode.nextSibling; node; node = node.nextSibling) {
 if (node.nodeType === Node.TEXT_NODE) {
 tailingText  = node.textContent
 } else if (node.nodeType === Node.ELEMENT_NODE) {
 tailingText  = node.innerText
 }
 }

 for (
 let element = focusNode.parentElement;
 element && INLINE_TAGS.has(element.tagName.toLowerCase()) && element !== document.body;
 element = element.parentElement
 ) {
 for (let el = element.nextElementSibling; el; el = el.nextElementSibling) {
 tailingText  = el.innerText
 }
 }

 sentenceTail = (tailingText.match(sentenceTailTester) || [''])[0]
 }

 return (sentenceHead   selectedText   sentenceTail)
 .replace(/(^s )|(s $)/gm, 'n') // allow one empty line & trim each line
 .replace(/(^s )|(s $)/g, '') // remove heading or tailing n
}

总结

以上就是那篇小说的全体内容了,希望本文的剧情对我们的上学也许干活富有一定的参谋学习价值,若是有疑点大家可以留言沟通,多谢我们对帮客之家的辅助。

前言 近期接到一个 issue 期望能在划词的时候还要保留单词的上下文和根源网站。...

本文由新浦京娱乐场官网-301net-新浦京娱乐www.301net发布于301net网站建设,转载请注明出处:真做完发现其实并不复杂

您可能还会对下面的文章感兴趣: