Get selected text, excluding items that are not selectable

I am trying to get character offsets for the beginning and end of the selection inside the article. However, I would like some elements to be ignored from this process.

In my application, the server works with HTML before JS dynamically injects several elements, and it is very important that the number of characters in the text remains consistent between the server and the client.

I was hoping this would be as simple as window.getSelection() along with user-select: none; . Unfortunately, although the text does not appear to be selected, it is still included in the selection range.

I wrote a short example below. I had the chance to write removeFromSelection as a workaround without much success. Maybe I need to remove ranges that overlap .unselectable and manually fill in the blanks with new range objects. I feel that it should be easier than I do. How am I supposed to do this?

 function findAncestorOffset(container, node, offset) { if (node == container) return offset; var parent = node.parentNode; var syblings = parent.childNodes; for (var i = 0, len = syblings.length; i < len; i++) { if (syblings[i] == node) break; offset += $(syblings[i]).text().length; } return findAncestorOffset(container, parent, offset); } function removeFromSelection(selector, selection) { $(selector).each(function(i, node){ var range = document.createRange(); range.selectNodeContents(node); selection.removeAllRanges(range); }); } var onSelect = function(){ var sel = window.getSelection(); //removeFromSelection('.unselectable', sel); var text = sel.toString(); $('#out').text(text); var range = sel.getRangeAt(0).cloneRange(); var i = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset); $('#from').text(i); $('#to').text(i + text.length); } $('.article').mouseup(onSelect); $('.article').focusout(onSelect); 
 .unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } pre, .article { border: solid 1px black; padding: 12px; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <h1>Select This</h1> <div class="article"> <p> This can be selected. </p> <p class="unselectable"> This can't. </p> <p> And this can again. </p> </div> <h1>Output</h1> <div> From: <span id="from"></span>, To: <span id="to"></span> </div> <pre id="out"> </pre> 
+5
source share
1 answer

Well, this is not a complete solution, but it is a start. The idea is to unselectable over each paragraph element, and if it is unselectable , then remove it from the selection. I convert the selection to an array of characters and insert it at the beginning so that the index matches the index in the entire document.

I did not pay attention to the final values ​​of to and from , they are probably wrong - but more importantly, if you select all the text, you can see that it is disabled by several characters in the second unselected block. I don't have enough time to play with him, but maybe someone else can pick me up where I left off.

 function findAncestorOffset(container, node, offset) { if (node == container) return offset; var parent = node.parentNode; var syblings = parent.childNodes; for (var i = 0, len = syblings.length; i < len; i++) { if (syblings[i] == node) break; offset += $(syblings[i]).text().length; } return findAncestorOffset(container, parent, offset); } var onSelect = function(){ var sel = window.getSelection(); var textArray = sel.toString().split(''); var range = sel.getRangeAt(0).cloneRange(); var from = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset); var to = from + textArray.length; textArray = (new Array(from)).concat(textArray); var i = 0; $('.article p').each((_, rawElement) => { var element = $(rawElement); var sectionStart = i; var lengthOfSection = element.text().length; if(element.hasClass('unselectable')) { textArray.splice(sectionStart, lengthOfSection); } else { i += lengthOfSection; } }); var text = textArray.join(''); $('#from').text(from); $('#to').text(to); $('#out').text(text); } $('.article').mouseup(onSelect); $('.article').focusout(onSelect); 
 .unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } pre, .article { border: solid 1px black; padding: 12px; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <h1>Select This</h1> <div class="article"> <p> This can be selected. </p> <p class="unselectable"> This can't. </p> <p> And this can again. </p> <p> This can be selected. </p> <p class="unselectable"> This can't. </p> <p> And this can again. </p> </div> <h1>Output</h1> <div> From: <span id="from"></span>, To: <span id="to"></span> </div> <pre id="out"> </pre> 
0
source

Source: https://habr.com/ru/post/1237924/


All Articles