Редактор форматированного текста всегда был дырой в области внешнего интерфейса, но если это не инженер, который имеет глубокий контакт с разработкой редактора, не обязательно ясно, где находится редактор форматированного текста.Сегодня давайте поговорим о эти вещи о веб-редакторах форматированного текста.
Обычно, когда мы получаем требование с редактором форматированного текста, нам сначала нужно уточнить сценарии использования этого требования, а затем мы можем выбрать подходящий текстовый редактор с открытым исходным кодом для этих конкретных бизнес-сценариев для индивидуальной разработки.
Взгляните на методы реализации редакторов с открытым исходным кодом, которые мы можем выбрать на рынке, которые примерно делятся на два типа:
Первый основан на свойстве Contenteditable модели THML DOM, представляющей, например, UEditor, tinyMec, Quill.
Это самая долго используемая реализация традиционного редактора форматированного текста. Преимущества этой реализации очевидны. contenteditable — это родное свойство браузера Dom. Когда значение равно true, это означает, что элемент становится редактируемым. Таким образом, многие операции редактирования контента изначально поддерживаются напрямую, включая смещение курсора, выбор контента, события клавиатуры (например, клавиши со стрелками для управления курсором) и т. д., и даже большинство реализаций, необходимых для редактирования форматированного текста (document.execCommand)
Эти встроенные поддержки делают производительность и удобство ввода очень хорошими.На этом основании вторичная разработка кажется довольно простой.С помощью технологии iframe редактор может быть помещен под независимый объект документа, который отделен от объекта документа страницы .
Недостатки тоже очень фатальные, сwhy-contenteditable-is-terribleРепрезентативная статья объясняет почти все.В заключение, это не более чем: плохая совместимость с браузером, трудно контролировать поведение пользователя, трудно абстрагировать отношения логики представления в редакторе и сопоставить их с моделью кода (представьте, что вы хотите абстрагировать изменить логическую связь переменной структуры Dom, которая не может контролироваться правилами), визуальное положение курсора (выделение) и логическое положение могут не совпадать
Второй основан на реализации кастомной Модели, такой как: draft.js, trix
Проще говоря, этот метод реализации заключается в определении набора структур данных (модели), используемых внутри редактора, которые сопоставляются с представлением Dom, которое пользователь видит в редакторе; фиксируя поведение пользователя, исходная прямая операция Dom , измените состояние структуры данных, чтобы обновить состояние структуры данных, а затем сопоставьте обновленное состояние с представлением, чтобы реализовать WYSIWYG редактора.Очевидно, что обновление структуры данных очень управляемо поведением операции.
Это очень продвинутая концепция дизайна редактора, она почти полностью отказывается от функций contenteditable, что также означает, что побочные эффекты contenteditable исчезли.
Еще одним преимуществом этой реализации является то, что ее можно применять к бизнес-сценариям, в которых несколько человек сотрудничают в Интернете. Поскольку пользовательская операция фактически влияет на внутреннюю структуру данных, а результат каждой операции контролируется в определенном диапазоне, с помощью алгоритма diff относительно легко объединить несколько модификаций за короткий промежуток времени.
Похоже, это явно лучший вариант, чем редактор contenteditable.
К сожалению, в настоящее время существует не так много редакторов с открытым исходным кодом для этой реализации.На практике она может не соответствовать всем сценариям разработки.Например, draft.js может быть основан только на реакции, в то время как относительно небольшие проекты, такие как trix в Китае, немного неприемлемо (не спрашивайте меня, откуда я знаю), если вы не используете реакцию или хотите готовый редактор для настройки, и у вас нет условий для создания собственных колес, вы не нужно рассматривать
Оглядываясь назад на редактор contenteditable, реальность не так уж и плоха. В конце концов, это наиболее широко используемый метод реализации с большой практикой. Эти зрелые проекты с открытым исходным кодом уже предоставили нам решения.
Давайте посмотрим, как они это делают:
известен в странеUEditorНапример (также редактор, используемый общедоступной учетной записью WeChat), его ядро обеспечивает так много вещей
правила DTD: используется для указания правил вложенности dom в редакторе, используется в сочетании с методом фильтрации, чтобы избежать xxx
объект uNode: объект модели документа, абстрагированный от HTML DOM, абстрагирует атрибуты и иерархические отношения DOM, сохраняет некоторые методы работы DOM (аналогично пользовательской модели второй реализации) и отображает HTML содержимого редактора. вы можете легко выполнять фильтрацию правил, например, удалять избыточные атрибуты и метки, не входящие в белый список, и т. д.
Объект диапазона: информационные объекты курсора и выбора, которые записывают начало текущего курсора (выделение), узел контейнера и смещение конечной границы, а также закрытое состояние текущего курсора (выделение), а также предоставляют ряд API-интерфейсов для курсора. (выбор) операции
База событий:Предоставляет методы для регистрации, уничтожения и запуска пользовательских прослушивателей событий для создания некоторых ловушек.
Набор инструкций execCommand:Расширенная версия document.execCommand, общий интерфейс для выполнения команд, ядро операций форматированного текста, предоставляет ряд методов выполнения и запроса состояния для указанных команд (например, выполнение команды шрифта полужирного шрифта для содержимого выделения, запрос, выделено ли содержимое текущего выделения жирным шрифтом)
менеджер отмены:Отмените стек повторов и запишите процесс изменения содержимого.
domUtils:Набор методов DOM
Вы можете использовать вышеупомянутые основные методы для объединения некоторых практических инструментов.Например, очень важная система правил фильтрации в UEditor реализована с использованием комбинации eventBase и uNode (путем инкапсуляции метода регистрации правил и метода выполнения фильтрации на базе событий). , Параметр - это объект uNode, преобразованный из dom содержимого редактора, и на основе этого объекта выполняется конкретная фильтрация)
Весь UEditor построен вокруг этих основных методов и на этой основе предоставляет большое количество API-интерфейсов, позволяющих разработчикам настраивать разработку.Очевидно, что он достаточно зрелый редактор с редактируемым содержимым.
Но в реальной производственной среде нам все еще приходится сталкиваться с некоторыми трудными ситуациями в связи с различными требованиями к продукту.
Содержимое фиксированной структуры
Распространенным сценарием является контент с фиксированной структурой, такой как изображения и аннотации к изображениям.
Это типичное содержимое с фиксированной структурой, в редакторе появляется неизменяемая фиксированная комбинация, то есть изображение должно сопровождаться полем ввода комментария.
Для достижения этой необходимости посмотреть, какие проблемы необходимо учитывать
- Элементы изображения и аннотации должны соответствовать друг другу.
- Порядок расположения элементов изображения и аннотации изменить нельзя.
- Курсор не может быть вставлен в середине фиксированной структуры
- Курсор может быть расположен внутри элемента аннотации
- В элементы комментария можно помещать только обычный текст.
Один из принципов дизайна contenteditable редактора заключается в том, что все в редакторе можно редактировать свободно, а фиксированные структурные элементы в какой-то степени нарушают этот принцип, из-за чего возникает много проблем, а у пользователей слишком много способов нарушить ваши ожидания.
Обычное решение состоит в том, чтобы обернуть элемент с фиксированной структурой в нередактируемый элемент и установить интерактивные события (такие как щелчок для ввода, вставка фильтрации содержимого) для взаимодействующих элементов внутри него независимо.
Но этого мало, есть несколько проблем:
- В редакторе есть нередактируемые элементы, и будут проблемы с совместимостью с браузером, например, курсор не может быть правильно перемещен или даже элемент не может быть удален под браузером Firefox.
- Когда два нередактируемых элемента уровня блока находятся рядом друг с другом, курсор не может быть вставлен в середину, а клавиша Backspace удалит несколько элементов одновременно.
- Скопируйте и вставьте этот контент, структура может быть перепутана.
- Другие операции также могут привести к повреждению конструкции.
Чтобы решить вышеуказанные проблемы, необходимо перехватить работу курсора пользователя (щелчок мышью, клавиши со стрелками, клавиша возврата) и в то же время установить набор структурных правил для проверки того, не нарушена ли текущая структура.
предварительный просмотр эффекта
Короче говоря, он заключается в том, чтобы определить, находится ли курсор в ближайшей позиции нередактируемого элемента путем перехвата, и, когда условия соблюдены, использовать настраиваемое поведение для проксирования выбора браузера по умолчанию, удаления, копирования и вырезания и т. д. , а затем передать событие перемещения курсора ( onSelectionChange ), чтобы проверить, соответствует ли фиксированная структура в содержимом правилам (например, должна быть хотя бы одна метка пустой строки для вставки курсора между двумя нередактируемыми элементами и т. д. )
Перед лицом содержимого с фиксированной структурой есть два решения в соответствии с разными сценариями использования.
Для сцен с простой структурой, но с необходимостью взаимодействия, таких как аннотации к изображениям, вы можете использовать вышеупомянутые правила contenteditable=false + перехват поведения + правила фильтрации для достижения
В ситуациях, когда структура более сложная, но не требует взаимодействия, или сцена взаимодействия относительно проста, вы можете использовать холст для достижения
Преимущество использования холста заключается в том, что вам не нужно беспокоиться о структуре. Это просто картинка. Если вам нужны другие взаимодействия после публикации статьи, вы можете преобразовать ее в обычную структуру DOM на странице сведений. недостатком является то, что сгенерированное изображение необходимо загрузить на сервер изображений, что потребует дополнительных ресурсов хранения.
Еще одна проблема, которую следует учитывать, заключается в том, что в браузере Safari, если есть другой домен поверх изображения на холсте, даже если разрешено настроить междоменную политику безопасности, Safari будет блокировать [SecurityError (DOM Exception 18): Операция небезопасна. ] , в котором может потребоваться использовать локальную карту для разрешения заполнителя
Решения могут быть выбраны в соответствии с реальной ситуацией
курсор
Кроме того, UE также имеет некоторые общие проблемы в качестве редактора с возможностью редактирования контента.Одной из наиболее распространенных проблем является визуальное положение и логическое положение курсора.
Представьте, что этот жирный текст отмечен красным
Когда мы поместим курсор в начало этого текста, мы обнаружим, что фактическое положение курсора имеет 4 возможности.
- |<p><span ...
- <p>|<span class="font-color-red-01">...
- <p><span class="font-color-red-01">|<strong>...
- <p><span class="font-color-red-01"><strong>|text content
Хотя разницы в визуальной производительности нет, пользователь выполняет определенные действия, когда курсор находится в разных положениях.
Первоначально мы просто хотели использовать клавишу возврата, чтобы переместить заголовок на одну строку вверх, но поскольку позиция курсора находится в позиции
|...
, формат заголовка также очищается.Решение тоже очень простое, или угон => суждение => прокси, что тоже общее решение для редактора строго контролировать курсор
Отменить повтор стека
Отмена стека повторов также является проблемой.В нормальных условиях undoManager будет автоматически записывать каждое изменение содержимого в соответствии с минимальным периодом времени, так что пользователь может отменить состояние предыдущего шага, но это также принесет некоторые проблемы.Представьте себе такой сценарий
Мы вставляем изображение локально, и это изображение нужно в конечном итоге загрузить на сервер, поэтому мы сначала вставляем изображение-заполнитель в редакторе, затем начинаем загружать локальное изображение, и после того, как сервер вернет правильный адрес изображения, затем правильное изображение элемент заменяется позицией изображения-заполнителя и компонентом, который, кстати, добавляет аннотации к изображению
Затем (вставить карту-заполнитель => загрузить изображение => заменить карту-заполнитель => добавить дополнительные компоненты) это полный поток событий, если undoManager записывает каждый шаг в этом потоке событий отдельно, когда пользователь выполняет операцию отмены, возникнут проблемы
Поэтому нам нужно установить переключатель паузы для автоматической записи, чтобы мы могли контролировать время записи undoManager.
крючки жизненного цикла
Чтобы сделать редактор более стабильным, мы также можем разработать обработчики жизненного цикла для определенных событий через eventBase.
Например, вы можете распределить обратные вызовы до и после завершения операций отмены и повтора, чтобы выполнить серию дополнительной обработки, а также вы можете распределить функции-ловушки на процесс загрузки изображений.
Темы текстовых редакторов на самом деле гораздо шире, чем перечисленные выше, например, как элегантно взаимодействовать с элементами в редакторе, как управлять домом по состоянию, как адаптироваться к мобильным терминалам, операциям с таблицами и т. д., каждый пункт может быть подробно обсуждалось, пространство ограничено, здесь не будет расширяться
Подводя итог, несколько моментов, на которые следует обратить внимание для стабильной и надежной пользовательской разработки на основе редактора contenteditable.
- Строгий контроль над контентом (проверка правил форматирования, фильтрация ввода и вывода контента)
- Жестко контролировать курсор (угон, проверка, проксирование)
- Управляет стеком отмены-повтора
- Добавьте хуки жизненного цикла для некоторых ключевых операций