CSS-свойство «невидимость», которое можно было бы свободно добавлять и убирать, у HTML элементов не существует. Его нужно сэмулировать, в этом и состоит правильная постановка вопроса show-hide toggle.

Скрыть – показать HTML элементы с помощью Javascript

Специалист – человек, который, избегая мелких ошибок, неуклонно движется к глобальному заблуждению.

Закон программирования Мэрфи, ст. 17

Специалист подобен флюсу: полнота его одностороння.

Козьма Прутков. Мысли и афоризмы: мысль 101

На веб-страницах особенно заметны недостатки работы узких специалистов. Создание сайта как игра в шахматы: нельзя думать только о пешках королевского фланга. Или как исполнение симфонии: нельзя дирижировать только контрабасами, забыв о скрипках и флейтах.

Статья http://javascript.ru/ui/show-hide-toggle – замечательный образец аналитической работы вдумчивого (я бы даже сказал, въедливого) программиста. В статье подробно разбираются достоинства и недостатки разных способов скрытия-отображения элементов на веб-странице. Логика анализа практически безупречна. Тем не менее выводы неверны. Потому что логика-то правильная, но исходные посылки неверные.

Основная проблема, разбираемая в статье, возникает при попытке сокрытия элемента путём назначения ему свойства:

Точнее, проблема возникает при возвращении элементу первоначального вида: мы не знаем, какой у него изначально был style.display, и, чтобы узнать это, вынуждены использовать хренову тучу кода, включая создание «виртуальных» DOM-объектов. И даже это (показывает автор) не решает проблему на 100%!

Назначая HTML элементу style.display = "none", мы изменяем существующее свойство, а потом не знаем, как вернуть его значение обратно. Это плохо и тупиково, и у хорошего дирижёра должен возникнуть вопрос... – а вот это как раз самое трудное – сформулировать! «Можно ли сделать элемент невидимым, не меняя существующее свойство style.display?» – г. вопрос, в вопросе должна содержаться половина ответа!

Правильный вопрос такой: как сделать «невидимость» отдельным свойством, которое можно добавлять-убирать, не трогая других свойств элемента. Не «можно ли», а именно «как», потому что при настойчивом поиске можно решить любую проблему.

Разумеется, никакого такого свойства «невидимость» у HTML элементов не существует. Существует именно display (ну, или visibility). Отдельно добавляемое свойство «невидимость» нужно сэмулировать, в этом и состоит правильная постановка вопроса. Мы добавляем к элементу что-то, какой-то объект, и элемент меняется в строго заданном направлении. Потом просто убираем добавленный объект, и с ним исчезают внесённые изменения.

Вообще то, что можно добавить к HTML элементу или убрать от него, существует в виде атрибутов. Атрибутов, которые влияют на отображение элемента, не очень много – всего два: style и class. Первый, как видим, менять плохо (трудно вернуть обратно). Остаётся class (при программном доступе className). О чём и напоминает в комментариях к статье show-hide-toggle участник обсуждения Kolyaj, предлагая для скрытия элементов конструкцию:

Такое решение предполагает, что у атрибута class может быть несколько значений сразу, разделённых пробелами. И это действительно так (о чём я, например, узнал с большим удивлением – плохо учил в своё время W3C!). Из сложной строки (несколько слов) удалять значение придётся с помощью регулярных выражений. Неплохо было бы также, чтобы зря не дёргать лишний раз DOM, проверять наличие удаляемого значения у атрибута class. Вот как выглядит вся триада класснэйм-функций на стр. openjs.com:

Более-менее похожие наборы существуют во многих фреймворках. С некоторыми вариациями. Например, в библиотеке http://github.com/Kolyaj/CrossJS, функция добавления CSS класса реализована без проверки на наличие добавляемого слова:

Что вполне может привести к появлению у атрибута значения " element_hide element_hide element_hide" (не фатально, но как-то... неэстетично). Дело в том, что show-hide не всегда бывает в прямом смысле переключателем (toggle). Например, я часто использую скрытие всплывающих объектов по нажатию клавиши Esc, и не могу исключить ситуацию, когда какой-либо энергичный пользователь захочет нажать Esc несколько раз подряд.

В своей библиотеке ir2.js я до сих пор использовал только две функции (добавления-удаления класса) с одинаковым регулярным выражением (для проверки-замены) в каждой. Мне понравилась идея вынести проверку в отдельную функцию. Но регэкспы (в класс-манипулирующих функциях на openjs.com) всё равно повторяются дважды! К тому же регэксп составлен так, что при переключениях туда-сюда в className будут накапливаться лишние пробелы. Ну, раз уж взялся оптимизировать, надо идти до конца. В результате некоторых мозговых усилий получилось следующее:

Скрыть-показать несколько элементов сразу

