Да, если сайт правильный. Только вот зачем это может понадобиться? Очень просто. В начале этой страницы есть список задач, которые можно решить с помощью DHTML. Список при открытии страницы отображается в свёрнутом виде – только три пункта верхнего уровня. Подпункты разворачиваются при наведении мыши на заголовки разделов. Если список вам уже знаком по предыдущим статьям, вам нет необходимости разворачивать его часто. Но если всё-таки вам захочется несколько раз взглянуть на него целиком, постоянная необходимость ёрзать над ним мышкой может поднадоесть. Ну, либо вас просто может раздражать активное поведение списка при случайных движениях мышкой.
Поэтому мы сделали над списком такое окошечко (элемент checkbox), щёлкнув по которому вы а) развернёте весь список, б) запишете состояние списка к себе на компьютер, в ма-аленький файл (?) cookie (куки). Вопрос после слова файл стоит потому, что никто на самом деле не знает, как выглядят куки – файлы это или какие-то записи в базе данных sqlite. И это правильно, потому что нам всё равно, как выглядят куки – главное, что они работают: если вы закроете страницу и вообще окно браузера, а завтра опять откроете, верхний список будет отображаться в том состоянии, в котором вы его оставили в прошлый раз.
И ещё одну кнопочку-флажок до кучи – чтобы вообще убрать список. Всё работает прямо на этой странице. Код javascript, обеспечивающий изменения страницы, находится здесь: dhtml3.js. Для правильного отображения элементов необходимо также правило CSS: #roll ul {display:none;}, означающее, что вложенные в наш список (id=roll) подпункты (элементы UL) изначально на странице не отображаются. Система хранения выбранного пользователем вида страницы работает независимо от сервера: если вы сохраните страницу к себе на компьютер (со всеми сопутствующими файлами, полностью), система останется работоспособной при открытии файла локально.
Принципиальная схема сохранения настроек достаточно проста: 1) при щелчке по активному элементу (у нас это checkbox) в куки записывается «переменная» с неким цифровым значением; 2) при следующем открытии страницы это значение считывается из куки и страница отображается в соответствии с ним.
Код javascript, однако, получился довольно длинным. Но не из-за системы хранения, а из-за вариантов отображения списка. На «интерфейс» всегда уходит много материала. Невинная попытка сделать структуру страницы чуть более понятной легко может вырасти в целую javascript-функцию. Мы споткнулись на LABEL. Этот элемент содержит описание для checkbox'а («флажка»); он удобен, в частности, тем, что для достижения результата щёлкать можно не только по флажку, но и по его описанию. Чтобы лишний раз обратить внимание пользователя на это удобство, мы решили для описаний флажков (label) назначить форму курсора pointer («указатель» в виде руки). Обычно для этого бывает достаточно правила CSS:
label (cursor: pointer;)
Но в данном случае закрались смутные сомненья: ведь ИЕ (в некоторых своих версиях) понимает только слово hand, т.е. по команде pointer он ничего делать не будет. Проверять, в каком браузере открыта страница, – не очень хорошая практика. Идеально было бы проверить, допустимо ли в данном браузере значение pointer для свойства элементов cursor. Но как это сделать? Решение немного ниже.
У нас два флажка, по которым для изменения отображения страницы пользователь может щёлкать или не щёлкать. Таким образом, возникает четыре состояния флажков (и страницы):
Флажок «Убрать» | Флажок «Развернуть» | Состояние списка | Числовой вид |
---|---|---|---|
Свободен | Свободен | Свёрнут и виден | 0 (00) |
Свободен | Отмечен | Развёрнут и виден | 1 (01) |
Отмечен | Свободен | Свёрнут и не виден | 2 (10) |
Отмечен | Отмечен | Развёрнут и не виден | 3 (11) |
Понятно, что когда состояние списка «Не виден», пользователю неважно, свёрнут список или нет. Но зато когда пользователь захочет увидеть список обратно, список вернётся к нему в том состоянии, в котором был спрятан.
Вполне очевидным стало решение присвоить состоянию флажка «Свободен» цифру 0, а состоянию «Отмечен» – 1. Тогда для отображения всего набора состояний можно будет использовать двухбитовое число, левый бит в котором будет обозначать состояние флажка «Убрать», а правый – «Развернуть». Именно эти числа – 0, 1, 2 или 3 и записываются в куки в качестве значения параметра, названного нами rollstatus. Формируется число статуса так:
rollstatus=0; rollstatus+=(rollmove.checked)?2:0; rollstatus+=(rollch.checked)?1:0;
При чтении куки нам нужно обратное действие: по значению статуса определить состояние флажков (и способ отображения списка). Это, наверное, проще всего сделать с помощью побитовых операторов. То есть для того чтобы узнать, была ли добавлена к числу статуса единица (правый бит), нужно побитово сравнить число статуса с единицей. В данном случае результатом сравнения должно быть совпадение, поэтому сравниваем с помощью оператора &: флажок «Развернуть» должен быть отмечен, если истинно значение выражения (1 & rollstatus). А истинно оно будет для следующих чисел:
status | 1 | 1 & status | |
---|---|---|---|
0 | (00) | 01 | false |
1 | (01) | 01 | true |
2 | (10) | 01 | false |
3 | (11) | 01 | true |
Для получения значения левого бита статуса сравниваем его с двойкой: флажок «Убрать» нужно отметить, если истинно значение выражения (2 & rollstatus).
Стараемся как можно меньше нагружать атрибуты HTML-элементов обращением к javascript. Например, присваиваем нашим флажкам функции по событию onclick в тексте файла dhtml3.js, а не в тексте страницы:
var rollch=document.getElementById("rollch"); rollch.onclick=rollswitch;
Список («свиток», элемент roll) развёртывается при движении мышкой тоже по событию onmouseover, присвоенному всему документу в тексте скрипта: document.onmouseover=mover;, а уже функция mover(e) проверяет, над каким элементом мышь, и решает, что после этого делать.
Элементы «свитка» (списка), у которых есть вложенные подпункты, подчёркиваются на экране, как ссылки (и курсор у них не text, а default – стрелка). После наведения на них мыши, подпункты становятся видимыми, и подчёркивание убирается. Всё это тоже делается через внешний javascript (в той же функции mover(e)).
Во имя той же оптимизации мы читаем-записываем куки не гигантской фукнцией (многочисленные варианты которой кишмя кишат в интернете), а «в одну строку» (ну, в крайнем случае, в две):
//прочитать значение куки rollstatus var rollstatus=/rollstatus\s*\=\s*(\d+)/im.exec(document.cookie); rollstatus=rollstatus && rollstatus[1];
//записать значение куки rollstatus document.cookie="rollstatus="+rollstatus+"; expires=" + expires+"; path=/";
Необходимо, правда, сначала вычислить expires. Это делается стандартным способом (тут трудно что-либо улучшить), один раз, при загрузке страницы:
var expires = new Date(); expires.setTime(expires.getTime()+36*24*60*60*1000); expires = expires.toGMTString();
Проверить наличие какого-нибудь свойства DOM для конкретного браузера несложно: нужно спросить, не равно ли это свойство значению false. Например, так:
if (document.all) alert("document.all: OK!"); else alert("document.all: нет такой фигни!");
Но как спросить у браузера, готов ли он выполнить команду cursor: pointer;? Мы не нашли другого способа, кроме как попытаться заставить браузер выполнить эту команду, а потом проверить значение свойства cursor. Такой способ допустим благодаря двум особенностям: 1) при неправильной команде («невалидном» значении) фатальной ошибки не возникает и работа всех подсистем браузера (CSS, javascript) продолжается нормально; 2) при попытке назначить невалидное значение CSS, данное значение, каким бы оно ни было до этого момента, становится пустым. Таким образом, мы смело задаём значение какому-то свойству, а потом просто спрашиваем у браузера, каким стало это значение:
//hack IE-'hand' var ls=document.getElementsByTagName("LABEL"); for (var el in ls) { elem=ls[el]; if (1==elem.nodeType) { /*Так можно проверить, существует ли значение 'pointer'*/ elem.style.cursor="pointer"; if (!elem.style.cursor) elem.style.cursor="hand"; /*Конец проверки значения свойства cursor*/ } } //hack IE-'hand' end