Хранилище DOM: удобное хранение данных (ч.2)
3.2. Локальное хранилище DOM
Локальное хранилище DOM доступно для веб-страниц, загруженных с одного домена и его поддоменов и открытых в разных окнах или вкладках веб-браузера. То есть мы можем загрузить несколько веб-страниц с одного и того же домена и открыть их в разных окнах или вкладках - и все они будут иметь доступ к данным, помещённым в локальное хранилище.
Данные в локальном хранилище хранятся постоянно, даже при закрытом веб-браузере. Этим локальное хранилище отличается от сессионного (см. параграф 3.1).
Локальное хранилище DOM также представляет собой экземпляр объекта HTMLStorage. Этот экземпляр создаётся самим веб-браузером и доступен через свойство localStorage объекта Window.
var oLocalStorage = window.localStorage;
Это выражение помещает локальное хранилище DOM в переменную oLocalStorage.
Работа с локальным хранилищем выполняется точно так же, как и с сессионным хранилищем (см. параграф 3.1).
window.localStorage.name1 = "Вася";
window.localStorage.name2 = "Пупкин";
window.localStorage.age = (27).toString();
. . .
var name1 = window.localStorage.name1;
var name2 = window.localStorage.name2;
var age = parseInt(window.localStorage.age);
);[/code]
В качестве примера давайте возьмём две веб-страницы, описанные в параграфе 3.1, и изменим их код таким образом, чтобы для хранения данных использовалось локальное хранилище. Так, исправленный HTML-код первой веб-страницы приведён ниже.
[code]<!DOCTYPE html>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<TITLE>Локальное хранилище DOM</TITLE>
</HEAD>
<BODY>
<FORM>
Имя: <INPUT TYPE="text" ID="txtName1"><BR>
Фамилия: <INPUT TYPE="text" ID="txtName2"><BR>
<INPUT TYPE="button" ID="btnSave" VALUE="Сохранить">
</FORM>
<SCRIPT>
var oBtnSave = document.getElementById("btnSave");
oBtnSave.addEventListener("click",
function()
{
var oTxtName1 = document.getElementById("txtName1");
var oTxtName2 = document.getElementById("txtName2");
window.localStorage.name1 = oTxtName1.value;
window.localStorage.name2 = oTxtName2.value;
}, false);
</SCRIPT>
</BODY>
</HTML>[/code]
Здесь мы заменили обращения к сессионному хранилищу на обращения к хранилищу локальному. Также мы удалили выражение, выполняющее переход на вторую веб-страницу, поскольку собираемся открыть обе этих страницы в разных вкладках окна браузера.
Сохраним первую Web-страницу в файле 3.html.
А здесь представлен исправленный HTML-код второй веб-страницы.
[code]<!DOCTYPE html>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<TITLE>Локальное хранилище DOM</TITLE>
</HEAD>
<BODY>
<P ID="pOutput"></P>
<SCRIPT>
var oPOutput = document.getElementById("pOutput");
var s = "Здравствуйте, " + window.localStorage.name1 + " " +
window.localStorage.name2 + "!";
oPOutput.innerHTML = s;
</SCRIPT>
</BODY>
</HTML>[/code]
Здесь мы, опять же, заменили обращения к сессионному хранилищу на обращения к хранилищу локальному.
Сохраним вторую веб-страницу в файле 4.html.
Проверим, установлен ли у нас веб-сервер (например, пакет Internet Information Services), установим его, если ещё не сделали этого, и запустим. Скопируем файлы 3.html и 4.html в корневую папку веб-сайта, созданного при установке веб-сервера, и откроем в браузере первую страницу, набрав в его строке адреса http://localhost/3.html. Когда первая веб-страница загрузится, введём в поля ввода свои имя и фамилию и нажмём кнопку Сохранить. После этого откроем в другой вкладке окна браузера вторую веб-страницу, набрав в строке адреса http://localhost/4.html. Когда вторая страница откроется, мы увидим на ней текст приветствия.
Локальное хранилище прекрасно подходит для постоянного хранения данных, ведь находящиеся в нём данные не теряются даже после закрытия веб-браузера. Такими данными могут быть имена и пароли, под которыми выполняюется вход на веб-сайты в разграничением доступа, неотправленные электронные письма и др.
4. Полезные свойства и методы объекта HTMLStorage
А теперь самое время поближе познакомиться с объектом HTMLStorage - с его свойствами и методами, которые могут быть нам полезны. Таковых не очень много.
Свойство length возвращает количество сохранённых в хранилище значений в виде числа. Оно доступно только для чтения.
[code]var valuesCount = window.sessionStorage.length;[/code]
Это выражение помещает в переменную valuesCount количество значений, сохранённых в сессионном хранилище.
Свойство remainingSpace возвращает размер свободного пространства хранилища в байтах. Это свойство также доступно только для чтения.
[code] var freeSpace = window.localStorage.remainingSpace;[/code]
Это выражение помещает в переменную freeSpace размер свободного пространства в локальном хранилище.
[code]if (window.localStorage.remainingSpace >= 20)
window.localStorage.name1 = "Вася";[/code]
А здесь мы проверяем, есть ли в локальном хранилище, по крайней мере, 20 свободных байт, и, если есть, записываем в хранилище новое значение.
Метод setItem сохраняет в хранилище новое значение с указанным именем. Формат его вызова таков:
[code]<хранилище DOM>.setItem(
<имя>,
<значение>
);[/code]
Первым параметром передаётся имя, под которым будет сохранено значение, а вторым параметром - само сохраняемое значение. И <имя>, и <значение> должны быть представлены как строки; если они имеют другой тип, браузер преобразует их в строки.
Если свойство экземпляра объекта HTMLStorage, представляющего хранилище, с указанным нами <именем> ещё не существует, оно будет создано, и ему будет присвоено заданное <значение>. Если же такое свойство уже существует, его значение будет заменено на указанное нами.
Метод setItem не возвращает результата.
Преимущество использования метода setItem перед подходом, описанным в параграфе 3.1, заключается в том, что в случае его использования мы можем задавать для сохраняемых значений любые имена, даже содержащие пробелы. В принципе, можно использовать и пустые строки, но согласитесь, такие имена выглядят странно.
[code]window.sessionStorage.setItem("name 1", "Вася");
window.sessionStorage.setItem("name 2", "Пупкин");
window.sessionStorage.setItem("age", 27);[/code]
Этот код сохраняет в сессионном хранилище три уже знакомых нам по другим примерам значения. Отметим, что первые два значения имеют имена, содержащие пробелы, а третье имеет числовой тип и при сохранении будет автоматически преобразовано в строку.
Метод getItem позволяет получить значение, сохранённое под указанным именем. Имя значения, которое следует получить, указывается в качестве единственного параметра этого метода в виде строки. Отметим, что с помощью данного метода мы можем получать из хранилища значения, чьи имена содержать пробелы.
Метод getItem возвращает значение, сохранённое под указанным именем, опять же, в виде строки. Если значения с указанным нами именем, в хранилище нет, возвращается значение null.
[code]var name1 = window.sessionStorage.getItem("name 1");
var name2 = window.sessionStorage.getItem("name 2");
var age = parseInt(window.sessionStorage.getItem("age"));[/code]
Очень полезный метод removeItem удаляет из хранилища значение с указанным именем. Имя удаляемого значения передаётся ему в качестве единственного параметра в виде строки. Результата он не возвращает.
[code]window.sessionStorage.removeItem("name 1");
window.sessionStorage.removeItem("name 2");
window.sessionStorage.removeItem("age");[/code]
Другой полезный метод - clear - удаляет всё содержимое хранилища. Он не принимает параметров и не возвращает результата.
[code]window.sessionStorage.clear();
window.localStorage.clear();[/code]
Здесь мы очищаем и сессионное, и локальное хранилища.
5. Событие storage объекта Window и объект события StorageEvent
А теперь рассмотрим специфическое событие, имеющее прямое отношение к хранилищу DOM, и новый объект, представляющий сведения об этом событии.
5.1. Событие storage объекта Window
Объект Window, представляющий окно или вкладку веб-браузера, поддерживает событие storage, возникающее при помещении в хранилище нового значения или изменения значения, уже сохранённого там. Это событие мы можем использовать, чтобы отследить момент обновления содержимого хранилища.
Примечание:
В статье MSDN, посвящённой хранилищу DOM, указано, что это же событие поддерживает объект HTMLDocument, представляющий веб-страницу. Но, насколько удалось выяснить автору, это не так - объект HTMLDocument такого события не поддерживает.
Для примера возьмём две веб-страницы, описанные в параграфе 3.2, и изменим код второй веб-страницы таким образом, чтобы она отслеживала все изменения в содержимом локального хранилища и соответственно обновляла текст приветствия. Код первой страницы изменять не нужно.
Исправленный HTML-код второй веб-страницы представлен ниже.
[code]<!DOCTYPE html>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<TITLE>Локальное хранилище DOM</TITLE>
</HEAD>
<BODY>
<P ID="pOutput"></P>
<SCRIPT>
window.addEventListener("storage",
function()
{
var oPOutput = document.getElementById("pOutput");
var s = "Здравствуйте, " + window.localStorage.name1 + " " +
window.localStorage.name2 + "!";
oPOutput.innerHTML = s;
}, false);
</SCRIPT>
</BODY>
</HTML>[/code]
Здесь мы привязываем к событию storage объекта Window, представляющего текущее окно или вкладку браузера, функцию-обработчик и выполняем вывод текста приветствия в её теле.
Сохраним исправленную вторую веб-страницу в файле 5.html.
Проверим, установлен ли у нас веб-сервер (например, пакет Internet Information Services), установим его, если ещё не сделали этого, и запустим. Скопируем файлы 3.html и 5.html в корневую папку сайта, созданного при установке веб-сервера, и откроем в разных вкладках окна браузера первую и вторую страницы, набрав в строке адреса вкладок http://localhost/3.html и http://localhost/4.html. Переключимся на первую веб-страницу, введём в поля ввода свои имя и фамилию и нажмём кнопку Сохранить. Сразу после этого переключимся на вторую веб-страницу и увидим, что на ней появился текст привествия.
5.2. Объект события StorageEvent
В функцию-обработчик всегда передаётся экземпляр объекта Event или производного от него объекта, хранящий сведения о возникшем событии. Чтобы мы смогли получить к нему доступ, мы должны указать в списке параметров объявляемой функции-обработчика параметр, которому будет присвоен этот экземпляр объекта. Например, так (параметр выделен полужирным шрифтом):
[code]window.addEventListener("storage",
function(evt)
{
. . .
}, false);[/code]
После этого мы сможем получить в теле функции-обработчика доступ к экземпляру объекта, представляющему событие, обратившись к параметру evt.
В случае события storage функции-обработчику передаётся экземпляр объекта StorageEvent, производного от объекта Event. В дополнение к свойствам, унаследованным от объекта Event, он поддерживает свойства, перечисленные ниже. Отметим, что все эти свойства доступны только для чтения.
- key - возвращает имя добавленного или изменённого значения в виде строки (фактически - имя соответствующего свойства экземпляра объекта HTMLStorage, представляющего хранилище). Если значение было удалено или если хранилище было очищено, возвращает значение null.
- newValue - возвращает значение, помещаемое в хранилище (новое значение свойства экземпляра объекта HTMLStorage, представляющего хранилище), в виде строки. Если значение было удалено или если хранилище было очищено, возвращает значение null.
- oldValue - возвращает предыдущее значение, уже присутствующее в хранилище (предыдущее значение свойства экземпляра объекта HTMLStorage, представляющего хранилище), в виде строки. Если значение ранее не существовало и только сейчас было добавлено в хранилище, возвращает значение null.
- storageArea - возвращает экземпляр объекта HTMLStorage, представляющего хранилище (то есть значение свойства sessionStorage или localStorage объекта Window, в зависимости от того, содержимое какого именно хранилища было изменено).
- url - возвращает полный интернет-адрес веб-страницы, вызвавшей возникновение события storage, в виде строки.
Для эксперимента давайте возьмём первую веб-страницу из описанных в параграфе 3.1 и добавим в конец присутствующего в её коде веб-сценария такой фрагмент:
[code]<SCRIPT>
. . .
window.addEventListener("storage",
function(evt)
{
window.alert((evt.storageArea == window.sessionStorage ? "session" : "local") +
"." + evt.key + "(" + evt.oldValue + ") = " + evt.newValue);
}, false);
</SCRIPT>[/code]
Этот код выводит на экран окно-предупреждение, сообщающее о каждом изменении содержимого хранилища DOM и описывающее, какие именно изменения произошли, в таком формате:
<хранилище DOM>.<имя значения>(<старое значение>) = <новое значение>
<Хранилище DOM> обозначает, в содержимом какого именно хранилища DOM - сессионного или локального - произошли изменения. Если изменения произошли в сессионном хранилище, выводится слово "session", если в локальном - слово "local".
Данный фрагмент кода можно использовать при отладке веб-сценариев, использующих хранилище DOM. Сам автор применял его в работе над примерами к этой статье.
6. Что делать, если хранилище DOM недоступно
Но что делать, если веб-страница, в которой используется хранилище DOM, открыта в браузере, не поддерживающем это самое хранилище, и вообще HTML 5? Понятно, что выполнение веб-сценария завершится с ошибкой, и это в лучшем случае; в худшем же результаты его работы будут непредсказуемыми.
Но даже если браузер поддерживает хранилище DOM, оно может оказаться недоступным. Вспомним, что говорилось ещё в параграфе 3: хранилище DOM доступно только в том случае, если веб-страница была загружена с веб-сервера; если же загрузить веб-страницу напрямую с жёсткого диска, хранилище DOM доступно не будет.
Значит, нам придётся как-то проверять, доступно ли хранилище, перед тем как его использовать, и, если оно недоступно, предпринимать какие-то шаги, чтобы уведомить об этом посетителя. Для этого можно применить обычный подход, знакомый всем JavaScript-программистам:
[code]if (window.sessionStorage) {
//Хранилище DOM доступно, и мы можем его использовать
} else {
//Хранилище DOM недоступно
}[/code]
Если свойство sessionStorage экземпляра объекта Window, представляющего текущее окно (вкладку) браузера, содержит какое-либо значение, отличное от null и undefined, значит, сессионное хранилище доступно. При использовании этого значения в качестве условия оно будет преобразовано в true, и условие станет истинным. В результате выполнится секция if условного выражения, где находится код, использующий сессионное хранилище.
Если же свойство sessionStorage экземпляра объекта Window содержит значение undefined (или, возможно, null), это говорит, что сессионное хранилище недоступно. При использовании этого значения в качестве условного выражения оно будет преобразовано в false, и условие станет ложным. В результате выполнится секция else условного выражения, где находится код, сообщающий посетителю, что он не сможет сохранить какие-то данные.
Аналогично выполняется проверка доступности локального хранилища DOM. Нужно только заменить свойство sessionStorage на свойство localStorage.
Мы можем изменить веб-сценарии, присутствующие в созданных нами в параграфах 3.1 и 3.2 веб-страницах, таким образом, чтобы они перед использованием хранилища проверяли его доступность. Так, ниже приведён исправленный код веб-сценария, присутствующего в странице 1.html.
[code]var oBtnSave = document.getElementById("btnSave");
if (window.sessionStorage)
oBtnSave.addEventListener("click",
function()
{
var oTxtName1 = document.getElementById("txtName1");
var oTxtName2 = document.getElementById("txtName2");
window.sessionStorage.name1 = oTxtName1.value;
window.sessionStorage.name2 = oTxtName2.value;
window.location.href = "2.html";
}, false)
else
oBtnSave.disabled = true;[/code]
Здесь мы сначала проверяем, доступно ли сессионное хранилище. Если оно доступно, мы привязываем к событию click кнопки функцию-обработчик, которая и выполнит сохранение данных в хранилище. Если же оно недоступно, мы делаем недоступной кнопку, присвоив её свойству disabled значение true.
Свойство disabled объекта HTMLInputElement, представляющего любой элемент управления, как раз и "отвечает" за доступность или недоступность этого элемента управления. Значение false этого свойства делает элемент управления доступным, а значение true - недоступным.
Сценарии других веб-страниц из примеров правятся подобным образом.
Дополнительные материалы
Март 2011