10 November 2010

WYSIWYG HTML редактор в браузере. Часть 2

Website development
Original author: OLAV JUNKER KJÆR
Это вторая часть перевода статьи о свойствах designMode и contentEditable, их поведении и особенностях.

В статье рассматриваются базовые принципы и проблемы унификации особенностей редактирования в современных браузерах. Темы рассматриваемые в статье:
  • Различные методы включения режима редактирования
  • Команды редактирования
  • HTML генерируемый в процессе редактирования
  • Взаимодействие с DOM

WYSIWYG HTML редактор в браузере. Часть 1 находится тут.

Команды редактирования


В браузерах поддерживается множество команд редактирования. HTML генерируемый этими командами не стандартизирован и отличается от браузера к браузеру. Например, в IE «Bold» сгенерирует код:
  1. <strong>Hello!</strong>
Safari генерирует:
  1. <span class="Apple-style-span" style="font-weight: bold;">hello!</span>


Код, как минимум в IE, несколько старомоден. Во множестве команд используется ужасный тег font:
  1. <FONT color=#ff0000>23</FONT>

Генерируемая разметка не является валидным XHTML и как правило даже валидным HTML кодом.

Opera генерирует код сходный с IE (нет, не полностью), используя тег font и так далее.

Safari генерирует форматирование используя span'ы и инлайн CSS. Достоинство подхода Safari в том, что он по крайней мере является валидным HTML 4.01 Strict.

Mozilla поддерживает 2 режима работы — она либо генерирует презентационные теги как IE/Opera или же стилизирует контейнеры как Safari.

Если вы уверены, что вам нужен валидный HTML код, то вам стоит чистить генерированный редактором код на стороне сервера, что бы из этого безобразия получить валидный (X)HTML. (Ну, вам это в любом случае стоит делать, что бы избежать XSS-атак).

Горячие сочетания клавиш


Множество команд доступно через горячие сочетания клавиш, например Ctrl/Cmd + B для полужирного начертания, Ctrl/Cmd + Z для отмены последнего действия и т.д. Но эти сочетания могут различаться в зависимости от локализации браузера.

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

API редактора


Возможно, вы захотите сделать панель управления, что бы позволить пользователю использовать команды редактирования. Это можно сделать с использованием API редактора. Это API не похоже на обычное API DOM, на самом деле это скриптовая адаптация интерфейса IOleCommandTarget, который является COM интерфейсом используемым Microsoft для синхронизации панели управления и редактирования документа.

API находится в объекте document и состоит из метода execCommand и нескольких методов с префиксом «query» которые возвращает информацию о команде.

Все методы принимают ID команды в качестве первого аргумента, это строка, содержащая имя команды. Собственно, методы:

ExecCommand


Выполняет команду применительно к текущему выделению. Некоторые команды переключаются между состояниями в зависимости от контекста. Например, если вы применяете команду «Bold» к выделению, которое уже имеет полужирное начертание, то выделение будет отображено в нормальном начертании. Некоторые команды тредуют аргументов, например forecolor требует в качестве аргумента код цвета.

Некоторые команды вызывают модальные диалоги — например, команда link вызовет диалог, который предложит ввести URL. Диалоги нельзя изменить, но возможно заменить их. Например:
  1. result = document.execCommand(command, useDialog, value)
Что есть что:
command: Строка; имя команды.
useDialog: Булев тип; Показывать ли встроенный диалог (Не все команды могут вызывать диалоги).
value: Значение, принимаемое командой. Не все команды принимают значения; Если вызывает диалог, то значение береться из него.
result: true если команда выполнена, false если отменена пользователем (закрытием диалога) или команда не может быть выполнена.

Если выделения нет (только курсор), команды форматирования текста применяются по разному, в зависимости от браузера. Если курсор внутри слова, IE применит форматирование к слову; остальные браузеры применят его к следующему символу, который будет набран, если курсор не будет перемещен назад.

