Как найти начальный и конечный индекс выделения текста DOM?

#javascript #html #dom #textselection

Вопрос:

В терминале jQuery я хочу добавить API, который будет возвращать индексы выбора.

Пример HTML у меня есть:

 <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=">amp;nbsp;">
                <span style="width: 2ch;">amp;>amp;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="amp;nbsp;">
                <span>amp;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="amp;nbsp;">
                <span>amp;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>amp;nbsp;<span></span></span>
                </span>
            </span>
            <span></span>
        </div>
    </div>
    <textarea autocapitalize="off" spellcheck="false" tabindex="1" class="cmd-clipboard" data-cmd-prompt=">amp;nbsp;" style=""></textarea>
</div>
 

Это копия-вставка DOM после ввода «Hello Worldnxxxxx», отформатированная и красиво напечатанная с использованием https://jsonformatter.org/html-pretty-print

Мой вопрос в том, что я должен сделать, чтобы получить индексы отбора?

Например, у меня есть такая команда:

 > He|lo wor|d
 

Я должен получить [2, 8] , и если выбор находится за пределами диапазона: пример

 >|>> Hello| world
 

где >>> подсказка, которую я должен получить [0, 5] , меня не волнует негатив. Я также должен справляться, когда весь выбор находится снаружи

 |>>>| Hello World
 

он должен возвращать [0, 0] значение или значение null.

Как бы реализовать что-то подобное? Примечание: меня волнует только API window.getSelection, это 100% поддержка, не нужно быть глупым и поддерживать IE8.

Комментарии:

1. Я не понимаю: разве window.getSelection (с его начальным и конечным api) не является ответом?

2. @ControlAltDel, Где у вас есть начальный и конечный API в выборе, я не вижу его в MDN developer.mozilla.org/en-US/docs/Web/API/Selection

Ответ №1:

Вы хотите что-то вроде

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

Обратите внимание, что этот код предполагает, что range.startContainer === range.endContainer (что он часто делает). Если вы хотите получить текст / длину текста между начальным и конечным контейнерами, вам нужно рекурсивно пересечь DOM между ними. Существует также проблема, когда длина текста в DOM не совпадает с длиной текста в HTML (браузеры иногда добавляют пробелы и другие элементы HTML).

Вы были бы правы, если бы догадались, что я много работал в Javascript с выделениями. ИМО, это своего рода кошмар. Тим Даун написал очень популярный пакет под названием Rangy , который я ему очень рекомендую. Вы должны проверить его и посмотреть, соответствует ли он требованиям того, что вы делаете.

Ответ №2:

Я сам решил эту проблему:

  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});
 }