Неверно было бы утверждать, что известные JavaScript программисты плохо знают CSS – они ведь им манипулируют; JavaScript ведь для того и предназначен, чтобы менять объекты DOM, в том числе, с помощью таблиц стилей (их невозможно менять, плохо зная). Просто подход к HTML странице у хороших специалистов неизбежно становится односторонним (как флюс): если без конца оптимизировать JavaScript, в конце концов это становится самоцелью. Деятельность определяет сознание (А. Н. Леонтьев). Любитель (из ложной скромности не будем показывать пальцем) отличается от профессионала тем, что знает обо всём понемногу, и не теряет общей картины.

Правильное взаимодействие DOM и CSS с JavaScript способно дать выигрыш в разы в размере кода и скорости исполнения. В случае с переключателем типа «Скрыть – Отобразить» это особенно заметно, если менять надо сразу несколько элементов. Пример: на странице http://javascript.ru/manual есть кнопки «Кратко» и «Подробно», при нажатии на которые меняется список объектов (к названиям добавляются и убираются описания). В списке штук 15 пунктов. Не знаю, как там выглядит JavaScript-код (боюсь даже заглядывать туда), но, судя по появлению (в подробном представлении) у каждого пункта атрибута style="display: block;", используется цикл перебора всех элементов списка (получаемого чем-то вроде list = obj.getElementsBy...).

В данном случае описанная выше «правильная» система (менять class вместо style) сама по себе даёт малый выигрыш – всё равно нужен цикл. Однако обойтись без цикла вполне возможно. Забавно, что даже в своём стареньком, 7-летней давности учебнике, я уже настойчиво искал (и нашёл!) решение этой проблемы. Не самое лучшее, но во многих случаях всё равно лучше цикла: у страницы просто менялся один из внешних файлов стилей. Просто и грубо:

Понятно, что при первом запуске это должно будет тормозить (пока оба файла стилей не закэшируются). Ну, да речь не о том. А о хорошем, идеальном решении (которое таки существует). Оно основано на том, что в CSS можно назначать селекторам правила не напрямую, а через родительский элемент. Что-то вроде:

То есть добавить (addclass()) ячейке класснэйм «nice» – и текст во всех вложенных элементах p «разъедется»; убрать – сожмётся. Примерно такая фигня используется на нашей доске объявлений http://irkutsk.ir2.ru/: по умолчанию там все объявления имеют фиксированную высоту и visibility: scroll, а при отмеченном флажке «Развернуть все объявления» – наоборот; что делается простой заменой класса у родительского элемента:

и двумя строчками CSS:

Электронные документы и фильтрация данных

При больших размерах HTML-страницы правильная стратегия скрытия-отображения элементов позволяет делать очень интересные вещи. Например, выбирать данные из строк таблицы, фильтруя их по заданному значению в определённом столбце. То есть выбирать-то данные всё равно надо перебором строк в цикле, но при отсутствии результата надо как-то отобразить обратно сразу все строки. Это делается с помощью одного правила CSS (.disnone_child .disnone_if {display:none;}) и одной строки JavaScript, удаляющей у элемента TBODY className .disnone_child (tabsort2.js).

Другой пример связан с проектом «Справочник предпринимателя “Инфодиск”». Мы с коллегами решили, что неплохо бы добавить в справочник немного полезных законов (чтоб были у предпринимателя всегда под рукой). И возник спор, в каком именно виде красть из сети общедоступные российские законы: то ли открывать каждую главу на отдельной странице, то ли каждую статью, то ли сделать одну страницу на весь закон, а вверху страницы оглавление со ссылками. Подумав, решили всё пихать в одну страницу (справочник-то локальный, на компакт-диске, а не в Интернете). Но метаться по странице между оглавлением вверху и статьями показалось как-то стрёмно.

Тогда и вспомнили про такую вещь, как электронный документ, которая бывает в Ворде и Экселе: с помощью плюсиков и минусиков скрываются-отображаются уровни документа – главы, статьи, параграфы... С помощью изменения классов родительских элементов делать это оказалось очень просто. Главное условие – документ должен быть чётко структурирован (например, главы – элемент H2, cтатьи – H3, параграфы – H4...). Тогда управлять отображением всего этого сможет скрипт в 50 строк: http://infodisk.info/kodex.js. Рабочий пример – http://infodisk.info/konstitut.htm. Ещё более чудовищный (осторожно: размер 2 Мб!) – http://infodisk.info/gkodex.htm. Вряд ли такой страницей можно эффективно управлять с помощью циклов JS.

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

Комментарии

Тимофей 07.04.2012 02:29:53

Скажите пожалуста, а как сделать как у вас на сайте, "добавить комментарии"..? Я у вас посмотрел код, но мне это ничего не дало( Спасибо.

лесник 18.04.2012 03:55:41

Не понял, что именно надо. Код php? Могу выслать на указанную вами в комментарии почту, если подтвердите, что это ещё актуально. Но код достаточно примитивный, там хорошему не научитесь.