
Запоминание состояний элементов с помощью cookie
📅 Опубликовано:18 июля 2011
Это статья из старого первого блога, который сохранился только на archive.org. Все старые статьи помечены тегом 🚨 старое
Нередко возникает потребность в создании элементов, внешний вид которых должен меняться при клике и после перезагрузки страницы «запоминаться». Очень часто подобное поведение можно встретить на сайтах с большим количеством информации, оформленной в виде блоков с элементами управления, при щелчке на которых (или на заголовках блоков), тело блока исчезает и остается только верхняя его часть, сигнализирующая о том, что блок в данный момент свернут.
Создать визуальную анимацию сворачивания-разворачивания особого труда составить не должно, но вот как адекватно запомнить состояние блока, чтобы после перезагрузки страницы свернутый блок остался свернутым, по крайней мере мне, было не совсем ясно.
В этом посте собственно я и буду разбираться с этой задачей, в итоге должно получиться что-то вроде этого.
Задача в следующем. На сайте имеется определенное количество блоков, которые можно, по клику на соответствующем элементе управления в заголовке блока, сворачивать и по повторному клику на нем же — разворачивать.
Таких блоков на страницах может быть разное количество. И состояния их должны запоминаться.
Для начала изобразим весь процесс на клиенте, а потом немного поговорим о том, что должно происходить на бэкэнде.
Структура блока будет как всегда незамысловатой:
HTML
<div id="shb-1" class="show-hide-block">
<div class="block-header">
<h2>Заголовок блока</h2>
<div class="shb-control"></div>
</div>
<div class="block-body">
<p>Содержимое блока.</p>
</div>
</div>
CSS для блока:
CSS
.show-hide-block {
background:#EEF0F2;
border:1px solid #CAC9C9;
margin-bottom:25px;
}
.block-header,
.block-body {padding:10px}
.hide .block-body {display:none;}
.block-header {
background:#D6E4F1;
border-bottom:2px solid #fff;
position:relative;
cursor:pointer;
}
.shb-control {
position:absolute;
top:10px;
right:10px;
background:red;
width:20px;
height:20px;
background:url("images/close-open-icon.gif") left -20px no-repeat;
cursor:pointer;
}
.hide .shb-control {background-position:0 0;}
.block-body p {
padding:0;
margin:0;
}
Займемся анимацией поведения блока.
При клике по блоку с классом .block-header
, содержимое контейнера .block-body
будет либо скрываться, либо появляться в зависимости от того, присутствует у родительского контейнера .show-hide-block
класс .hide
или нет:
JavaScript
$('.block-header').each(function() {
var blockHeader = $(this);
blockHeader.click(function(){
blockHeader.parent().toggleClass('hide');
return false;
});
});
Так как блоков таких неизвестное количество, переберем все блоки-заголовки с помощью метода .each()
и при каждом клике по заголовку с помощью метода .toggleClass()
, обращаясь к родительскому контейнеру посредством .parent()
, будем добавлять или удалять класс hide
.
Анимация готова и работает.
Теперь подумаем про логику запоминания. Ясно, что будем создавать куку, причем всего одну на все состояния блоков. Приведем в соответствие каждый блок и его номер, который запишем айдишником блока. Номер блока будет его позицией в массиве состояний, ну а цифры 0 и 1 будут отвечать за его состояние — блок закрыт либо открыт. При этом весить кука будет совсем немного. Если у нас имеется пять блоков: 5 байт + название куки, в данном случае «blockState»: 10 байт итого: 15 байт.
Для работы с куками воспользуемся jQuery-плагином jquery-cookie
Для начала нужно подумать о записи состояний в куку во время их изменения.
Так как блоков на странице неопределенное количество, создадим функцию параметром которой будет номер блока. Готовый вариант будет выглядеть следующим образом:
JavaScript
function setState (blockName) {
$('#shb-'+blockName).find('.block-header').click(function() {
var blockStateCookie = $.cookie('blockState');
if (blockStateCookie == null) var blockState = [1, 1, 1, 1, 1];
else var blockState = blockStateCookie.split('');
var arrEl = blockName-1;
if ($(this).parent().is('.hide')) blockState [arrEl] = 1;
else blockState [arrEl] = 0;
var blockStateCookie = blockState.join('');
$.cookie('blockState', blockStateCookie, { expires: 10000, path: '/' });
});
};
Итак, рассмотрим все более детально. Вызывать будем ее так: setState (‘1’);
, то есть указывая параметром только лишь порядковый номер. Остальное имя будет конкатенироваться в самой функции.
Берем наш блок и методом .find()
обращаемся к блоку-заголовку, по которому будем кликать.
При клике создадим переменную blockStateCookie
, в которую прочитаем значение из куки, воспользовавшись плагином jquery-cookie .
Если мы загружаем страницу в первый раз и никакой куки еще нет, $.cookie(‘blockState’)
вернет null
. Проверяем переменную и если она равна null
, создаем необходимый нам массив состояний из пяти элементов, так как блоков у нас будет пять: var blockState = [1, 1, 1, 1, 1];
. По-умолчанию блоки будут открыты, поэтому все элементы массива равны единице.
Если же кука уже есть, восстановим массив состояний методом .split
c пустым параметром .split(”)
, так как в куку мы будем записывать только состояния-цифры без разделитилей.
Далее создадим переменную arrEl
, которая будет означать нужный элемент нашего массива, а так как нумерация начинается с нуля, уменьшим номер нашего блока на единицу.
Затем проверяем задан ли класс .hide
родителю текущего элемента if($(this).parent().is(‘.hide’))
. Если у блока есть класс, то следовательно он находится в закрытом состоянии и когда мы кликнем по нему еще раз, класс исчезнет и блок будет в открытом состоянии, поэтому приравниваем соответствующий элемент массива состояний к единице. В противном случае он будет равен нулю.
Здесь анимацию блоков и запись состояний я умышленно разделил, для наглядности. Что дало мне возможность не изменяя функцию записи состояний блоков, немного преобразить поведение блоков. При подобном разделении анимация должна идти после вызова функций. Хотя анимацию преспокойно можно совместить с функцией записи состояний.
Далее совершаем обратную операцию и с помощью метода .join(”)
с пустым параметром, формируем из измененного массива строку и записываем куку, под именем blockState
.
Если куки до этого не было, если мы вызывали функцию для блока с номером 1, то есть с айди #shb-1
и если этот блок будет в свернутом состоянии, то кука будет представлять из себя такой набор: 01111
.
Для более наглядного тестирования я добавил строчку для вставки содержимого куки в заголовок на странице:
JavaScript
$('.cookie-h').text('Кука «blockState» записана и равна: ' + blockStateCookie);
Собственно на этом клиентская часть заканчивается. Потому как если мы напишем обратную функцию раздачи состояний блокам после перезагрузки страницы тоже на клиенте, то при формировании DOM, визуально хоть и на долю секунды, но наши блоки, которые должны быть скрытыми, будут видны на странице.
Хотя клиентскую функцию для тестирования всетаки написать стоит. Она будет выглядеть проще, чем функция записи состояний:
JavaScript
function restoreState (blockName) {
var arrEl = blockName-1,
blockStateCookie = $.cookie('blockState');
if (blockStateCookie != null) {
blockState = blockStateCookie.split('');
if (blockState [arrEl] == 0) {
$('#shb-'+blockName).addClass('hide');
}
}
};
В ней, если кука записана, из строки формируется массив и если соответствующий элемент массива, равный номеру блока уменьшенному на единицу, равен нулю, то есть блок должен быть в закрытом состоянии — этому блоку методом .addClass()
присваивается необходимый класс.
Вызывать функцию будем для каждого блока, в данном случае для блока #shb-1
, так: restoreState (‘1’);
. Готовый вариант можно посмотреть здесь или скачать архив
На самом деле, когда дело касается создания блоков с подобным поведением, оптимальным вариантом будет создание некоторого «гибрида» из клиентской части и серверной. На клиенте пока пользователь не перезагружает страницу, кука перезаписывается столько, сколько раз пользователь сменил состояния у блоков. А затем, когда страница перезагружается, и все куки вместе с HTTP-заголовком передаются на сервер (что делается всегда), просиходит раздача необходимых состояний, похожими методами, но уже на серверном языке программирования с использованием суперглобальных переменных, содержащих значения куки в заголовке запроса.
Хотя, к примеру, в такой известной коммерческой системе управления контентом как «1С-Битрикс», на страницах с их рабочим столом (страница «Моя страница» в сообществе веб-разработчиков), состояния блоков передаются POST-запросом при каждом клике (если кликать не чаще, чем нужно для ожидания ответа сервером) по элементу управления блоком, что не есть хорошо для такого недешевого запроса.