Подтвердить что ты не робот

Установите курсор в определенную позицию в CKEditor

Есть ли способ установить позицию курсора на известный индекс внутри CKEditor?

Я хочу сделать это, потому что, когда я изменяю html внутри редактора, он сбрасывает курсор на начало вставленного элемента, что является проблемой, поскольку я изменяю содержимое "на лету", как пользовательский тип.

Если я знаю, что я хочу установить курсор обратно в известную позицию символа, например, 100 внутри редактора, возможно ли это?

(Я задал вопрос, связанный с, но я думаю, что я смущал проблему с примером кода.)

4b9b3361

Ответ 1

Основной способ установки выбора - создание Range, установка его позиции и выбрав его.

Примечание: если вы не знаете API диапазона (или, по крайней мере, идею, которая стоит за диапазонами), вы не сможете использовать выбор. Здесь довольно хорошее введение - DOM Range spec (да, это спецификация, но это хорошо). API CKEditor Range API очень похож, но немного больше.

Например:

// Having this HTML in editor:
// <p id="someId1">foo <em id="someId2">bar</em>.</p>

var range = editor.createRange();
range.setStart( editor.document.getById( 'someId1' ), 0 ); // <p>^foo
range.setEnd( editor.document.getById( 'someId2' ).getFirst(), 1 ); // <em>b^ar</em>

editor.getSelection().selectRanges( [ range ] );

// Will select:
// <p id="someId1">[foo <em id="someId2">b]ar</em>.</p>

Или в другом случае:

// Having this HTML in editor:
// <p>foo bar.</p>
var range = editor.createRange();
range.moveToElementEditablePosition( editor.editable(), true ); // bar.^</p>

editor.getSelection().selectRanges( [ range ] );

// Will select:
// <p>foo bar.^</p>

Восстановление выбора после изменения DOM

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

Диапазон сохраняет ссылки на свои начальные и конечные контейнеры (в свойствах startContainer и endContainer). К сожалению, эти ссылки могут быть нарушены:

  • перезапись innerHTML,
  • перемещение узлов DOM вокруг,
  • удаление узлов DOM.

То же самое может произойти с смещениями (startOffset и endOffset properties) - если вы удалили один из дочерних узлов стартового/конечного контейнера, эти смещения могут нуждаться в обновлении.

Таким образом, в некоторых ситуациях экземпляр диапазона не помогает, когда мы хотим запомнить позицию выбора. Я объясню три основных способа решения этой проблемы.

Во-первых, это наш план:

  • Мы получаем текущую позицию выбора.
  • Мы храним его (как-то).
  • Мы выполняем изменения DOM.
  • Мы восстанавливаем выбор.

Примечание: Теперь я использую "диапазоны" во множественной форме, потому что Firefox поддерживает несколько вариантов выбора диапазона: один выбор может содержать более одного диапазона (попробуйте, например, использовать CTRL-ключ при выборе).

Решение 1 - диапазоном

var ranges = editor.getSelection().getRanges();

// Make DOM changes.

editor.getSelection().selectRanges( ranges );

Это простейшее решение. Он будет работать только в том случае, если изменения DOM, которые мы сделали, не имеют устаревших диапазонов или мы знаем, как их обновлять.

Решение 2 - интрузивные закладки

var bookmarks = editor.getSelection().createBookmarks();

// Make DOM changes.

editor.getSelection().selectBookmarks( bookmarks );

Закладки, созданные методом createBookmarks, вставляют невидимые элементы <span> со специальными атрибутами (включая data-cke-bookmark) в начальных и конечных точках диапазона выбора.

Если вы можете избежать неконтролируемых изменений innerHTML и вместо этого добавить/удалить/переместить некоторые узлы, просто помните, что вам нужно сохранить эти элементы <span>, и этот метод будет работать отлично. Вы также можете перемещать элементы закладок, если ваши изменения также должны изменить выбор.

По умолчанию закладки сохраняют ссылки на их элементы <span>, но вы также можете создавать сериализуемые закладки, передавая true методу createBookmarks. Такие закладки будут содержать ссылки на узлы по идентификаторам, поэтому вы можете перезаписать весь innerHTML.

Примечание. Этот метод также доступен в API диапазона.

Это самый популярный метод, потому что у вас есть полный контроль над выбором, и вы можете изменить DOM, хотя вам нужно позаботиться о закладках spans.

Решение 3 - неинтрузивные закладки

var bookmarks = editor.getSelection().createBookmarks2();

// Make DOM changes.

editor.getSelection().selectBookmarks( bookmarks );

Примечание. В этом решении мы используем метод createBookmarks 2.

Здесь мы также создаем массив объектов закладок, но мы не вставляем никаких элементов в DOM. Эти закладки сохраняют свои позиции по адресам. Address - это массив индексов предков в родителях.

Это решение очень похоже на решение 1, но вы можете перезаписать весь innerHTML, потому что он (скорее всего; > ) не изменит адреса узлов закладок. Хотя в таком случае вам нужно передать true в createBookmarks2, чтобы получить нормализованные адреса, потому что смежные текстовые узлы будут соединены и пустые будут удалены при установке innerHTML.

Подводя итог...

... Работа с DOM и выбором не является тривиальной. Вам нужно знать, что вы делаете, вам нужно знать DOM, и вам нужно выбрать правильное решение для вашей проблемы. Чаще всего это будет второй, но это зависит от случая.

Ответ 2

Ответ от Рейнмара привел меня к этому решению

var selection = ed.getSelection();
var bookmarks = selection.createBookmarks(true);

//delete text from editor

var range = selection.getRanges()[0];
range.moveToBookmark(bookmarks[0]);
range.select();

ПРИМЕЧАНИЕ. Функция moveToBookmark не документирована в api, но была чрезвычайно полезной и была единственным решением, которое сработало для меня. Я, конечно, не специалист по ckeditor и взял несколько дней, чтобы найти рабочее решение. Поэтому moveToBookmark может быть устаревшей функцией, я не уверен.

Ответ 3

Это будет работать наверняка. CKEDITOR.config.startupFocus = 'end';