Dynamic HTML - изменение текста (и оформления) страницы после её открытия в браузере. Нужно для отображения выпадающего меню, всплывающих подсказок и многих других удобных изобретений на Javascript, помогающих пользователю.

DHTML

Нечасто задаваемые вопросы

Изменение текста (и оформления) страницы после её открытия в браузере иногда называют DHTML – Dynamic HTML. Справочник по DHTML найти в сети почти невозможно. Гораздо чаще способы обработки HTML страниц в браузере ищут в справочниках JavaScript. Dynamic HTML – более правильное описание вопроса. Потому что JavaScript там совсем немного, и, теоретически, может использоваться любой другой язык. Совсем правильная постановка вопроса: где найти справочник по манипуляциям с DOM – Document Object Model? Такой справочник (достаточно полный, комплексный) нам известен только один – «Справочник веб-разработчика», его автор – Юрий Лукач. Есть, конечно, известные спецификации W3C, но не всегда по-русски и с массой лишней (для практического использования) информации.

Даже основной вопрос – в каком справочнике это искать (к чёрту подробности! в каком я городе?) – сформулировать оказалось не так просто. Дальше – ещё хуже. Вопрос, заданный по-человечески (например, «как удалить прямо на странице лишние буквы из добавляемого пользователем текста?»), надо перевести в «машинные коды» спецификации (или в понятия справочника – который, в сущности, является адаптированной спецификацией).

На это, пожалуй, уходит львиная доля времени начинающего (впрочем, и продолжающего тоже) веб-разработчика – на «перевод» вопросов с естественного языка на язык спецификаций. Попробуем пособирать эти вопросы и дать на них ответы в виде строчек HTML-кода.

Вопросы веб-разработки: DOM

1. Как удалить лишние буквы из поля HTML-формы?

Код элемента:

<input name="address" onkeyup="if (this.value.length>3) 
 this.value=this.value.substr(0,3)">

2. Как сделать, чтобы картинка всё время то появлялась, то исчезала (а лучше чтоб ещё и меняла размер), и чтобы текст выезжал слева и плавно двигался вправо?

Никак. Во всяком случае, не в этой жизни.

