Существенным для правильной организации системы «скрыть-показать» элементы является расстояние между управляющим элементом (назовём его Кнопкой) и отображаемым элементом (назовём его Целью). Расстояние это может быть представлено в трёх вариантах:
Единственно правильная, простая и надёжная схема скрытия-отображения заключается в изменении класса родительского элемента Цели. Если рассмотреть структуру отображения в этом ключе, окажется что существенно разных варианта всего два: важно лишь, имеет ли Кнопка доступ к родительскому элементу Цели. В первом варианте сама Кнопка является этим родителем; во втором варианте у обоих элементов общий родитель; в обоих случаях доступ к родителю есть – по действию пользователя (щелчку мышью) мы просто меняем класс Кнопки либо класс parentNode Кнопки:
В третьем варианте Кнопка расположена чёрт знает как далеко от Цели, и у нас проблемы:
Как на javascript узнать, какая из Целей связана с данной Кнопкой? Самый простой вариант, который приходит в голову, – при генерации HTML страницы создавать идентификаторы, которые присваивать в виде id Целям и в виде какого-нибудь «левого», ненужного атрибута – Кнопкам. Так все обычно и делают. И так делают, даже когда идентификаторы вовсе не нужны.
Мы же поступаем полностью противоположным способом – всячески избегаем «навязчивых» атрибутов, даже когда они, казалось бы, нужны. В нашем случае решение приходит неизбежно, как паровоз по рельсам: если что-то нельзя соединить в DOM естественным образом при генерации на сервере, значит этого вовсе не нужно делать. Если нельзя создать на сервере Кнопки, соединённые с целями, значит Кнопки вовсе не нужно генерировать на Сервере – их нужно генерировать в самом javascript.
Все отображаемые и скрываемые Цели находятся в одном контейнере (назовём его Каталог). После загрузки страницы нам нужно подготовить элементы Каталога к определённым действиям пользователя (например, что-то делать с ними по щелчку мыши или при наведении мыши). Вот во время этой подготовки, когда мы получаем элементы Каталога в цикле, мы и будем генерировать элементы, управляющие отображением (наши Кнопки). И в этот момент нет ничего легче, чем присвоить текущей Кнопке javascript-свойство – ссылку на элемент-Цель:
Пусть, например, у нас управляющий элемент Кнопка (по которому кликаем) и скрытый элемент Цель находятся в одном общем контейнере (элементе Каталога). Тогда (по щелчку) надо менять класс контейнера, а в CSS описать, что должен делать этот изменённый класс. Пусть по умолчанию класс контейнера будет 'hidechild', и мы запишем в правилах CSS, что он должен запрещать отображение элементов с классом 'body':
При загрузке страницы все элементы с классом 'body' не будут видны на странице. По щелчку на управляющем элементе 'head' мы просто будем убирать (или добавлять) этот класс 'hidechild' родительскому контейнеру. Вот вкратце и вся логика. Она немного усложнится, если мы хотим создавать вложенные друг в друга контейнеры. Во всех случаях, для надёжности лучше не просто убирать «скрывающий потомков» класс, а ещё и добавлять «отображающий» класс – это упростит взаимоотношение нашей системы отображения с другими правилами CSS:
В случае, например, вложенности элементов-контейнеров друг в друга, логику отображения нижележащего элемента можно будет реализовать так:
– то есть Цель текущего контейнера может быть скрыта, а вложенного контейнера – показана; их отображение будет зависеть только от класса непосредственного родителя (но не от более далёких предков).
Правильнее здесь было бы сказать, «удобства для параноиков», потому что я и сам параноик – хожу по незнакомым сайтам исключительно с включённым NoScript, то есть с отключенным по умолчанию javascript. В такой ситуации, зайдя на страницу нашего примера, я вообще не смогу увидеть содержимое контейнеров ('body'), а буду видеть лишь кнопки, по которым щёлкать с отключенным javascript бесполезно.
Это неправильно. Ведь скрываем мы часть содержимого для удобства пользователей – чтобы большое количество текста (и рисунков) не загромождало страницу, а пользователи могли бы видеть избранное, то, что нужно. Если же содержимым вообще невозможно управлять (скрывать-отображать по желанию пользователя), логичнее всё-таки отобразить всё, а не скрыть всё. Будет менее удобно, чем с управляющими кнопками, но не фатально (а «скрыть всё» – фатально).
Для обеспечения такой логики (без javascript показывать всё) нужно будет использовать ещё один, вышележащий контейнер. С учётом того, что наши каталоги – не единственное, что на нашем сайте требует javascript, разумно выбрать в этом случае в качестве управляющего контенера самый верхний элемент – body. Логика защиты проста:
Таким образом, если javascript отключён, никто не поменяет класс body на js, и правила display:none; вообще не будут срабатывать (все элементы страницы будут сразу видны при загрузке).
Вот рабочий пример, иллюстрирующий скрытие-отображение элементов, генерацию кнопок (во втором случае) и удобства для параноиков: hidechild.html