$(function() { $(document).on('mouseup', '#text', function(e) { var selection = document.getSelection().getRangeAt(0); console.log(selection.toString()); // ??? }) }) 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="text"> Lorem ispum Lorem ispum <span>Lorem ispum</span> <strong>Lorem ispum</strong> Lorem ispum </div> 

Actually, how to get the ordinal number of the selected fragment inside the #text container? For example, when you select the word "ispum" inside the strong, the result should be the number 4, because this is the fourth word from the beginning, "ispum" inside #text.

  • For example, when you highlight the word "ispum" inside the strong, the result should be the number 4 - And why 4? - Stepan Kasyanenko
  • By the fact that it is the fourth word from the beginning "ispum" inside #text - norgen
  • Selected text or words? - Slavik
  • Selected Fragment. Those. you can select and "Lorem ispum", then you need to return what account the fragment is selected inside #text. - norgen
  • Why do you need it? Maybe you can solve your real problem. For some reason it seems to me that getting a serial number is not the final goal. - Stepan Kasyanenko

2 answers 2

The task is not quite trivial. Due to the difference in the representation of white space characters by the browser and their real number.

If you simplify the task by setting the value of the white-space property to the value of pre , you can use the following approach:

  1. selection.startOffset will show the index of the start of the selection inside the element.
  2. rise to the level of the container div#test simultaneously calculating the number of characters from the beginning of the element
  3. take the container's textContent and get the required substring
  4. produce a split on the selected piece of text - the number of elements in the resulting array will be the desired sequence number.

Example of implementation:

 $(function() { $(document).on('mouseup', '#text', function(e) { var selection = document.getSelection().getRangeAt(0); var index = selection.startOffset; el = selection.startContainer.previousSibling || selection.startContainer.parentNode; while (el.id != "text") { while (el.previousSibling) { el = el.previousSibling; index += el.textContent.length; } el = el.parentNode; } console.log(el.textContent.substring(0, index).split(selection.toString()).length); }) }) 
 #text { white-space: pre; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="text"> Lorem ispum Lorem ispum <span>Lorem ispum</span> <strong>Lorem ispum</strong> Lorem ispum </div> 

    To begin, let's try to figure out how to find such a sequence number. Simplify the task and assume that there are no nested blocks. Then you can find all occurrences of the selected text as long as its position is less than or equal to the left border of the selection. The search function may be:

     function findAndCount(text, search, limit) { var count = 0; var fromIndex = 0; var pos = text.indexOf(search, fromIndex); while (pos >= 0 && pos <= limit) { ++count; fromIndex = pos + search.length; pos = text.indexOf(search, fromIndex); }; return count; } 

    The next task is to come up with a way to find the left selection boundary for a document with nested blocks. Range has the property startContainer - the element with which the range starts. Elements seem to have no position property in the text, but you can try to replace the element with a unique sequence of characters, and find the position of this sequence in the text of the parent.

    Unique text can be generated like this:

     function generateBoundary(text, tryLimit) { var boundary; var pos = 0; do { --tryLimit; boundary = Math.random().toString(36).substring(2, 15); // лаконичное решение из интернета pos = text.indexOf(boundary); } while (pos >= 0 && tryLimit > 0); return boundary; } 

    The position of the element is as follows (I use jQuery):

     function findElTextPosition($el, $parent) { var boundary = generateBoundary($parent.text(), 5); $el.replaceWith(boundary); var pos = $parent.text().indexOf(boundary); return pos; } 

    Test:

     $(function() { var $mainEl = $('#text'); $(document).on('mouseup', '#text', function(e) { var selection = document.getSelection().getRangeAt(0); var text = $mainEl.text(); var selectedText = selection.toString(); var offset = selection.startOffset; var childOffset = 0; if (selection.startContainer !== $mainEl[0]) { // если выделение во вложенном элементе childOffset = findElTextPosition($(selection.startContainer), $mainEl); } console.log(selectedText, findAndCount(text, selectedText, offset + childOffset)); }); }); 

    The method is of course so-so, and if you do not clone the #text element, then it works only once. But you can finish it if you want.

    PS Probably there is no reason to use this approach, unless you are writing something very special.