JavaScript преобразует положение мыши в диапазон выбора - PullRequest
18 голосов
/ 15 ноября 2011

Я хотел бы иметь возможность преобразовать текущую позицию мыши в диапазон, в частности, в CKEditor.

CKEditor предоставляет API для установки курсора в соответствии с диапазоном:

var ranges = new CKEDITOR.dom.range( editor.document );
editor.getSelection().selectRanges( [ ranges ] );

Поскольку CKEditor предоставляет этот API, проблему можно упростить, удалив это требование и просто найдя способсоздать диапазон из координат мыши над элементом div, содержащим различные элементы HTML.

Однако это не то же самое, что преобразование координаты мыши в позицию курсора в текстовой области, поскольку текстовые области имеют фиксированную ширину столбцов и высоту строк, гдеCKEditor отображает HTML с помощью iframe.

На основе this похоже, что диапазон может быть применен к элементам.

Как бы вы выяснили начало / конецдиапазон, ближайший к текущей позиции мыши?

Редактировать: пример того, как можно использовать API-интерфейс ckeditor для выбора диапазона события mouseup.

editor.document.on('mouseup', function(e) {
    this.focus();
    var node = e.data.$.target;

    var range = new CKEDITOR.dom.range( this.document );
    range.setStart(new CKEDITOR.dom.node(node), 0);
    range.collapse();

    var ranges = [];
    ranges.push(range);
    this.getSelection().selectRanges( ranges );
});

Проблема с приведенным выше примером заключается в том, что целевой узел события (e.data. $. Target) запускается только для таких узлов, как HTML, BODY или IMG, но не для текстовых узлов.Даже если это так, эти узлы представляют собой фрагменты текста, которые не поддерживают установку курсора в положение мыши в этом фрагменте текста.

Ответы [ 3 ]

2 голосов
/ 15 апреля 2012

То, что вы пытаетесь сделать, действительно сложно в браузере.В частности, я не знаком с ckeditor, но обычный javascript позволяет вам выбирать текст, используя диапазон, поэтому я не думаю, что он добавляет что-то особенное.Вы должны найти элемент браузера, который содержит щелчок, а затем найти символ в элементе, который был нажат.

Обнаружение элемента браузера является простым битом: вам нужно либо зарегистрировать свой обработчик на каждом элементе, либоиспользуйте поле назначения события.Там много информации об этом, задайте более конкретный вопрос о стекапровороте, если у вас возникли проблемы.

После того, как у вас есть элемент, вам нужно выяснить, какой символ внутри элемента был нажат, затем создайте соответствующий диапазон, чтобы поместить курсор туда.Как указывалось в сообщении, на которое вы ссылались, вариации браузера делают это действительно сложно.Эта страница немного устарела, но имеет хорошее обсуждение диапазонов: http://www.quirksmode.org/dom/range_intro.html

Диапазоны не могут сообщить вам свои позиции на странице, поэтому вам придется использовать другой метод, чтобы выяснить, какой биттекста нажали.

Я никогда не видел полного решения этого в javascript.Несколько лет назад я работал над одним, но я не нашел ответа, которым я был доволен (некоторые действительно сложные случаи).Подход, который я использовал, был ужасным хаком: вставьте интервалы в текст, затем используйте их для выполнения бинарного поиска, пока не найдете наименьший возможный интервал, содержащий щелчок мыши.Пролеты не изменяют макет, поэтому вы можете использовать свойства span_x / y пролета, чтобы выяснить, содержат ли они щелчок.

Например, предположим, у вас есть следующий текст в узле:

<p>Here is some paragraph text.</p>

Мы знаем, что клик был где-то в этом абзаце.Разделите абзац пополам с помощью интервала:

<p><span>Here is some p</span>aragraph text.</p>

Если диапазон содержит координаты щелчка, продолжайте двоичный поиск в этой половине, в противном случае ищите вторую половину.

Это прекрасно работает для одиночногострок, но если текст занимает несколько строк, сначала нужно найти разрывы строк, иначе они могут перекрываться.Вы также должны решить, что делать, когда щелчок мышью был не по любому тексту, а по элементу - например, после конца последней строки в абзаце.

Поскольку я работал над этимбраузеры стали намного быстрее.Теперь они, вероятно, достаточно быстрые, чтобы добавлять s вокруг каждого символа, затем вокруг каждых двух символов и т. Д., Чтобы создать двоичное дерево, которое легко искать.Вы могли бы попробовать этот подход - было бы намного проще определить, над какой линией вы работаете.

TL; DR, это действительно сложная проблема, и если есть ответ, он может не стоитьваше время, чтобы придумать это.

1 голос
/ 28 марта 2012

Есть два способа сделать это, как и каждый WYSIWYG.

Во-первых: - ты сдаешься, потому что это слишком сложно, и в конечном итоге он станет убийцей браузера;

Во-вторых: - вы пытаетесь разобрать текст и поместить его в точное место в полупрозрачной текстовой области или в div выше оригинала, но здесь у нас есть две проблемы:

1) как бы вы анализировали динамические порции данных, чтобы получить только текст и убедиться, что вы отображаете его в точную позицию фактического контента

2) как бы вы решили обновить обновление для разбора каждого чертового символа, который вы вводите, или каждого действия, которое вы делаете в редакторе.

В конце концов, это просто «жестокая одиссея к темной стороне дерева DOM», но если вы выберете второй путь, то код из вашего поста будет работать как шарм.

0 голосов
/ 15 февраля 2019

Я работал над аналогичной задачей, чтобы позволить TinyMCE (встроенный режим) инициализироваться с помощью каретки, помещенной в положение щелчка мыши.Следующий код работает в последних версиях Firefox и Chrome, по крайней мере:

let contentElem  = $('#editorContentRootElem');
let editorConfig = { inline: true, forced_root_block: false };

let onFirstFocus = () => {
  contentElem.off('click focus', onFirstFocus);

  setTimeout(() => {
    let uniqueId = 'uniqueCaretId';
    let range    = document.getSelection().getRangeAt(0);
    let caret    = document.createElement("span");
    range.surroundContents(caret);
    caret.outerHTML = `<span id="${uniqueId}" contenteditable="false"></span>`;

    editorConfig.setup = (editor) => {
      this.editor = editor;

      editor.on('init', () => {
        var caret = $('#' + uniqueId)[0];
        if (!caret) return;

        editor.selection.select(caret);
        editor.selection.collapse(false);
        caret.parentNode.removeChild(caret);
      });
    };

    tinymce.init(editorConfig);         
  }, 0); // after redraw
}; // onFirstFocus

contentElem.on('click focus', onFirstFocus);

Пояснение

Похоже, что после события щелчка мыши / фокуса и перерисовки (setTimeout ms 0) document.getSelection().getRangeAt(0) возвращаетсядопустимый диапазон курсора.Мы можем использовать его для любых целей.TinyMCE перемещает курсор в начало при инициализации, поэтому я создаю специальный элемент span со значением 'caret' в начале текущего диапазона, а затем принудительно выбираю его редактором, а затем удаляю его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...