QueryCommands

Query-команды имеет смысл использовать для определения состояния кнопок на панели управления в зависимости от текущего выделения и положения курсора.

QueryCommandEnabled

Определяет возможно ли выполнение команды применительно к текущему выделению. Например «unlink» можно применить только в том случае, если курсор находится внутри ссылки. Если выделение содержит область недоступную для редактирования, то никакие команды выполнятся не будут.

QueryCommandState

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

QueryCommandValue

Возвращает значение некой команды для выделения. Значение соответствует тому, которое было задано при использовании execCommand, тоесть, например для ForeColor это будет код цвета (в виде строки) для текущего выделения.

Формат будет отличаться в разных браузерах. Например, ForeColor возвращает шестнадцатеричное значение в IE (такое как #ff0000), а другие вернут RGB выражение, такое как Rgb(255,0,0).

Некоторые значения вариируются в зависимости от локализации браузера, например значение для FormatBlock в IE вернет имя для параграфа на языке локализации интерфейса баузера.

Команды, для которых значений нет, например bold, просто вернут false. (API содержит два дополнительных метода, queryCommandSupported и queryCommandIndeterminate, но они слишком глючные, что бы хоть как то их использовать.)

Range и Selection API


Встроенные команды полезны, но невозможно изменить их поведение или добавить нестандартную реализацию. Используя Range и Selection API вы можете использовать произвольные HTML трансформации, которые можно использовать для реализации нестандартных команд.

Проблема в том, что любые трансформации затрагивающие DOM разрушают стек undo, который используется для реализации команд UnDo/ReDo. Это не слишком хорошо, но может быть приемлемой ценой за нестандартные команды.

Range/selection API имеют два основных класса:

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

Selection — представляет текущее пользовательское выделение в документе. Выделение содержит один подсвеченный диапазон. Если диапазон выделения вырожден, то он отображается как курсор. (Диапазоны и выделения могут быть использованы вне элементов в режиме редактирования. Вы можете создать выделение в документе, доступном только для чтения. Такое выделение, впрочем, не может быть вырожденным, так как курсор показывается только когда элемент в режиме редактирования.)

Эти принципы идентичны во всех браузерах, но сами API отличаются по реализации в IE и всех остальных браузерах. IE использует собственное проприетарное решение для range м selection API, остальные браузеры используют W3C DOM Range API в комбинации с нестанжартизированным selection API.

Основное отличие в том, что в IE содержимое диапазона доступно как строка с HTML разметкой. В W3C DOM Range API, содержимое диапазона доступно как дерево узлов DOM.

Пример диапазона

Что бы показать различные подходы, ниже приведена команда, которая применяет тег «code» к текущему выделению.

В IE (editWindow это ссылка на frame в designMode):
  1. var rng = editWindow.document.selection.createRange();
  2. rng.pasteHTML("" + rng.htmlText + "");
В Mozilla:
  1. var rng = editWindow.getSelection().getRangeAt(0);
  2. rng.surroundContents(document.createElement("code"));

Выделение элементов управления


IE поддерживает выделение элементов управления, которое отличает от обычного выделения диапазона. Выделение элемента управления происходит, когда вы кликаете на объект, такой, как изображение, элемент управления формы или рамку таблицы.

В IE возможно выделение более, чем одного элемента одновременно с использование комбинации ctrl+клик. Остальные браузеры не поддерживают концепцию выделения элементов управления; в остальных браузерах выделение это всегда текстовый диапазон.

Выводы


Статья рассматривает основные принципы редактирования данных в браузере. Часть вторая статьи показывает множество примеров применения вышеописанных API.

От переводчика: если это интересно не только мне, то я переведу и вторую статью.

И пара ссылок на дополнительные материалы:
Tags:браузерыредакторыhtmljavascripthtml-версткаdesignModecontentEditablewysiwyg
Hubs: Website development
+40
10.8k 106
Comments 46