Use Javascript to get detailed explanation of the sentence where the selected text is located, and use javascript to explain

Source: Internet
Author: User

Use Javascript to get detailed explanation of the sentence where the selected text is located, and use javascript to explain

Preface

I recently received an issue hoping to save both the context and source URL of a word when you draw a word. This feature was actually thought about for a long time ago, but it was not easy to implement and was delayed. It is not complicated to find it. The complete code is here, or you can continue to read and analyze it. Let's take a look at the detailed introduction.

Principle Analysis

Obtain selected text

Passwindow.getSelection()You can obtain a Selection object and reuse it..toString() You can obtain the selected text.

Anchor node and focal node

The Selection object also stores two important information: anchorNode and focusNode, which respectively represent the node at the moment of generation and the node at the end of Selection, anchorOffset and focusOffset Save the offset value selected in the two nodes.

Now you may think of the first solution: Isn't that easy? With the first and last nodes and offsets, you can get the head and end of the sentence, and then use the selected text as the middle, the entire sentence does not come out.

Of course it won't be that simple.

Emphasize

Generally, anchorNode and focusNode are both Text nodes (and because the processing is Text, it will be ignored in other cases). You can consider this situation:

<strong>Saladict</strong> is awesome!

If "awesome" is selected, both anchorNode and focusNode are awesome !, Therefore, the preceding "Saladict" cannot be obtained ".

In addition, nesting is also a problem.

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

Therefore, we also need to traverse the sibling and parent nodes to obtain the complete sentence.

Where to traverse?

The next step is to solve the problem of traversing the border. Where does the traversal end? My judgment criteria are: Skip the inline-level element, until the block-level element is encountered. The most accurate way to determine whether an element is inline-level or block-level is to usewindow.getComputedStyle(). However, I think this is too heavy and does not require strict accuracy. Therefore, I use the common inline label to judge.

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

Principles

A sentence consists of three parts: select text as the middle, and traverse the sibling node and parent node to get the first and last parts.

Implementation

Select text

Obtain the text first. Exit if no text exists.

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

Retrieve Header

For anchorNode, only the Text node is considered. Use anchorOffset to obtain the content of the first half of the selected anchorNode.

Then, complete the sibling node before anchorNode, and then complete the sibling element before the parent element of anchorNode. Note that the following elements can be used to reduce the number of traversal times, And innerText is used instead of textContent, considering that hidden content does not need to be obtained.

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]}

The regular expression used to extract the first part of a sentence is

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

The preceding ((\.(?! [.]) It is mainly used to skip the common writing methods such as a. B, especially in technical articles.

Get tail

Similarly to the header, You can traverse it later. The last regular expression retains punctuation marks.

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

Compressed line feed

Compress multiple line breaks into a blank line and delete the blank characters at the beginning and end of each line.

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

Complete code

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}

Summary

The above is all the content of this article. I hope the content of this article has some reference and learning value for everyone's learning or work. If you have any questions, please leave a message to us, thank you for your support.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.