Доброго времени суток, друзья!
В данной статье мы рассмотрим парочку примеров использования Web Storage API или объекта «Storage».
Что конкретно мы будем делать?
- Научимся запоминать время воспроизведения видео.
- Поработаем с формой входа на страницу.
- Напишем логику списка задач (todo list).
- Схематично набросаем корзину для товаров.
Краткий обзор
Объект «Storage» используется для хранения данных на стороне клиента и в этом смысле выступает альтернативой cookies. Преимущество Storage состоит в размере хранилища (от 5 Мб, зависит от браузера, при превышении лимита выбрасывается ошибка «QUOTA_EXCEEDED_ERR») и отсутствии необходимости обращаться к серверу. Существенный недостаток — безопасность: стоит вредоносному скрипту получить доступ к странице, и пиши пропало. Поэтому крайне не рекомендуется хранить в Storage конфиденциальную информацию.
Справедливости ради стоит отметить, что на сегодняшний день существуют более продвинутые решения для хранения данных на стороне клиента — это IndexedBD, Service Workers + Cache API и др.
О сервис-воркерах можно почитать здесь.
Web Storage API включает в себя localStorage и sessionStorage. Разница между ними состоит во времени хранения данных. В localStorage данные хранятся постоянно до их «явного» удаления (ни перезагрузка страницы, ни ее закрытие не приводят к удалению данных). Время хранения данных в sessionStorage, как следует из названия, ограничено сессией браузера. Поскольку sessionStorage на практике почти не используется, мы будет рассматривать только localStorage.
Что необходимо знать о localStorage?
- При использовании localStorage создается представление объекта «Storage».
- Данные в localStorage хранятся в виде пар ключ/значение.
- Данные хранятся в виде строк.
- Данные не сортируются, что иногда приводит к их перемешиванию (в чем мы убедимся на примере списка задач).
- При включении в браузере режима инкогнито или приватного режима использование localStorage может стать невозможным (зависит от браузера).
localStorage обладает следующими методами:
- Storage.key(n) — имя ключа с индексом n
- Storage.getItem() — получить элемент
- Storage.setItem() — записать элемент
- Storage.removeItem() — удалить элемент
- Storage.clear() — очистить хранилище
- Storage.length — длина хранилища (количество элементов — пар ключ/значение)
В спецификации это выглядит так:
interface Storage { readonly attribute unsigned long length; DOMString? key(unsigned long index); getter DOMString? getItem(DOMString key); setter void setItem(DOMString key, DOMString value); deleter void removeItem(DOMString key); void clear(); };
Данные в хранилище записываются одним из следующих способов:
localStorage.color = 'deepskyblue' localStorage[color] = 'deepskyblue' localStorage.setItem('color', 'deepskyblue') // рекомендуется использовать этот способ
Получить данные можно так:
localStorage.getItem('color') localStorage['color']
Как перебрать ключи хранилища и получить значения?
// способ 1 for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i) console.log(`${key}: ${localStorage.getItem(key)}`) } // способ 2 let keys = Object.keys(localStorage) for (let key of keys) { console.log(`${key}: ${localStorage.getItem(key)}`) }
Как мы отмечали выше, данные в хранилище имеют строковый формат, поэтому с записью объектов возникают некоторые трудности, которые легко решаются с помощью тандема JSON.stringify() — JSON.parse():
localStorage.user = { name: 'Harry' } console.dir(localStorage.user) // [object Object] localStorage.user = JSON.stringify({ name: 'Harry' }) let user = JSON.parse(localStorage.user) console.dir(user.name) // Harry
Для взаимодействием с localStorage существует специальное событие — storage (onstorage), которое возникает при записи/удалении данных. Оно имеет следующие свойства:
- key — ключ
- oldValue — старое значение
- newValue — новое значение
- url — адрес хранилища
- storageArea — объект, в котором произошло изменение
В спецификации это выглядит так:
[Constructor(DOMString type, optional StorageEventInit eventInitDict)] interface StorageEvent : Event { readonly attribute DOMString? key; readonly attribute DOMString? oldValue; readonly attribute DOMString? newValue; readonly attribute DOMString url; readonly attribute Storage? storageArea; }; dictionary StorageEventInit : EventInit { DOMString? key; DOMString? oldValue; DOMString? newValue; DOMString url; Storage? storageArea; };
Допускает ли localStorage прототипирование?
Storage.prototype.removeItems = function() { for (item in arguments) { this.removeItem(arguments[item]) } } localStorage.setItem('first', 'some value') localStorage.setItem('second', 'some value') localStorage.removeItems('first', 'second') console.log(localStorage.length) // 0
Как проверить наличие данных в localStorage?
// способ 1 localStorage.setItem('name', 'Harry') function isExist(name) { return (!!localStorage[name]) } isExist('name') // true // способ 2 function isItemExist(name) { return (name in localStorage) } isItemExist('name') // true
В браузере localStorage можно найти здесь:

Довольно слов, пора переходить к делу.
Примеры использования
Запоминаем время воспроизведения видео
window.onload = () => { // находим элемент <video> let video = document.querySelector('video') // если localStorage содержит значение currentTime (текущее время), присваиваем это значение video.currentTime if(localStorage.currentTime) { video.currentTime = localStorage.currentTime } // при каждом изменении video.currentTime, записываем его значение в localStorage.currentTime video.addEventListener('timeupdate', () => localStorage.currentTime = video.currentTime) }
Результат выглядит так:

Запускаем видео и останавливаем воспроизведение на третьей секунде, например. Время, на котором мы остановились, хранится в localStorage. Чтобы в этом убедиться, перезагружаем или закрываем/открываем страницу. Видим, что текущее время воспроизведения видео остается прежним.
Работаем с формой для входа
Разметка выглядит так:
<form> Login: <input type="text"> Password: <input type="text"> <input type="submit"> </form>
У нас имеется форма и три «инпута». Для пароля мы используем <input type = «text»>, поскольку если использовать правильный тип (password), Chrome будет пытаться сохранять введенные данные, что помешает нам реализовать собственный функционал.
JavaScript:
// находим форму и инпуты для ввода логина и пароля let form = document.querySelector('form') let login = document.querySelector('input') let password = document.querySelector('input + input') // если localStorage не пустой // получаем из него необходимые данные // и присваиваем их инпутам if (localStorage.length != 0) { login.value = localStorage.login password.value = localStorage.password } // вешаем на форму обработчик события "submit" form.addEventListener('submit', () => { // записываем введенные пользователем данные в localStorage localStorage.login = login.value localStorage.password = password.value // если пользователем введены hello и world в качестве логина и пароля, соответственно // используем древний метод для вывода сообщения "welcome" на страницу if (login.value == 'hello' && password.value == 'world') { document.write('welcome') } })
Мы не «валидируем» форму, потому что наша цель — изучить возможности localStorage. Отсутствие валидации, в частности, позволяет записывать пустые строки в качестве логина и пароля.
Результат выглядит так:

Вводим волшебные слова.

Данные записываются в localStorage, а на страницу выводится приветствие.
Пишем логику списка задач
Разметка выглядит так:
<input type="text"><button class="add">add task</button><button class="clear">clear storage</button> <ul></ul>
У нас имеется «инпут» для ввода задачи, кнопка для добавления задачи в список, кнопка для очистки списка и хранилища и контейнер для списка.
JavaScript:
// находим инпут и фокусируемся на нем let input = document.querySelector('input') input.focus() // находим кнопку для добавления задачи в список let addButton = document.querySelector('.add') // находим контейнер для списка let list = document.querySelector('ul') // если в localStorage имеются данные if (localStorage.length != 0) { // цикл по количеству пар ключ/значение for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i) // получаем шаблон - элемент списка let template = `${localStorage.getItem(key)}` // помещаем задачу в список list.insertAdjacentHTML('afterbegin', template) } // находим все кнопки с классом "close" - галочки для выполненных задач document.querySelectorAll('.close').forEach(b => { // для каждой кнопки b.addEventListener('click', e => { // получаем родительский элемент "li" let item = e.target.parentElement // удаляем задачу из списка list.removeChild(item) // удаляем данные из localStorage localStorage.removeItem(`${item.dataset.id}`) }) }) } // возможность добавления задачи в список при нажатии клавиши "Enter" window.addEventListener('keydown', e => { if (e.keyCode == 13) addButton.click() }) // вешаем на кнопку для добавления задачи в список обработчик события "клик" addButton.addEventListener('click', () => { // получаем строку - задачу let text = input.value // формируем шаблон, запись и идентификация значений по ключам осуществляется через атрибут "data-id" let template = `<li data-id="${++localStorage.length}"><button class="close">V</button><time>${new Date().toLocaleDateString()}</time> <p>${text}</p></li>` // добавляем шаблон - задачу в список list.insertAdjacentHTML('afterbegin', template) // записываем данные в localStorage localStorage.setItem(`${++localStorage.length}`, template) // сбрасываем значение инпута input.value = '' // вешаем на кнопку для удаления задачи из списка обработчик события "клик" document.querySelector('.close').addEventListener('click', e => { // получаем элемент списка - родительский элемент кнопки let item = e.target.parentElement // удаляем задачу из списка list.removeChild(item) // удаляем данные из localStorage localStorage.removeItem(`${item.dataset.id}`) }) }) // вешаем на кнопку для очистки обработчик события "клик" document.querySelector('.clear').onclick = () => { // очищаем хранилище localStorage.clear() // удаляем задачи из списка document.querySelectorAll('li').forEach(item => list.removeChild(item)) // фокусируемся на инпуте input.focus() }
Результат выглядит так:

Задачи, добавляемые в список, сохраняются в localStorage в виде готовой разметки. При перезагрузке страницы список формируется из данных хранилища (имеет место перемешивание, о котором упоминалось выше).

Удаление задачи из списка посредством нажатия зеленой галочки приводит к удалению соответствующей пары ключ/значение из хранилища.
Схема корзины для товаров
Мы не преследуем цель создать полнофункциональную корзину, поэтому код будет написан «в старом стиле».
Разметка одного товара выглядит так:
<div class="item"> <h3 class="title">Item1</h3> <img src="http://placeimg.com/150/200/tech" alt="#"> <p>Price: <span class="price">1000</span></p> <button class="add" data-id="1">Buy</button> </div>
У нас имеется контейнер для товара, наименование, изображение и цена товара, а также кнопка для добавления товара в корзину.
Также у нас имеется контейнер для кнопок отображения содержимого корзины и очистки корзины и хранилища и контейнер для корзины.
<div class="buttons"> <button id="open">Cart</button> <button id="clear">Clear</button> </div> <div id="content"></div>
JavaScript:
// находим товары и корзину let itemBox = document.querySelectorAll('.item'), cart = document.getElementById('content'); // получаем данные из localStorage function getCartData() { return JSON.parse(localStorage.getItem('cart')); } // записываем данные в хранилище function setCartData(o) { localStorage.setItem('cart', JSON.stringify(o)); } // добавление товара в корзину function addToCart() { // блокируем кнопку на время работы с корзиной this.disabled = true; // получаем данные из корзины или создаем новый объект, если данные отсутствуют let cartData = getCartData() || {}, // родительский элемент кнопки "Buy" parentBox = this.parentNode, // id товара itemId = this.getAttribute('data-id'), // название товара itemTitle = parentBox.querySelector('.title').innerHTML, // стоимость товара itemPrice = parentBox.querySelector('.price').innerHTML; // +1 к товару if (cartData.hasOwnProperty(itemId)) { cartData[itemId][2] += 1; } else { // + товар cartData[itemId] = [itemTitle, itemPrice, 1]; } // обновляем данные в localStorage if (!setCartData(cartData)) { // снимаем блокировку кнопки this.disabled = false; } } // устанавливаем обработчик события "клик" на каждую кнопку "Buy" for (let i = 0; i < itemBox.length; i++) { itemBox[i].querySelector('.add').addEventListener('click', addToCart) } // содержимое корзины function openCart() { // получаем данные из хранилища let cartData = getCartData(), totalItems = '', totalGoods = 0, totalPrice = 0; // формируем данные для вывода if (cartData !== null) { totalItems = '<table><tr><th>Name</th><th>Price</th><th>Amount</th></tr>'; for (let items in cartData) { totalItems += '<tr>'; for (let i = 0; i < cartData[items].length; i++) { totalItems += '<td>' + cartData[items][i] + '</td>'; } totalItems += '</tr>'; totalGoods += cartData[items][2]; totalPrice += cartData[items][1] * cartData[items][2]; } totalItems += '</table>'; cart.innerHTML = totalItems; cart.append(document.createElement('p').innerHTML = 'Goods: ' + totalGoods + '. Price: ' + totalPrice); } else { // если в корзине пусто cart.innerHTML = 'empty'; } } // открываем корзину document.getElementById('open').addEventListener('click', openCart); // очищаем корзину document.getElementById('clear').addEventListener('click', () => { localStorage.removeItem('cart'); cart.innerHTML = 'сleared'; });
Результат выглядит так:

Выбранные товары записываются в хранилище в виде одной пары ключ/значение.

При нажатии кнопки «Cart» данные из localStorage выводятся в таблицу, подсчитывается общее количество товаров и их стоимость.
Благодарю за внимание.
ссылка на оригинал статьи https://habr.com/ru/post/496348/
Добавить комментарий