Графические выпадающие меню хороши лишь в том случае, если на сайте относительно мало разделов с несложной внутренней структурой. А как быть, если ваш сайт является чем-то вроде электронной книги со сложной системой разделов, глав и параграфов? И в этом случае вам на помощь при создании удобной навигации может прийти динамический HTML. Попробуйте сделать содержание сайта раскрывающимся по желанию посетителя.
В предыдущем примере мы оперировали свойством CSS visibility, теперь же нам больше подойдет свойство display, которое может принимать значения none и block. На первый взгляд свойства visibility и display ничем не отличаются — и то, и другое отображает или прячет элемент в зависимости от нашего желания. Но это только на первый взгляд. На самом деле у них имеется очень важное отличие. Если мы используем конструкцию visibility: hidden, то элемент не отображается на экране, но место, занимаемое этим элементом, оставляется пустым. Создается впечатление, что браузер не отобразил элемент, но «на всякий случай» ничем не занял его место. Если же мы напишем display: none, то браузер полностью изымет элемент из потока отображения, а на его месте отобразит следующий видимый относительно позиционированный элемент. Таким образом, свойство display как нельзя лучше подходит для создания текстовых меню с расширяемым по желанию пользователя содержимым. Обратите внимание: если мы собираемся динамически изменять свойство display, позиционировать динамические элементы мы должны относительно! Иначе открывающиеся блоки меню будут частично или полностью перекрывать друг друга.
Безусловно, удобнее всего воспользоваться для создания таких меню маркированными списками, содержащими вложенные списки. Каскадные таблицы стилей посредством свойства list-style-image позволяют переопределить стандартные маркеры списков на собственные, хранящиеся в файлах изображений. Создадим три небольших графических файла — для маркера, обозначающего раздел со скрытым содержимым («закрытый» раздел), для маркера раздела с развернутым содержимым («открытый» раздел) и для маркера собственно содержимого раздела. В нашем примере это будут файлы close.gif, open.gif и mark.gif.
Для начала следует создать единые стили для всех элементов основного и вложенного списков, определив при этом файлы маркеров для «открытых» (класс .open) и «закрытых» (класс .close) разделов, для маркеров содержимого, а также цвет ссылок и все остальные параметры, какие захотите. Естественно, для манипулирования свойством display также следует назначить разные классы (в примере — классы .col и .exp). Таблица стилей при этом должна выглядеть приблизительно так:
<style>
...
body {color: #940122; background: white;}
.close {cursor: hand; list-style-image: url(images/close.gif);}
.open {cursor: hand; list-style-image: url(images/open.gif);}
.col {position: relative; display: none;}
.exp {position: relative; display: block; list-style-image: url(images/mark.gif);}
...
</style>
Так как для сложных текстовых меню неудобно использовать обработчик события onmouseover (возможно, наш посетитель хочет, чтобы открытое меню было постоянно у него перед глазами), мы используем обработчик onclick, написав для него более интеллектуальную функцию. Наша функция будет автоматически определять, какой класс присвоен объекту, и заменять его на альтернативный. То есть, если пользователь щелкнет мышкой по заголовку «открытого» списка подразделов, то список закроется, и маркер заголовка сменится на картинку с закрытой папкой. А если, наоборот, клик будет произведен по заголовку «спрятанного» списка — список откроется, одновременно «откроется» и папочка рядом с заголовком. Записывается нужная нам функция следующим образом:
<head> ... <script language="JavaScript"> function showhideObj(obj,expand) { obj.className = obj.className == "col" ? "exp": "col"; expand.className = expand.className == "close" ? "open": "close"; } </script> ... </head>
Сопоставив названия классов с таблицей стилей, вы, наверное, догадались, что объект obj — это вложенный, изначально спрятанный от посетителя список подразделов, а объект expand — это внешний элемент списка с визуально значимым маркером. Главное, не перепутайте последовательность аргументов при обращении к функции в обработчике событий, и не забудьте правильно задать идентификаторы динамическим элементам!
Код самих меню в «теле» страницы будет выглядеть приблизительно так:
<body>
<ul>
<li onclick="javascript:showhideObj(In1,Out1)"
class="close" id="Out1">Первый раздел сайта
<ul id="In1" class="col">
<li>
<a href="sample1.html" title="Подраздел первый">Подраздел первый</a>
</li>
<li>
<a href="sample2.html" title="Подраздел второй">Подраздел второй</a>
</li>
<li>
<a href="sample3.html" title="Подраздел третий">Подраздел третий</a>
</li>
<li>
<a href="sample4.html" title="Подраздел четвертый">Подраздел четвертый</a>
</li>
</ul>
</li>
<li onclick="javascript:showhideObj(In2,Out2)"
class="close" id="Out2">Второй раздел сайта
<ul id="In2" class="col">
<li>
<a href="sample1.html" title="Подраздел первый">Подраздел первый</a>
</li>
<li>
<a href="sample2.html" title="Подраздел второй">Подраздел второй</a>
</li>
<li>
<a href="sample3.html" title="Подраздел третий">Подраздел третий</a>
</li>
<li>
<a href="sample4.html" title="Подраздел четвертый">Подраздел четвертый</a>
</li>
</ul>
</li>
</ul>
</body>
<head> ... <script language="JavaScript" src="my.js"> </script> ... </head>
Содержимое файла my.js должно содержать только код функции (или нескольких функций). Тэги <script> и </script> в этом файле отсутствуют. Если вы на всех страницах сайта используете одновременно несколько сценариев (например, вверху страницы у вас располагаются графические меню из предыдущего примера, а слева — обсуждаемые сейчас текстовые меню), то вы можете включить в один внешний файл скрипта все три используемые функции, при этом лишь нужно следить за тем, чтобы имена функций (showObject, hideObject, showhideObj) различались.