Почему мой контент-ориентированный курсор прыгает до конца в Chrome?

Сценарий

У меня есть contenteditable <div> область, и внутри этой области у меня может быть некоторый <span contenteditable="false"></span> содержащий некоторый текст. Идея состоит в том, что эти элементы span будут представлять собой стилизованный текст, который нельзя редактировать, но его можно удалить из области <div contenteditable="true"></div>, нажав клавишу возврата.

Проблема

Размещение курсора здесь большое. если вы удалите один из этих элементов <span>, курсор переместится в конец <div>. Более интересно, если вы набираете текст, когда курсор находится "в конце", размещение текста просто отлично... Затем, если вы удаляете только что напечатанный текст, курсор отскакивает назад!

Я подготовил скрипку, которая продемонстрирует это. Мне нужно, чтобы это работало только в Chrome, а другие браузеры сейчас либо неактуальны, либо имеют обходные пути. (Также обратите внимание, что подготовленный Fiddle создан для демонстрации этого только в Chrome).

Fiddle


Инструкция Fiddle: Версия Chrome версии 39.0.2171.95 м (64-разрядная версия), воспроизводимая также в 32-разрядной версии

  • Нажмите <div> область
  • Введите "123"
  • Backspace "3" Backspace "2" Backspace "1" enter image description here

Дополнительные сведения

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

Ближайшее решение SO, которое я нашел, может быть здесь. Идея в этом решении состоит в размещении &zwnj; символов после элементов <span>, но если я хочу удалить мой <span>, вместо этого я удалю &zwnj;... заставляя мой курсор прыгать до конца, предлагая странный опыт пользовательского интерфейса, не удаляя мой <span> по моему "начальному удалению ключа".

Вопрос

Кто-нибудь испытал эту проблему и нашел работу? Я приветствую любое возможное решение, даже когда JS взломан, когда они приходят. Я пришел к выводу, что использование contenteditable сопровождается списком белья, но это, по-видимому, последняя оставшаяся трудность, которую я сейчас испытываю.

Ответ 1

Я не знаю, почему это происходит, но у меня было ощущение, что это имеет отношение к размеру <div>, поэтому я попытался сыграть с свойством display. Установив его на inline-block и немного поиграв с текстом, я обнаружил, что проблема исчезла после внесения некоторых изменений в нее, в частности, добавив новую строку.

Я видел, что по какой-то причине тег <br/> хранится в div.main после удаления моей новой строки, но внешний вид <div> и способ, которым он отвечает на клавиши со стрелками, такие же, как если бы в нем нет новой линии.

Итак, я перезапустил скрипт с изменением CSS и тегом <br/> в div.main и альта!

Итак, заключаем:

  • Добавить display: inline-block в div.main
  • добавить <br/> в конец div.main

ссылка JSFiddle

Ответ 2

Проблема заключается в том, что ваш элемент contenteditable является div, который по умолчанию - display: block. Это то, что вызывает проблему с позицией каретки. Это можно устранить, сделав внешний div нередактируемым и добавив новый редактируемый div, который будет обрабатываться как inline-block.

Новый HTML будет иметь новый div только внутри внешнего (и соответствующий закрывающий тег в конце):

<div id="main" class="main"><div id="editable" contenteditable="true">...

И добавьте это в свой CSS:

div#editable {
    display: inline-block;
}

Для того, чтобы лучше видеть карету, когда она находится между элементами span, я также добавил margin: 2px в правило для div.main span в CSS, но это не обязательно для предотвращения сообщения о проблеме с кареткой в вопросе.

Вот fiddle.

По мере того как вы начали обнаруживать, contenteditable обрабатывается непоследовательно в браузерах. Несколько лет назад я начал работать над проектом (в редакторе XML в браузере), где, как я думал, contenteditable сделает все проще. Затем, когда я разработал приложение, я вскоре обнаружил, что беру на себя функции, которые, как я думал, contenteditable предоставит мне бесплатно. Сегодня только contenteditable дает мне то, что он включает события клавиатуры на элементах, которые я хочу редактировать. Все остальное, включая движение каретки и отображение каретки, управляется пользовательским кодом.

Ответ 3

Итак, у меня есть немного более чистая реализация вашего подхода, который опирается на событие input, которое запускается, когда a contenteditable обновляется. Это событие не поддерживается IE, но похоже, что contenteditable в любом случае довольно неуловимо в IE.

Он в основном вводит элементы вокруг каждого элемента, помеченного как contenteditable="false". Вероятно, это может быть сделано при начальной загрузке, а не только в событии input, но я решил, что у вас могут быть способы ввода большего количества span в область, поэтому input должен учитывать это.

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

ссылка JSFiddle

Javascript

$('#main').on('input', function() {
    var first = true;
    $(this).contents().each(function(){
        if ($(this).is('[contenteditable=false]')) {
            $("<i class='wrapper'/>").insertAfter(this);
            if (first) {
                $("<i class='wrapper'/>").insertBefore(this);
                first = false   
            }
        }
    });
}).trigger('input');

CSS

div.main {
    width: 400px;
    height: 250px;
    border: solid 1px black;
}

div.main span {
    width:40px;
    background-color: red;
    border-radius: 5px;
    color: white;
    cursor: pointer;
}

i.wrapper {
    font-style: normal;
}

Ответ 4

Я нашел хакерский способ решить начальную проблему с ошибкой в ​​конце <div> с некоторым JQuery. Я в основном вставляю пустой <i> для курсора для "фиксации" в конце содержимого <div>. Этот тег работает для меня, потому что для конечного пользователя нет разницы в пользовательском интерфейсе, если отменить его из обычного текста из-за переопределения font-style: normal (см. Скрипку), и мне также понадобилось что-то другое, чем <span>, чтобы вставить

Я не особенно взволнован этим обходным решением, особенно выбрав <i> только потому, что, но у меня нет лучшей альтернативы, и я по-прежнему приветствую лучшие решения, если они существуют!. Я планирую продолжать работать над этим, чтобы, надеюсь, выяснить стрелки, но, к счастью, меня беспокоит только этот глюк в скрипке, а не мой код.


Fiddle (только для Chrome)


<div id="main" contenteditable="true">
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
</div>

function hackCursor() {    
    return $('#main :last-child').get(0).tagName === 'SPAN' ? 
        $('#main span:last-child').after('<i style="font-style: normal"></i>') : 
        false;
}

$(function(){

    hackCursor();

    $('#main').bind({
        'keyup': function(e) {
            hackCursor();
        }
    })
});

Ответ 5

Просто добавьте новую строку char (\n) перед закрытием тега contenteditable. Например, в JavaScript: var html = '<div id="main" contenteditable="true">' + content + "\n" + '</div>'