HTML5 Web Storage - работа с веб-хранилищем

66

На данном этапе мы познакомились с основами работы с веб-хранилищем — как помещать информацию в него и извлекать эту информацию оттуда. Но нам нужно знать еще несколько подробностей и полезных методов, прежде чем мы сможем применять его. В последующих разделах мы изучим, как удалять информацию из веб-хранилища и просматривать всю находящуюся в нем в данный момент информацию. Также мы рассмотрим, как работать с разными типами данных, сохранять пользовательские объекты и реагировать на изменения в коллекции хранящихся данных.

Удаление элементов

Задача удаления хранящихся в веб-хранилище данных проста до предела. Для удаления отдельного ненужного элемента используется метод removeItem(), которому передается соответствующий ключ:

// JS
localStorage.removeItem("localData");

А если же требуется удалить все локальные данные, сохраненные веб-сайтом, используется более радикальный метод clear():

// JS
localStorage.clear();

Поиск всех сохраненных элементов

Чтобы извлечь отдельный элемент данных из веб-хранилища, нам нужно знать его ключ. Но есть еще один ловкий прием. Посредством метода key() можно извлечь все элементы, хранящиеся в локальном хранилище или в хранилище сеансов (для текущего веб-сайта), даже если нам не известны их ключи. Этот метод полезен при отладке или когда нужно просто просмотреть, какие данные сохраняют другие страницы веб-сайта и под какими ключами.

Ниже показан простой пример, в котором нажатие кнопки активирует функцию findAllItems(), которая сканирует коллекцию элементов в локальном хранилище. Далее приводится полный код примера:

  <button onclick="findAllItems()">Содержимое локального хранилища</button>
  <ul id="itemList"></ul>
function findAllItems() {
  // Получаем элемент <ul> для списка элементов данных
  var itemList = document.getElementById("itemList");
  
  // Очищаем список
  itemList.innerHTML = "";

  // Перебираем все элементы данных в цикле
  for (var i=0; i<localStorage.length; i++) {
	// Получаем ключ текущего элемента
    var key = localStorage.key(i);
	
	// Получаем сам элемент, хранящийся под этим ключом
    var item = localStorage[key];

    // Заполняем список
    var newItem = document.createElement("li");
    newItem.innerHTML = key + ": " + item;
    itemList.appendChild(newItem);
  }
}

// Сохранить какие-нибудь данные для примера
window.onload = function() {
	localStorage.username = "Alexandr";
	localStorage.password = "12345";
	localStorage.work = "programmer";
	localStorage.lang = "C#";
}
Получение всех данных из веб-хранилища

Сохранение чисел и дат

До сих пор в наших исследованиях веб-хранилища мы обходили стороной один важный аспект. А именно — все данные, сохраняемые посредством объектов localStorage и sessionStorage, автоматически преобразуются в текст.

Со значениями, которые и так являются текстовыми (например, имя пользователя, вводимое в текстовое поле), это обстоятельство не представляет никаких проблем. Но числа не такие сговорчивые.

Текст и числа легко поддаются обработке, но при сохранении в веб-хранилище других типов данных следует соблюдать осторожность. Для некоторых типов данных существуют удобные процедуры приведения типов (например Number для чисел). Но допустим, например, мы сохранили следующую дату:

var today = new Date();

Этот код сохраняет не объект даты, а текстовую строку, например Sat Jun 09 2013 13:30:46. К сожалению, не существует легкого способа для преобразования этого текста обратно в объект даты при извлечении его из хранилища. А если у нас нет объекта даты, мы не сможем манипулировать этой датой, как и ее исходным объектом, например вызывать его методы и выполнять вычисления.

Чтобы решить эту проблему, разработчик должен явно преобразовать дату в текст, а потом преобразовать извлеченный из хранилища текст обратно в правильный объект даты. Пример таких преобразований, включая и пример с числом, приводится в следующем коде:

window.onload = function() {
	// Сохранить какие-нибудь данные
	localStorage.mynumber = 5;
	
	// Получить число при считывании
	var x = Number(localStorage.mynumber);
	
	// Проверяем (если не использовать приведение к типу Number,
	// результатом будет строка "520")
	alert(x + 20);
	
	// Создаем новый объект даты
	var today = new Date();
	
	// Преобразуем дату в тестовую строку в формате ГГГГ/ММ/ДД 
	// и сохраняем эту строку
	sessionStorage.session_started = today.getFullYear() + 
	         "/" + today.getMonth() + "/" + today.getDate();
	
	// Теперь извлекаем из хранилища строку даты и с ее помощью 
	// создаем новый объект Date.
	// Это возможно благодаря распознаваемому формату текста даты
	today = new Date(sessionStorage.session_started);
	
	alert(today.getFullYear());
}

Сохранение объектов

В предыдущем разделе мы рассмотрели преобразование чисел и дат в текст для сохранения в веб-хранилище и обратное преобразование данных, извлеченных из веб-хранилища. Выполнение этих преобразований доступно благодаря таким возможностям языка JavaScript, как функция Number(), и преобразованиям, зашитым в объекты данных. Но существует большое количество других объектов, которые нельзя преобразовывать таким способом. Классическим примером таких объектов будет пользовательский объект.

