麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > JavaScript > 正文

利用Javascript獲取選擇文本所在的句子詳解

2019-11-19 14:47:57
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

最近收到一個(gè) issue 期望能在劃詞的時(shí)候同時(shí)保存單詞的上下文和來(lái)源網(wǎng)址。這個(gè)功能其實(shí)很久之前就想過(guò),但感覺(jué)不好實(shí)現(xiàn)一直拖延沒(méi)做。真做完發(fā)現(xiàn)其實(shí)并不復(fù)雜,完整代碼在這里,或者繼續(xù)往下閱讀分析。話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。

原理分析

獲取選擇文本

通過(guò) window.getSelection() 即可獲得一個(gè) Selection 對(duì)象,再利用 .toString() 即可獲得選擇的文本。

錨節(jié)點(diǎn)與焦節(jié)點(diǎn)

在 Selection 對(duì)象中還保存了兩個(gè)重要信息,anchorNode 和 focusNode,分別代表選擇產(chǎn)生那一刻的節(jié)點(diǎn)和選擇結(jié)束時(shí)的節(jié)點(diǎn),而 anchorOffset 和 focusOffset 則保存了選擇在這兩個(gè)節(jié)點(diǎn)里的偏移值。

這時(shí)你可能馬上就想到第一個(gè)方案:這不就好辦了么,有了首尾節(jié)點(diǎn)和偏移,就可以獲取句子的頭部和尾部,再把選擇文本作為中間,整個(gè)句子不就出來(lái)了么。

當(dāng)然不會(huì)這么簡(jiǎn)單哈stuck_out_tongue。

強(qiáng)調(diào)一下

一般情況下,anchorNode 和 focusNode 都是 Text 節(jié)點(diǎn)(而且因?yàn)檫@里處理的是文本,所以其它情況也會(huì)直接忽略),可以考慮這種情況:

<strong>Saladict</strong> is awesome!

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

另外還有嵌套的情況,也是同樣的問(wèn)題。

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

所以我們還需要遍歷兄弟和父節(jié)點(diǎn)來(lái)獲取完整的句子。

遍歷到哪?

于是接下就是解決遍歷邊界的問(wèn)題了。遍歷到什么地方為止呢?我的判斷標(biāo)準(zhǔn)是:跳過(guò) inline-level 元素,遇到 block-level 元素為止。而判斷一個(gè)元素是 inline-level 還是 block-level 最準(zhǔn)確的方式應(yīng)該是用 window.getComputedStyle() 。但我認(rèn)為這么做太重了,也不需要嚴(yán)格的準(zhǔn)確性,所以用了常見(jiàn)的 inline 標(biāo)簽來(lái)判斷。

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'])

原理總結(jié)

句子由三塊組成,選擇文本作為中間,然后遍歷兄弟和父節(jié)點(diǎn)獲取首尾補(bǔ)上。

實(shí)現(xiàn)

選擇文本

先獲取文本,如果沒(méi)有則退出

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

獲取首部

對(duì)于 anchorNode 只考慮 Text 節(jié)點(diǎn),通過(guò) anchorOffset 獲取選擇在 anchorNode 的前半段內(nèi)容。

然后開(kāi)始補(bǔ)全在 anchorNode 之前的兄弟節(jié)點(diǎn),最后補(bǔ)全在 anchorNode 父元素之前的兄弟元素。注意后面是元素,這樣可以減少遍歷的次數(shù),而且考慮到一些被隱藏的內(nèi)容不需要獲取,用 innerText 而不是 textContent 屬性。

let sentenceHead = ''const anchorNode = selection.anchorNodeif (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]}

最后從提取句子首部用的正則是這個(gè)

// match head   a.b is ok chars that ends a sentenceconst sentenceHeadTester = /((/.(?![ .]))|[^.?!。?!…/r/n])+$/

前面的 ((/.(?![ .])) 主要是為了跳過(guò) a.b 這樣的特別是在技術(shù)文章中常見(jiàn)的寫(xiě)法。

獲取尾部

跟首部同理,換成往后遍歷。最后的正則保留了標(biāo)點(diǎn)符號(hào)

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

壓縮換行

拼湊完句子之后壓縮多個(gè)換行為一個(gè)空白行,以及刪除每行開(kāi)頭結(jié)尾的空白符

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}

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)武林網(wǎng)的支持。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 草久网 | 欧美高清另类自拍视频在线看 | 91福利社在线 | 在线亚洲欧美 | 热re91久久精品国产99热 | 成人宗合网 | 亚洲一区二区三区日本久久九 | 亚洲射逼 | av在线播放观看 | 日韩欧美精品电影 | 中文字幕天堂在线 | 午夜精品久久久久久久久久久久久蜜桃 | 人人舔人人射 | 亚洲成人夜色 | 深夜毛片免费看 | 国产人妖一区二区 | 一级爱片 | 国产午夜精品久久久久婷 | 爱操av | 久久精品99久久久久久2456 | 国产99久久精品一区二区 | 特级黄一级播放 | 国产毛片自拍 | www.17c亚洲蜜桃 | 日本一区二区免费在线观看 | 久久老司机精品视频 | 免费一区在线 | www.54271.com| 亚洲精品成人av在线 | 亚洲第一综合色 | 黄色网址在线视频 | 91精品国产乱码久久久久久久久 | 欧美成人精品欧美一级 | 在线a毛片免费视频观看 | 91精品国产乱码久久久久久久久 | 日韩一级片一区二区三区 | 国产亚洲综合一区二区 | 特级黄一级播放 | 欧美18—19sex性hd | 免费视频a | 免费a级片在线观看 |