Управление всем UI происходит, как правило, работой с элементами DOM.

DOM (Document Object Model) — это дерево всех элементов, из которых состоит отображаемый HTML. В первом приближении можно считать, что один DOM-элемент соответствует одному теги исходного текста HTML, впрочем, из этого правила есть исключения (см. Примечание 1).

Изменение DOM (состава, внешнего вида, состояния) происходит только следующими способами (весь API для этого описан в заголовочном файле htmlayout_dom.h, тонкая C++-обертка “для примера” — htmlayout_dom.hpp):

  1. Навигация по DOM — используется для того, чтобы найти элемент, который собираешься менять.
  2. Добавление/удаление DOM элементов — используется, собственно, для изменения элементов UI.
  3. Изменение HTML элементов DOM — альтернатива модификации DOM путем удаления/вставки элементов
  4. Изменение аттрибутов элементов DOM — используется для изменения представления элементов
  5. Изменение состояния элементов DOM — используется для того, для чего и можно предположить по названию
  6. Изменение "значения" (value) элементов DOM — быстрый способ изменения значимой части состояния элемента

Далее подробнее.

Навигация по DOM

Методы навигации:

  • прямой перебор всех “детей” данной вершины: HTMLayoutGetChildrenCount, HTMLayoutGetNthChild
  • прямой переход к родителю: HTMLayoutGetParentElement
  • некоторые специальные функции: HTMLayoutFindElement (элемент в данной точке [x, y]), HTMLayoutGetFocusElement (текущий элемент в фокусе)
  • поиск элемента по css-селектору — достаточно сложные функции, которые принимают на вход параметры для поиска и callback-функцию, которую надо вызвать для найденных элементов. Таких функций 3: HTMLayoutSelectElements (ищет вниз по DOM от заданного элемента), HTMLayoutSelectParent (ищет вверх по DOM), HTMLayoutVisitElements (ищет не по css-селектору, а по именам тагов и аттрибутов, которые могут содержать wildcards).

Добавление/удаление элементов

Реализуется, соответственно, функциями: HTMLayoutInsertElement, HTMLayoutDetachElement.

Полезно знать:

  • элемент, удаленный из DOM, можно использовать и дальше (например, вставить в другое место DOM), пока он актуален. Актуальность контролируется счетчиком ссылок элемента (Примечание 2).
  • элемент вставляемый в DOM при помощи HTMLayoutInsertElement, может быть:
    1. совершенно новым элементом, созданным с помощью HTMLayoutCreateElement
    2. элементом, ранее вытащенным из DOM с помощью HTMLayoutDetachElement
    3. клоном существующего элемента, созданным с помощью HTMLayoutCloneElement.
  • чтобы отсортировать элементы, лежащие в контейнере (например, строки в таблице), не нужно их все по очереди удалять из DOM и опять вставлять: для этого существует специальная функция HTMLayoutSortElements
  • чтобы поменять местами два элемента, есть функция HTMLayoutSwapElements
  • если нужно сделать элемент “временно невидимым”, а потом опять показать (например, как реакцию на кнопку “Скрыть/Показать подробности”), то лучше делать это не удалением/вставкой, а с помощью изменения стилей. Например, установить атрибут class в hidden, а в CSS прописать:

.hidden{display:none;}

  • некоторые изменения DOM вызывают side effects (вставку дополнительных, “синтетических” элементов). Например, если в таблицу с 5 колонками вставить tr, в котором всего 4 ячейки, в него добавится дополнительная, пятая синтетическая ячейка (о том, что такое “синтетический”, см. Примечание 3).
  • находится ли данный элемент в DOM, и если да, то какого окна, можно узнать функцией HTMLayoutGetElementHwnd.
  • порядковый номер данного элемента внутри его родительского элемнта можно узнать функцией HTMLayoutGetElementIndex.

Изменение HTML элементов DOM

Выполняется, естественно, функциями HTMLayoutGetElementHtml/HTMLayoutSetElementHtml.

Важно понимать, что не все прямые изменения HTML одинаково полезны (и безопасны). Например, попытка установить для элемента <tr> какой-нибудь другой html, кроме набора <td>, может привести к совершенно непредсказуемым последствиям.

Лично я предпочитаю использовать HTMLayoutSetElementHtml только в простейших случаях, когда нужно, скажем, в ячейке таблицы написать “немножко <b>жирного</b> текста”, а все более глобальные изменения UI выполнять методами, описанными в разделе Добавление/удаление элементов.

Изменение текста элементов DOM

Чтобы узнать текст, используются функции HTMLayoutGetElementText, HTMLayoutGetElementInnerText — первая вернет текст с “дырками” на месте дочерних элементов, вторая — полный текст. (например, для элемента ”<p>some text with <b>bold</b> part</p>” первая вернет “some text with \0 part”, вторая — “some text with bold part”).

Чтобы изменить текст, используется функция HTMLayoutSetElementInnerText. Внимание! Все дочерние элементы будут удалены. (То есть вызывать эту функцию для <table> — не очень разумная идея).

Эти функции (как и большинство других функций HTMLayout) работают с текстом в кодировке UTF-8.

Существуют также UTF-16 варианты: HTMLayoutGetElementInnerText16, HTMLayoutSetElementInnerText16.

Изменение аттрибутов элементов DOM

Перебор всех аттрибутов: HTMLayoutGetAttributeCount, HTMLayoutGetNthAttribute Запрос/установка значения аттрибута по имени: HTMLayoutGetAttributeByName, HTMLayoutSetAttributeByName.

Сброс всех аттрибутов: HTMLayoutClearAttributes.

Основные цели, ради которых используются аттрибуты:

  • установка личных настроек стиля отдельным элементам (например, установить элементу class=important, а в таблице стилей прописать, как должны отображаться элементы этого класса);
  • сохранить связанные с элементом данные для последующего считывания (например, установить элементу id=”{идентификатор объекта в БД}”, а потом при обновлении интерфейса, перечитать объект по идентификатору)
  • отметить элемент для последующего поиска (например, установить одному элементу в списке аттрибут toDelete=true, а по нажатию какой-нибудь кнопки найти все элементы, у которых установлен такой аттрибут, и удалить их).

Имена аттрибутов можно использовать любые, соответствующие стандарту (ссылка?), но обратить внимание, что есть некоторые аттрибуты, которые обрабатывает сам HTMLayout, и их изменение вызовет side effects. Примеры:

  • аттрибут src тега img (изменит отображаемую картинку)
  • аттрибут fixedrows тега table (изменит количество “непрокручиваемых” строк таблицы)
  • аттрибут type тега input (изменит тип элемента ввода)
  • Внимание! Этот список неполон!

Изменение состояния элементов DOM

HTMLayoutGetElementState/HTMLayoutSetElementState

Используется для запроса и установки состояний элементов, которые изменяются под действиями пользователя.

Например:

  • выбранный чекбокс имеет состояние STATE_CHECKED
  • элемент в фокусе имеет состояние STATE_FOCUS
  • элемент, над которым находится мышка, имеет состояние STATE_HOVER

Вообще, все возможные состояния перечислены в enum ELEMENT_STATE_BITS в файле htmlayout_dom.h. Там же — подробные комментарии, какое состояние что означает.

Очевидно, что элемент может иметь несколько состояний одновременно (допустим, FOCUS | HOVER | CHANGED).

Полезные применения состояний:

  • просто запросить (мышка над этим элементом?)
  • установить (скажем, состояние VISITED для ссылки)
  • использовать в CSS-селекторах (практически каждому STATE_XXXX соответствует CSS-селектор :xxxx) — таким образом можно, например, установить особый стиль для элемента, над которым мышь; или для нажатой кнопки (STATE_ACTIVE, :active) или для отключенного (STATE_DISABLED, :disabled)

Вообще, работа с состояниями идет согласно с человеческой логикой: если нужно изменить состояние некоего элемента (например, свернутый/развернутый для вершины дерева), нужно в первую очередь поискать соответствующую константу STATE (STATE_COLLAPSED/STATE_EXPANDED).

Изменение "значения" (value) элементов DOM

HTMLayoutControlGetValue/HTMLayoutControlSetValue — удобные “шорткаты” для работы с элементами ввода, позволяет однообразно запросить/установить значение для разных элементов. Все, что делается этими функциями, может быть сделано и без них (но несколько менее удобно).

Например:

  • для поля ввода текста, value — это и будет вводимый текст; его также можно запросить/установить с помощью GetText/SetText
  • для чекбокса, value — булевое значение “выбран/не выбран”; его также можно запросить/установить с помощью GetState/SetState (STATE_CHECKED)
  • для списка, value — это выбранная option; чтобы узнать ее “руками”, надо найти option со state = STATE_CHECKED

и т.п.

Дополнения

  • в htmlayout_dom.h определено еще несколько полезных функций. Часть из них я опишу в следующих главах — работа с behaviors и events, работа с popup-ами; другую часть (HTMLayoutScrollToView и т.п.) оставляю для самостоятельного изучения.
  • все функции htmlayout_dom.h возвращают HLDOM_RESULT; возможные его значения можно посмотреть все в том же файле. Кодом успеха являются HLDOM_OK, HLDOM_OK_NOT_HANDLED, все остальные — коды ошибки (операция не удалась). Впрочем, из этого правила есть исключения: например, функция HTMLayoutGetElementHwnd на “детачнутом” (отцепленном от документа) элементе вернет HLDOM_INVALID_HWND, что можно считать не ошибкой, а просто символом “неприаттаченности элемента”.
  • большинство изменений DOM и отдельных его элементов будут невидимы до обновления DOM - которое произойдет либо по пересчету всего окна (например, изменение его размеров), либо может быть вызвано насильственно, функцией HTMLayoutUpdateElement. Нужно иметь в виду, что выполняемые этой функцией действия могут быть относительно длительными (поскольку происходит пересчет размеров и положений всех элементов окна, которые могут друг от друга зависеть), и вызывать ее с осторожностью (то есть, если вы собираете в DOM таблицу из сотни строк, функцию лучше вызвать один раз, когда вся таблица будет собрана).

Примечания

Примечание 1

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

<select>
  <option>1</option>
  <option>2</option>
  <option>3</option>
</select>

(выпадающий список с 3-мя элементами) “на самом деле” преобразуется вот в такой:

<select>
  <caption></caption>
  <popup type="select">
    <option>1</option>
    <option>2</option>
    <option>3</option>
  </popup>
</select>

В некоторых случаях этот нюанс оказывается важным.

Примечание 2

Элементы DOM HTMLayout — это объекты со счетчиками ссылок, они удаляются, когда счетчик ссылок равен 0. Пользователь может контролировать счетчик ссылок функциями HTMLayout_UseElement, HTMLayout_UnuseElement. Пока элемент находится в DOM, счетчик ссылок у него > 0. Если пользователь удалил элемент из DOM (HTMLayoutDetachElement), то его можно продолжать использовать дальше, пока счетчик ссылок > 0.

Внимание, важный нюанс! Если элемент, который удаляется из DOM, еще нужен (вы собираетесь его куда-то вставить), то правильная последовательность действий такая:

  1. увеличить счетчик ссылок объекта на 1 (HTMLayout_UseElement)
  2. удалить элемент из DOM (HTMLayoutDetachElement) - на этом этапе счетчик уменьшится на единицу (дереву объект уже “не нужен”), и если бы мы не выполнили шаг 1, счетчик ссылок здесь стал бы равен 0, и объект был бы собран сборщиком мусора
  3. вставить объект, куда собирались (HTMLayoutInsertElement) - счетчик опять увеличится
  4. сделать HTMLayout_UnuseElement.

Чтобы во всем этом не запутаться, существенно предпочтительнее использовать какую-нибудь обертку вокруг HELEMENT с автоматическим подсчетом ссылок. Для C++ такая обертка входит в поставку HTMLayout и находится в файле htmlayout_dom.hpp

Примечание 3

Некоторые элементы DOM требуют специального количества дочерних элементов. Главным образом, это относится к строкам таблиц - в каждой строке таблицы должно быть одинаковое количество ячеек; поэтому, если создать “кривую” таблицу (например, с тремя строками длиной 3, 2, и 4 ячейки), HTMLayout добавляет недостающие ячейки автоматически (то есть, в первую строку будет добавлена одна, а во вторую - 2 ячейки). Касательно этих “авто-добавленных” ячеек следует знать:

  1. “исправление” будет выполнено только после обновления-перерисовки элемента (перерисовки всего окна или вызова HTMLayoutUpdateElement), до тех пор можно заниматься конструированием безбоязненно (если, например, ваш юз-кейс требует создать таблицу с кучей строк разной длины, а затем пробежаться по ней и отрезать лишние ячейки).
  2. добавленные ячейки имеют установленный флаг состояния STATE_SYNTHETIC, по которому их можно отловить для каких-либо целей.
htmlayout/tutorial-dom-r.txt · Last modified: 2008/05/02 15:53 by zverok
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0