Чтобы сохранить пользовательский объект в веб-хранилище, нам нужно преобразовать его в текст. Это можно было бы сделать с помощью кода, что отвечало бы цели, но потребовало бы приложения больших усилий. К счастью, существует более простой, стандартный способ решения этой задачи, который называется кодированием JSON (JavaScript Object Notation - система обозначений объектов JavaScript).

Облегченный формат JSON преобразует структурированные данные — наподобие всех значений, обернутых в объект в текст. Но лучшее в JSON то, что его поддержка встроена в браузеры. Это означает, что мы можем преобразовать в текст любой объект JavaScript вместе с его данными простым вызовом метода JSON.stringify(), а метод JSON.parse() преобразует этот текст обратно в объект.

Далее приведен код примера использования этих методов с простым пользовательским объектом UserInfo:

// Определяем тип данных UserInfo
function UserInfo(name, family, age, login) {
	this.name = name;
	this.family = family;
	this.age = age;
	this.login = login;
}

window.onload = function() {
	// Создаем объект UserInfo
	var user = new UserInfo("Иван", "Петров", 28, "ivan85_krevedko");
	
	// Сохраняем этот объект в формате JSON
	sessionStorage.userinfo = JSON.stringify(user);
	
	// Для проверки
	user = null;
	
	// Преобразуем JSON-текст в соответствующий объект
	user = JSON.parse(sessionStorage.userinfo);
	
	alert("Привет " + user.name + " " + user.family);
}

Реагирование на изменения в хранилище

Кроме рассмотренных применений, веб-хранилище предоставляет способ для общения между разными окнами браузера. Это возможно благодаря тому, что каждое изменение локального хранилища или хранилища сеансов активирует событие window.onstorage во всех других окнах, в которых просматривается та же страница или другая страница того же самого сайта.

Таким образом, изменение локального хранилища для страницы www.professorweb.ru/index.html активирует событие onstorage в окне браузера для страницы www.professorweb.ru/about.html. (Конечно же, страницы должны просматриваться в том же браузере и на том же компьютере, но вы это уже и так знали, не так ли?)

Событие onstorage активируется при добавлении объекта в хранилище, изменении объекта, находящегося в хранилище, удалении объекта из хранилища или полной очистке хранилища. Событие не активируется, если код выполняет операцию с хранилищем, которая не имеет никакого эффекта (например, сохранение значения, которое уже находится в хранилище, или очистка пустого хранилища).

Рассмотрим пример страницы показанной на рисунке ниже. Здесь в хранилище можно добавить любое значение с любым ключом, заполнив соответствующие текстовые поля и нажав кнопку "Добавить". Добавленное значение отображается на другой, уже открытой странице.

Обновление локального веб-хранилища на одной странице
Фиксация обновления локального веб-хранилища на другой странице

Чтобы создать пример, показанный на этих рисунках, сначала нужно создать страницу, на которой выполняется ввод и сохранение данных. Данные сохраняются функцией addValue(), которая активируется нажатием кнопки "Добавить" и выглядит так:

function addValue() {
    // Получаем значения из обоих текстовых полей
    var key = document.getElementById("key").value;
    var item = document.getElementById("item").value;

    // Сохраняем элемент в локальном хранилище.
    // Если ключ уже существует, новый элемент заменяет старый
    localStorage[key] = item;
}

Вторая страница тоже не представляет ничего сложного. При загрузке страницы событию window.onstorage присваивается функция с помощью следующего кода:

window.onload = function() {
     // Подключаем событие onstorage к функции storageChanged
     window.addEventListener("storage", storageChanged, false);
};

function storageChanged(e) {
    var message = document.getElementById("updateMessage");
    message.innerHTML = "Обновление локального хранилища.";
    message.innerHTML += "<br>Ключ: " + e.key;
    message.innerHTML += "<br>Старое значение: " + e.oldValue;
    message.innerHTML += "<br>Новое значение: " + e.newValue;
    message.innerHTML += "<br>URL: " + e.url;
}

Этот код выглядит несколько иначе, чем код для обработки событий, с которым мы сталкивались до сих пор. Вместо установки события window.onstorage он вызывает функцию window.addEventListener(). Это сделано для того, чтобы код работал на всех современных браузерах. Если активировать событие window.onstorage напрямую, код будет работать на всех браузерах, за исключением Firefox.

Старожилы, наверное, помнят, что метод addEventListener() не работает на версиях Internet Explorer 8 и более ранних. В данном примере это ограничение несущественно, т.к. IE 8 не поддерживает события хранилища в любом случае.

Функция storageChanged() выполняет простую задачу. Она берет обновленную информацию и выводит ее на странице в элементе <div>.

Как видно, событие onstorage предоставляет несколько единиц информации, в том числе ключ измененного значения, старое значение, новое значение и URL страницы, на которой изменение было выполнено. Если событие onstorage активировано вставкой нового элемента, значение свойства e.oldValue равно либо null (в большинстве браузеров), либо пустой строке (в Internet Explorer).

Если открыто несколько страниц одного и того же веб-сайта, событие onstorage активируется в каждой из этих страниц, за исключением страницы, на которой было осуществлено изменение (в данном примере это будет страница StorageEvents1.html). Но, как всегда, Internet Explorer не следует этому правилу и активирует событие onstorage и на странице, выполняющей изменения.

Пройди тесты
Лучший чат для C# программистов