3. Для чего вообще надо изменять открытую HTML-страницу?

  1. Чтобы улучшить восприятие страницы и сделать работу с ней более удобной.
    • Скрыть-отобразить часть текста.
      • Выпадающее меню.
      • Скрыть-показать примечания (сноски).
      • Свернуть-развернуть ряд произвольных элементов.
      • Поиск по списку (отбор элементов).
    • Изменить оформление при наведении мыши.
      • Изменение формы курсора.
      • Изменение цвета активных элементов.
      • Изменение цвета строк таблицы.
      • Всплывающие подсказки.
    • Проверка вводимых пользователем данных до отправки на сервер.
    • Хранение настроек пользователя.
    • Невидимая (ненавязчивая) защита от спама.
    • Визуальный (wysiwyg) редактор HTML.
    • Уменьшение объёма страницы и трафика (дублирование элементов).
    • Редактирование большой таблицы базы данных.
  2. Для оптимизации взаимодействия веб-сервера с клиентом.
    • Фоновые http-запросы с помощью динамического элемента script (или так называемого Ajax'а).
    • Исправление ошибок или небрежностей серверных скриптов (движка).
  3. Для создания инструментов разработки.
    • Отображение объектов DOM.
    • Отображение скорости работы браузера.
    • Редактирование большой таблицы базы данных.

4. Ну, и как скрыть-отобразить выпадающее меню?

  1. Для начала нарисовать схему:

    div class="menutop"

    Пункт верхнего уровня

    div class="menuhid"

    Скрытый пункт 1

    Скрытый пункт 2

    Скрытый пункт 3

    ...

    menutop – главный контейнер столбца меню, в который помещается видимый всегда пункт верхнего уровня и список невидимых при открытии страницы подпунктов. Невидимые пункты помещаются в скрытый контейнер menuhid.

  2. Потом сделать контейнер menuhid невидимым с помощью правила CSS div.menuhid {display:none;} (правило должно быть внутри элемента style или в файле *.css, связанном с данной страницей через элемент link rel='stylesheet').

  3. Потом добавить элементу menutop атрибуты onmouseover='menushow(this,1)' и onmouseout='menushow(this,0)'. Функция menushow будет запускаться при появлении мыши над элементом menutop и должна будет делать видимыми все пункты текущего столбца меню.

  4. Ниже (или выше, или в отдельном файле js) меню нужно поместить внутри элемента script функцию menushow:

    <script type="text/javascript">
    function menushow(obj,show) {
     var ch=obj.childNodes
     var display=(show)?"block":"none"
     for (var d in ch) {
      if (1==ch[d].nodeType && "menuhid"==ch[d].className)
       ch[d].style.display=display
     }
     return true
    }
    </script>
    
  5. div class="menutop"

    Пункт верхнего уровня

    Вот примерно так всё и будет выглядеть (кроме форматирования, конечно, которое вы должны сделать по-своему). Мелкие детали: элементу menuhid обязательно нужно добавить правило CSS position:absolute – чтобы он, становясь видимым, не отпихивал бы нижележащий текст, а появлялся поверх него. Элементу menutop нужно добавить правило CSS position:relative (а то menuhid при абсолютном позиционировании может вылезти чёрт знает где).

    Ну, ещё элементу menuhid надо добавить атрибут onclick='menushow(this.parentNode,0)', а то при уходе на другую страницу выпадающее меню останется висеть, и при возвращении, например, по стрелке Alt+Left, страница появится обратно с висящим меню, что, наверное, не очень правильно.

    Несколько главных пунктов меню можно поместить в соседние ячейки обычной таблицы (так сделано в примере).

5. Как и где отображать примечания в книге (статье)?

Благодаря HTML теперь нет необходимости собирать все примечания в кучу в конце книги (или внизу каждой страницы). Примечания можно (и нужно) отображать при щелчке по ссылке прямо в том месте, где стоит ссылка (сразу за основным текстом), а скрывать текст примечания тоже можно при щелчке прямо по этому тексту. Подробно всё это описано в Учебнике HTML. Не идеально описано, так как писалось давно, но решение рабочее.

Принципиальная схема отображения примечаний проще, чем отображение скрытых пунктов меню, так как там не нужны правила position:absolute, и текст примечания должен отображаться по щелчку (а если бы по событию onmouseover, как в меню, тогда пришлось бы вкладывать элемент примечания в элемент основного текста, а то при переходе мыши с основного текста к примечанию примечание будет исчезать).

6. Как ограничить размер нескольких элементов с большим количеством текста и по команде пользователя отображать элементы целиком?

Назначить многотекстовым элементам div class="bigtext", задать CSS правило для данного класса элементов div.bigtext {height:150px; overflow:scroll;} – и забы/ить. Пользователи при желании всегда могут увидеть весь текст элемента с помощью прокрутки.

Если предположить, что у пользователей могут возникнуть другие желания (например, видеть текст всех элементов без прокрутки внутри элемента), надо запрограммировать кнопку, которая будет менять отображение элементов bigtext:

<button onclick="togglebigtext(this)">Показать элементы целиком</button>.

Функция, меняющая отображение больших элементов, должна быть примерно такой:

<script type="text/javascript">
function togglebigtext(obj) {
 var val=obj.firstChild.nodeValue
 if ("Показать элементы целиком"==val) {
  height="auto"
  overflow="visible"
 }
 else {
  height="100px"
  overflow="scroll"
  val="Свернуть большие абзацы"
 }
 var divs=document.getElementsByTagName("DIV")
 for (var i in divs) {
  div=divs[i]
  if (1==div.nodeType && "bigtext"==div.className) {
   div.style.height=height
   div.style.overflow=overflow
  }
 }
}
</script>

Проверьте, как это работает: . Можно вместо button использовать input type="button", тогда проще будет менять его свойство value. А ещё лучше использовать input type="checkbox".

7. Как вести поиск на странице с помощью JavaScript?

  1. Можно как браузер: получить текст всей страницы (например, с помощью свойства body innerHTML) и затем с помощью метода replace заменить во всём тексте фрагменты искомого текста на фрагменты с выделением – что-то вроде <span style="color:red;">фрагмент</span>. Но зачем делать «как браузер», если это и так может делать (и быстрее) сам браузер?

  2. Полезнее организовать поиск в стиле фильтрации – когда на странице остаются только те элементы текста, в которых есть искомый фрагмент. В этом случае возникает вопрос размера «элементов текста» – что это должно быть: слова, предложения, абзацы? К странице с неупорядоченным («естественным») текстом фильтрация практически неприменима (во всяком случае, с помощью JavaScript).

  3. Применять поиск-фильтр имеет смысл лишь к более-менее упорядоченным спискам, и там он может быть очень эффективен. Списки есть на многих веб-сайтах: это списки городов, предприятий, товаров, разделов, людей... Пример работающей фильтрации списка рубрик есть, например, на странице irkutsk.ir2.ru/listrubr.htm.

    • Принципиальная схема: при вводе букв в окно поиска по событию onkeyup запускается функция pickup(obj) (файл listrubr.js). Функция просматривает весь список ссылок страницы, и, если в текст ссылки входит искомый фрагмент, ссылка делается видимой, если нет – невидимой (display:none).

7. Зачем изменять оформление элементов при наведении мыши?

Чтобы пользователь видел, что какие-то из этих элементов активные – при щелчке по ним будет что-то меняться. Самый главный из таких элементов – ссылка. Курсор меняет свою форму над ссылками сам (точнее, это делает браузер), ничего программировать специально не надо.

Поменять цвет ссылки, над которой мышь, можно без JavaScript, с помощью правила CSS: a:hover {color:red;}. Теоретически так можно поменять цвет любого элемента. Но практически – Интернет Эксплорер саботирует спцификации HTML-CSS, и в нём такое решение работает только для ссылок.

8. Как изменять оформление элементов при наведении мыши?

Используя события элемента onmouseover и onmouseout – при наступлении второго события надо возвращать элементу первоначальный цвет (или фон):

Пример onmouseover

Код элемента:

<div style="color:#060; border:1px solid #999;" 
 onmouseover="this.style.color='red'; 
 this.style.backgroundColor='#fcc'; 
 this.style.cursor='default'" 
 onmouseout="this.style.color='#060'; this.style.backgroundColor='';">
 Пример onmouseover
</div>

9. Как делать всплывающие подсказки?

Стандартное средство – атрибут title внутри элемента:

Пример title

Код элемента:

<div title="Достаточно длинный текст...">Пример title</div>

Браузеры отображают перенос строк в атрибуте title по-разному, но ещё хуже то, что они сами устанавливают время отображения title – и пользователь может просто не успеть прочитать, что там написано. Поэтому более надёжно, конечно, использовать атрибут onmouseover и отображать с помощью функции JavaScript заранее приготовленный элемент со всплывающей подсказкой какой угодно формы:

заранее приготовленный элемент со всплывающей подсказкой
Пример всплывающей подсказки onmouseover

Код элемента:

<div onmouseover="this.firstChild.style.display='block'" 
onmouseout="this.firstChild.style.display='none'"
style="position:relative;"><div 
style="display:none; position:absolute;">
 заранее приготовленный элемент со всплывающей подсказкой
 </div>
Пример всплывающей подсказки onmouseover</div>

Обратите особое внимание на фрагмент кода relative;"><div: между открывающим тэгом внешнего элемента и открывающим тэгом вложенной в него подсказки не должно быть ничего (ни пробела, ни разрыва строки – именно ничего!). Иначе приведённый код не будет работать, так как firstChild для внешнего элемента будет уже не заготовленная подсказка, а текст (textNode) – тот самый разрыв строки или пробел.

Ну, и при абсолютном позиционировании элемента внешний для него элемент (родительский) должен иметь позиционирование как минимум relative (то есть обязательно хоть какое-то особое, не static). Иначе позиционируемый элемент может унести неизвестно куда.

Проверку вводимых пользователем данных и некоторые другие вопросы применения DHTML мы рассмотрим в следующей статье.

© 2009, «Деловая неделя», Михаил Гутентог

Комментарии

лесник 06.07.2010 02:05:08

Кирилл,

теперь этого я даже и знать не хочу – как вернуть первоначальный цвет с помощью конструкций типа el.style.backgroundColor=''. В этой части статья устарела, точнее, изначально опиралась на неверную технологию.

Лучше делать изменения оформления с помощью конструкций типа el.className='red'. А, поскольку в класснэйм уже может что-то находиться изначально, я написал специальные функции addclass и delclass (есть в библиотеке http://ir2.ru/static/ir2.js).

Т.е. разумная логика для раскраски элементов по событиям примерно такая: 1) создаёте в наборе CSS класс – например .onmouseo {color:red;}; 2) при событии, например, onmouseover, добавляете этот класснейм вызвавшему элементу: addclass(el, 'onmouseo'); 3) при противоположном событии удаляете класснейм: delclass(el, 'onmouseo').

Методика используется на этом сайте, например, в табличном редакторе tableedit2.aspx.

Кирилл 06.07.2010 00:47:52

А как,все-таки, узнать первоначальный цвет кнопки, чтобы задать его для onmouseout?