@TerritoryOfPeace
Изучаю php

Как изменять содержимое contenteditable блока в режиме реального времени?

Есть блок:
<div contenteditable="true" id="editable-block" style="border:1px solid white; height: 500px;"></div>

Пробую изменить его содержимое при вводе:
const editableBlock = document.getElementById('editable-block');
editableBlock.addEventListener('input', function() {
  const editableBlock = document.getElementById('editable-block');

// Запоминаем текущую позицию курсора
const selection = window.getSelection();
const range = selection.getRangeAt(0);
const startContainer = range.startContainer;
const startOffset = range.startOffset;

// Обновляем содержимое элемента
editableBlock.innerHTML = editableBlock.innerHTML + '!';

// Восстанавливаем позицию курсора
const newRange = document.createRange();
newRange.setStart(startContainer, startOffset);
newRange.setEnd(startContainer, startOffset);
selection.removeAllRanges();
selection.addRange(newRange);
});


Почему курсор всегда сбрасывается и как это исправить?
  • Вопрос задан
  • 204 просмотра
Решения вопроса 1
alexey-m-ukolov
@alexey-m-ukolov Куратор тега JavaScript
Сбрасывается он потому, что startContainer - это дочерний текстовый узел элемента #editable-block. И когда вы перезаписываете innerHTML, вы этот startContainer из документа изымаете и восстанавливать позицию курсора просто некуда.

Есть простые, но ограниченные решения:
  • Не убивать существующую текстовую ноду, а редактировать её текст:
    editableBlock.childNodes[0].textContent = editableBlock.textContent + "!"

  • Указывать в newRange новый элемент:
    editableBlock.innerHTML = editableBlock.innerHTML + "!";
    startContainer = editableBlock.childNodes[0];


Они подразумевают, что у вас содержимым блока является только текст, без HTML.

UPD: Нет, есть же и нормальное универсальное решение:
const editableBlock = document.getElementById("editable-block");

editableBlock.addEventListener("input", function (event) {
  // Запоминаем текущую позицию курсора
  const selection = window.getSelection();

  const {startContainer, startOffset} = selection.getRangeAt(0);

  // Обновляем содержимое элемента
  startContainer.textContent = startContainer.textContent.substr(0, startOffset) + "!" + startContainer.textContent.substr(startOffset);

  // Восстанавливаем позицию курсора
  const newRange = document.createRange();
  newRange.setStart(startContainer, startOffset + 1);
  newRange.setEnd(startContainer, startOffset + 1);
  selection.removeAllRanges();
  selection.addRange(newRange);
});

Тут мы редактируем текстовое содержимое именно того элемента, в котором в данный момент находится курсор (это по определению всегда текстовая нода). И тогда содержимое основного блока может быть любым.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы