Home > Mobile >  How to find start and end index of DOM text selection?
How to find start and end index of DOM text selection?

Time:09-18

In jQuery Terminal I want to add an API that will return indices of the selection.

Example HTML I have:

<div class="cmd" style="width: 100%; --cursor-line:1; top: 0px;">
    <div class="cmd-wrapper" style="">
        <span class="cmd-prompt" style="visibility: visible; margin-left: 0px;">
            <span data-text=">&nbsp;">
                <span style="width: 2ch;">&gt;&nbsp;</span>
            </span>
        </span>
        <div role="presentation" aria-hidden="true" class="cmd-end-line">
            <span data-text="H">
                <span>H</span>
            </span>
            <span data-text="e">
                <span>e</span>
            </span>
            <span data-text="l">
                <span>l</span>
            </span>
            <span data-text="l">
                <span>l</span>
            </span>
            <span data-text="o">
                <span>o</span>
            </span>
            <span data-text="&nbsp;">
                <span>&nbsp;</span>
            </span>
            <span data-text="W">
                <span>W</span>
            </span>
            <span data-text="o">
                <span>o</span>
            </span>
            <span data-text="r">
                <span>r</span>
            </span>
            <span data-text="l">
                <span>l</span>
            </span>
            <span data-text="d">
                <span>d</span>
            </span>
            <span data-text="&nbsp;">
                <span>&nbsp;</span>
            </span>
        </div>
        <div class="cmd-cursor-line" role="presentation" aria-hidden="true">
            <span>
                <span data-text="x">
                    <span>x</span>
                </span>
                <span data-text="x">
                    <span>x</span>
                </span>
                <span data-text="x">
                    <span>x</span>
                </span>
                <span data-text="x">
                    <span>x</span>
                </span>
                <span data-text="x">
                    <span>x</span>
                </span>
            </span>
            <span class="cmd-cursor" style="">
                <span data-text="" class="end">
                    <span>&nbsp;<span></span></span>
                </span>
            </span>
            <span></span>
        </div>
    </div>
    <textarea autocapitalize="off" spellcheck="false" tabindex="1" class="cmd-clipboard" data-cmd-prompt=">&nbsp;" style=""></textarea>
</div>

This is copy-paste of the DOM after entering "Hello World\nxxxxx" and formatted and pretty printed using https://jsonformatter.org/html-pretty-print

My question is what should I do to get the selection indices?

For example, I have a command like this:

> He|lo wor|d

I should get [2, 8] and if the selection is outside of the range: example

>|>> Hello| world

where >>> is prompt I should get [0, 5] I don't care about the negative. I should also handle when the whole selection is outside

|>>>| Hello World

it should return [0, 0] or null.

How would to implement something like this? Note: that I only care about window.getSelection API it's 100% support, not need to be silly and support IE8.

CodePudding user response:

You want something like

var range = window.getSelection().getRangeAt(0);
var start = range.startOffset;
var end = range.endOffset;

Note that this code assumes that range.startContainer === range.endContainer (which it often does). If you want to get the text / the length of the text between the start and the end containers, you need to recursively traverse the DOM between them. There is also an issue where the length of the text in the DOM is not the same as the length of the text in HTML (browsers sometimes add spaces and other HTML elements)

You'd be right if you guessed that I've worked a bunch in Javascript with selections. IMO it's kind of a nightmare. Tim Down has written a very popular package called Rangy which I recommend it a lot. You should check it out and see if it meets the requirements of what you are doing.

CodePudding user response:

I've solved the issue myself:

 var selection = window.getSelection();
 var start = $(selection.anchorNode);
 var end = $(selection.focusNode);

 var before = start.closest('.cmd [role="presentation"]').prevUntil('.cmd-prompt');
 var count = 0;
 if (before.length > 1) {
     count = before.find('[data-text]').length;
 }
 var s = start.closest('.cmd [role="presentation"] [data-text]');
 var e = end.closest('.cmd [role="presentation"] [data-text]');

 if ((s.length || e.length)) {
     start = count   s.index();
     end = count   e.index()   1;
     console.log({start, end});
 }
  • Related