Как работать с localstorage в javascript

Введение в хранилище HTML5

То, что я буду называть «HTML5-хранилище» в спецификации именуется «веб-хранилище», которое в свое время было частью спецификации HTML5, но затем выделено в отдельную спецификацию по неинтересным для вас политическим мотивам. Некоторые разработчики браузеров также называют его «Локальное хранилище» или «DOM-хранилище». Ситуацию с именами осложняют некоторые похожие названия из других стандартов, о которых пойдет речь ниже.

Так что такое HTML5-хранилище? Проще говоря, это способ для веб-страниц хранить пары ключ/значение локально с помощью браузера. Как и кукисы, эти данные сохраняется даже после ухода с сайта, закрытия вкладки браузера, с выходом из браузера или чего-нибудь еще. В отличие от кукисов эти данные никогда не передаются на удаленный веб-сервер (если только вы не пожелаете отправлять их самостоятельно). В отличие от всех предыдущих попыток обеспечить постоянное локальное хранилище, эта технология встроена в браузеры, поэтому она доступна даже тогда, когда нет сторонних плагинов.

Какие браузеры? Ну, последние версии практически каждого браузера поддерживает HTML5-хранилище… даже Internet Explorer!

Поддержка HTML5-хранилища
IE Firefox Safari Chrome Opera iPhone Android
8.0+ 3.5+ 4.0+ 4.0+ 10.5+ 2.0+ 2.0+

С помощью JavaScript вы можете получить доступ к HTML5-хранилищу через объект localStorage глобального объекта window. Перед использованием вы должны проверить, что браузер поддерживает технологию.

Проверка на HTML5-хранилище

function supports_html5_storage() {
  try {
    return ‘localStorage’ in window && window !== null;
} catch (e) {
    return false;
  }
}

Вместо самостоятельного написания этой функции, вы можете использовать библиотеку Modernizr.

if (Modernizr.localstorage) {
  // window.localStorage is available!
} else {
  // нет встроенной поддержки HTML5-хранилища
}

Открыть базу данных

Для начала работы с IndexedDB нужно открыть базу данных.

Синтаксис:

  • – название базы данных, строка.
  • – версия базы данных, положительное целое число, по умолчанию (объясняется ниже).

У нас может быть множество баз данных с различными именами, но все они существуют в контексте текущего источника (домен/протокол/порт). Разные сайты не могут получить доступ к базам данных друг друга.

После этого вызова необходимо назначить обработчик событий для объекта :

  • : база данных готова к работе, готов «объект базы данных» , его следует использовать для дальнейших вызовов.
  • : не удалось открыть базу данных.
  • : база открыта, но её схема устарела (см. ниже).

IndexedDB имеет встроенный механизм «версионирования схемы», который отсутствует в серверных базах данных.

В отличие от серверных баз данных, IndexedDB работает на стороне клиента, в браузере, и у нас нет прямого доступа к данным. Но когда мы публикуем новую версию нашего приложения, возможно, нам понадобится обновить базу данных.

Если локальная версия базы данных меньше, чем версия, определённая в , то сработает специальное событие , и мы сможем сравнить версии и обновить структуры данных по мере необходимости.

Это событие также сработает, если базы данных ещё не существует, так что в этом обработчике мы можем выполнить инициализацию.

Например, когда мы впервые публикуем наше приложение, мы открываем базу данных с версией и выполняем инициализацию в обработчике :

Когда мы публикуем вторую версию:

Таким образом, в мы обновляем базу данных. Скоро подробно увидим, как это делается. А после того, как этот обработчик завершится без ошибок, сработает .

После у нас есть объект базы данных в , который мы будем использовать для дальнейших операций.

Удалить базу данных:

А что, если открыть предыдущую версию?

Что если мы попробуем открыть базу с более низкой версией, чем текущая? Например, на клиенте база версии 3, а мы вызываем .

Возникнет ошибка, сработает .

Такое может произойти, если посетитель загрузил устаревший код, например, из кеша прокси. Нам следует проверить и предложить ему перезагрузить страницу. А также проверить наши кеширующие заголовки, убедиться, что посетитель никогда не получит устаревший код.

Раз уж мы говорим про версионирование, рассмотрим связанную с этим небольшую проблему.

Проблема заключается в том, что база данных всего одна на две вкладки, так как это один и тот же сайт, один источник. И она не может быть одновременно версии 1 и 2. Чтобы обновить на версию 2, все соединения к версии 1 должны быть закрыты.

Чтобы это можно было организовать, при попытке обновления на объекте базы возникает событие . Нам нужно слушать его и закрыть соединение к базе (а также, возможно, предложить пользователю перезагрузить страницу, чтобы получить обновлённый код).

Если мы его не закроем, то второе, новое соединение будет заблокировано с событием вместо .

Код, который это делает:

Здесь мы делаем две вещи:

  1. Добавляем обработчик после успешного открытия базы, чтобы узнать о попытке параллельного обновления.
  2. Добавляем обработчик для ситуаций, когда старое соединение не было закрыто. Такого не произойдёт, если мы закрываем его в .

Есть и другие варианты. Например, мы можем более «мягко» закрыть соединение в , предложить пользователю сохранить данные перед этим. Новое обновляющее соединение будет заблокировано сразу после того как обработчик завершится, не закрыв соединение, и мы можем в новой вкладке попросить посетителя закрыть старые для обновления.

Такой конфликт при обновлении происходит редко, но мы должны как-то его обрабатывать, хотя бы поставить обработчик , чтобы наш скрипт не «умирал» молча, удивляя посетителя.

Storages

Store.js will pick the best available storage, and automatically falls back to the first available storage that works:

List of all Storages

  • all.js All the storages in one handy place.
  • localStorage.js Store values in localStorage. Great for all modern browsers.
  • sessionStorage.js Store values in sessionStorage.
  • cookieStorage.js Store values in cookies. Useful for Safari Private mode.
  • memoryStorage.js Store values in memory. Great fallback to ensure store functionality at all times.
  • oldFF-globalStorage.js Store values in globalStorage. Only useful for legacy Firefox 3+.
  • oldIE-userDataStorage.js Store values in userData. Only useful for legacy IE 6+.

Storages limits

Each storage has different limits, restrictions and overflow behavior on different browser. For example, Android has has a 4.57M localStorage limit in 4.0, a 2.49M limit in 4.1, and a 4.98M limit in 4.2… Yeah.

To simplify things we provide these recommendations to ensure cross browser behavior:

Storage Targets Recommendations More info
all All browsers Store < 1 million characters (Except Safari Private mode)
all All & Private mode Store < 32 thousand characters (Including Safari Private mode)
localStorage Modern browsers Max 2mb (~1M chars)
sessionStorage Modern browsers Max 5mb (~2M chars)
cookieStorage Safari Private mode Max 4kb (~2K chars)
userDataStorage IE5, IE6 & IE7 Max 64kb (~32K chars)
globalStorage Firefox 2-5 Max 5mb (~2M chars)
memoryStorage All browsers, fallback Does not persist across pages!

Write your own Storage

Chances are you won’t ever need another storage. But if you do…

See storages/ for examples. Two good examples are memoryStorage and localStorage.

Basically, you just need an object that looks like this:

// Example custom storage
var storage = {
	name: 'myStorage',
	read: function(key) { ... },
	write: function(key, value) { ... },
	each: function(fn) { ... },
	remove: function(key) { ... },
	clearAll: function() { ... }
}
var store = require('store').createStore(storage)

Поиск по индексированному полю

Для поиска по другим полям объекта нам нужно создать дополнительную структуру данных, называемую «индекс» (index).

Индекс является «расширением» к хранилищу, которое отслеживает данное поле объекта. Для каждого значения этого поля хранится список ключей для объектов, которые имеют это значение. Ниже будет более подробная картина.

Синтаксис:

  • – название индекса,
  • – путь к полю объекта, которое индекс должен отслеживать (мы собираемся сделать поиск по этому полю),
  • – необязательный объект со свойствами:

    • – если true, тогда в хранилище может быть только один объект с заданным значением в . Если мы попытаемся добавить дубликат, то индекс сгенерирует ошибку.
    • – используется только, если является массивом. В этом случае, по умолчанию, индекс обрабатывает весь массив как ключ. Но если мы укажем true в , тогда индекс будет хранить список объектов хранилища для каждого значения в этом массиве. Таким образом, элементы массива становятся ключами индекса.

В нашем примере мы храним книги с ключом .

Допустим, мы хотим сделать поиск по полю .

Сначала нам нужно создать индекс. Индексы должны создаваться в , как и хранилище объектов:

  • Индекс будет отслеживать поле .
  • Поле price не уникальное, у нас может быть несколько книг с одинаковой ценой, поэтому мы не устанавливаем опцию .
  • Поле price не является массивом, поэтому флаг не применим.

Представим, что в нашем есть 4 книги. Вот картинка, которая показывает, что такое «индекс».

Как уже говорилось, индекс для каждого значения (второй аргумент) хранит список ключей, имеющих эту цену.

Индексы автоматически обновляются, нам не нужно об этом заботиться.

Мы также можем использовать , чтобы создать диапазон и найти дешёвые/дорогие книги:

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

sessionStorage

Объект используется гораздо реже, чем .

Свойства и методы такие же, но есть существенные ограничения:

  • существует только в рамках текущей вкладки браузера.

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

Давайте посмотрим на это в действии.

Запустите этот код…

…И обновите страницу. Вы всё ещё можете получить данные:

…Но если вы откроете ту же страницу в другой вкладке и попробуете получить данные снова, то код выше вернёт , что значит «ничего не найдено».

Так получилось, потому что привязан не только к источнику, но и к вкладке браузера. Поэтому используется нечасто.

Использование HTML5-хранилища

HTML5-хранилище базируется на именах пар ключ/значение. Вы сохраняете информацию, основываясь на имени ключа, а затем можете получить эти данные с тем же ключом. Имя ключа это строка. Данные могут быть любого типа, который поддерживает JavaScript, включая строки, логические, целые числа или числа с плавающей запятой. Однако в действительности данные хранятся в виде строки. Если вы сохраняете и извлекаете не строки, то надо будет использовать такие функции как parseInt() или parseFloat(), чтобы перевести полученные данные в корректные типы JavaScript.

Интерфейс хранилища {
  Получить через getItem(ключ);
  Установить через setItem(ключ, данные);
};

Вызов setItem() с существующим именем ключа молча перепишет предыдущее значение. Вызов getItem() с несуществующим ключом вернет NULL, а не вызовет исключение.

Подобно другим объектам JavaScript вы можете обращаться к объекту localStorage как к ассоциативному массиву. Вместо использования методов getItem() и setItem(), вы можете просто указать квадратные скобки. Например, этот фрагмент кода

var foo = localStorage.getItem(«bar»);
// …
localStorage.setItem(«bar», foo);

может быть переписан с использованием синтаксиса квадратных скобок:

var foo = localStorage;
// …
localStorage = foo;

Есть также методы для удаления значений по имени ключа, а также очистки всего хранилища (то есть удаление всех ключей и значений одновременно).

Интерфейс хранилища {
  Удалить через removeItem(ключ);
  clear();
}

Вызов removeItem() с несуществующим ключом ничего не вернет.

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

Интерфейс хранилища {
  length
  Получить key(целое неотрицательное число);
}

Если при вызове key() индекс лежит не в диапазоне от 0 до (length-1), то функция вернет null.

Storage event

When the data gets updated in or , event triggers, with properties:

  • – the key that was changed ( if is called).
  • – the old value ( if the key is newly added).
  • – the new value ( if the key is removed).
  • – the url of the document where the update happened.
  • – either or object where the update happened.

The important thing is: the event triggers on all objects where the storage is accessible, except the one that caused it.

Let’s elaborate.

Imagine, you have two windows with the same site in each. So is shared between them.

You might want to open this page in two browser windows to test the code below.

If both windows are listening for , then each one will react on updates that happened in the other one.

Please note that the event also contains: – the url of the document where the data was updated.

Also, contains the storage object – the event is the same for both and , so references the one that was modified. We may even want to set something back in it, to “respond” to a change.

That allows different windows from the same origin to exchange messages.

Modern browsers also support Broadcast channel API, the special API for same-origin inter-window communication, it’s more full featured, but less supported. There are libraries that polyfill that API, based on , that make it available everywhere.

JavaScript

JS Array
concat()
constructor
copyWithin()
entries()
every()
fill()
filter()
find()
findIndex()
forEach()
from()
includes()
indexOf()
isArray()
join()
keys()
length
lastIndexOf()
map()
pop()
prototype
push()
reduce()
reduceRight()
reverse()
shift()
slice()
some()
sort()
splice()
toString()
unshift()
valueOf()

JS Boolean
constructor
prototype
toString()
valueOf()

JS Classes
constructor()
extends
static
super

JS Date
constructor
getDate()
getDay()
getFullYear()
getHours()
getMilliseconds()
getMinutes()
getMonth()
getSeconds()
getTime()
getTimezoneOffset()
getUTCDate()
getUTCDay()
getUTCFullYear()
getUTCHours()
getUTCMilliseconds()
getUTCMinutes()
getUTCMonth()
getUTCSeconds()
now()
parse()
prototype
setDate()
setFullYear()
setHours()
setMilliseconds()
setMinutes()
setMonth()
setSeconds()
setTime()
setUTCDate()
setUTCFullYear()
setUTCHours()
setUTCMilliseconds()
setUTCMinutes()
setUTCMonth()
setUTCSeconds()
toDateString()
toISOString()
toJSON()
toLocaleDateString()
toLocaleTimeString()
toLocaleString()
toString()
toTimeString()
toUTCString()
UTC()
valueOf()

JS Error
name
message

JS Global
decodeURI()
decodeURIComponent()
encodeURI()
encodeURIComponent()
escape()
eval()
Infinity
isFinite()
isNaN()
NaN
Number()
parseFloat()
parseInt()
String()
undefined
unescape()

JS JSON
parse()
stringify()

JS Math
abs()
acos()
acosh()
asin()
asinh()
atan()
atan2()
atanh()
cbrt()
ceil()
clz32()
cos()
cosh()
E
exp()
expm1()
floor()
fround()
LN2
LN10
log()
log10()
log1p()
log2()
LOG2E
LOG10E
max()
min()
PI
pow()
random()
round()
sign()
sin()
sqrt()
SQRT1_2
SQRT2
tan()
tanh()
trunc()

JS Number
constructor
isFinite()
isInteger()
isNaN()
isSafeInteger()
MAX_VALUE
MIN_VALUE
NEGATIVE_INFINITY
NaN
POSITIVE_INFINITY
prototype
toExponential()
toFixed()
toLocaleString()
toPrecision()
toString()
valueOf()

JS OperatorsJS RegExp
constructor
compile()
exec()
g
global
i
ignoreCase
lastIndex
m
multiline
n+
n*
n?
n{X}
n{X,Y}
n{X,}
n$
^n
?=n
?!n
source
test()
toString()

(x|y)
.
\w
\W
\d
\D
\s
\S
\b
\B
\0
\n
\f
\r
\t
\v
\xxx
\xdd
\uxxxx

JS Statements
break
class
continue
debugger
do…while
for
for…in
for…of
function
if…else
return
switch
throw
try…catch
var
while

JS String
charAt()
charCodeAt()
concat()
constructor
endsWith()
fromCharCode()
includes()
indexOf()
lastIndexOf()
length
localeCompare()
match()
prototype
repeat()
replace()
search()
slice()
split()
startsWith()
substr()
substring()
toLocaleLowerCase()
toLocaleUpperCase()
toLowerCase()
toString()
toUpperCase()
trim()
valueOf()

Changelog

  • 2.1.7 — 2020-06-08 — Fixed stringifying null and undefined (thanks @gamesaucer)
  • 2.1.6 — 2020-04-10 — Fix backward compatibility bug (thanks @WillBartee)
  • 2.1.5 — 2019-12-02 — Fixed empty string key(n) return (@appy-one, thanks for reporting)
  • 2.1.2 thru 2.1.4 — 2019-11-17 — Upgrading and testing npm publish scripts
  • 2.1.1 — 2019-11-17 — npm publish cleanup
  • 2.1.0 — 2019-11-17 — Added back dot-property and associative-array syntax using ES6 Proxy
  • 2.0.0 — 2019-11-17 — Updated all the depdendencies, added ability to register as polyfill (thanks @dy)
  • 1.3.1 — 2018-03-19 — Resolves issue #32 (thanks, plamens)
  • 1.3.0 — 2016-04-09 — Possibly backward breaking if you were using experimental syntax Reverted experimental
    associative array and dot-property syntax. The API for Proxy changed with node.js v6.x which broke it. Then when
    I switched to the new syntax, it broke the EventEmitter functionality. Will restore once I know how to fix that.
  • 1.2.0 — 2016-04-09 — Atomic writes (thanks, mvayngrib)
  • 1.1.2 — 2016-01-08 — Resolves issue #17 (thanks, evilaliv3)
  • 1.1.1 — 2016-01-04 — Smarter associative array and dot-property syntax support
  • 1.1.0 — 2016-01-03 — Backward breaking if you used any of the non-standard methods. They are now all preceded with
    an underscore. Big upgrade for this version is experimental support for associative array and dot-property syntax.
  • 1.0.0 — 2016-01-03 — Fixed bug with empty string key (thanks, tinybike)
  • 0.6.0 — 2015-09-11 — Removed references to deprecated fs.existsSync() (thanks, josephbosire)
  • 0.5.2 — 2015-08-01 — Fixed defect where keys were not being updated correctly by removeItem() (thanks, ed69140)
  • 0.5.1 — 2015-06-01 — Added support for events
  • 0.5.0 — 2015-02-02 — Added JSONStorage class which allows you set and get native JSON
  • 0.4.1 — 2015-02-02 — More robust publishing/tagging (like Lumenize)
  • 0.4.0 — 2015-02-02 — Uses more efficient fs.statSync to set initial size (thanks, sudheer594)
  • 0.3.6 — 2014-12-24 — Allows usage without
  • 0.3.5 — 2014-12-23 — Fixed toString() for QuotaExceededError
  • 0.3.4 — 2013-07-07 — Moved CoffeeScript to devDependencies
  • 0.3.3 — 2013-04-05 — Added support for ‘/’ in keys by escaping before creating file names
  • 0.3.2 — 2013-01-19 — Renamed QuotaExceededError to QUOTA_EXCEEDED_ERR to match most browsers
  • 0.3.1 — 2013-01-19 — Fixed bug where it threw plain old Error instead of new QuotaExceededError
  • 0.3.0 — 2013-01-19 — Added QuotaExceededError support
  • 0.2.0 — 2013-01-03 — Added quota support
  • 0.1.2 — 2012-11-02 — Finally got Travis CI working
  • 0.1.1 — 2012-10-29 — Update to support Travis CI
  • 0.1.0 — 2012-10-29 — Original version

Flash. SharedObject.

Там, где нет DOM Storage, для offline-хранения используют flash-интерфейс . Он позволяет хранить самые разные объекты средствами Adobe Flash.

Пример ActionScript для работы с :

// создать/получить namespace storage
storage = SharedObject.getLocal("storage"); 

// записать данные name => Вася
storage.data.name="Вася";

// сохранить объект
storage.flush()

// перечислить свойства объекта
for (var name in storage.data) {
    trace(name + ":" + storage.data)
}

Чтобы работать с этим хранилищем из javascript, нужен способ коммуникации JS<->Flash.

В старых версиях Flash вызвать javascript можно через .

Передать значение во Flash можно установкой переменной flash-объекту. Эту переменную flash-ролик может считывать каждый кадр и предпринимать соответствующие действия.

Во Flash 8+ появился интерфейс , который позволяет как указывать AS-функцию для приема данных из JS, так и напрямую вызывать JS-метод.

Открыть рабочий пример передачи значения Flash <-> JS.

Код примера в ActionScript:

import flash.external.*;

// установить местную функцию recieveFromJS для приема данных
// от javascript-функции sendFromJS
ExternalInterface.addCallback("sendFromJS", null, recieveFromJS);

// Эта функция будет реагировать на sendFromJS
function recieveFromJS(text) {
    _root.theText.text = text; // .. и устанавливать текст в окошке
}

// Это действие, наоборот, отправляет данные в JS.
_root.button.onRelease = function() {
    // вызвать javascript-функцию recieveFromFlash
    ExternalInterface.call("recieveFromFlash", _root.theText.text);
    _root.theText.text = "";
}

Код примера в JS:

function recieveFromFlash(Txt) {
	document.getElementById('text').value = Txt;
}

function sendFromJS() {
    var value = document.getElementById('text').value
    
    var movie = (navigator.appName.indexOf("Microsoft")!=-1 ? window : document)    
    
    movie.sendFromJS(value);    

    document.getElementById('text').value = ''
}

Доступ к SharedObject ограничен роликами с того же домена.

Это принципиально отличается от Javascript, в котором доступ определяется адресом страницы а не скрипта, и делает возможным разного рода кросс-доменные трюки.

Ограничение по умолчанию на размер данных — в районе 100Kb, пользователь может уменьшить или увеличить его в специальном Flash-интерфейсе, который открывается при вызове ActionScript:

System.showSettings(1);

Во-первых, надо иметь Flash. Хранилище доступно только после инициализации Flash-ролика.

Много ошибок в различных версиях Flash затрагивают ExternalInterface, включая повреждение данных во время передачи JS->Flash.

Проще всего узнать о них:

http://google.ru/search?q=ExternalInterface+bug

Много работы над обходом багов провел Brad Neuberg для flash-хранилища в dojo:

  • dojox/storage — различные хранилища, включая flash
  • dojox/flash — кроссбраузерная js/flash коммуникация, необходима для dojox/storage

Examples #

If you’re interested in tracking changes made to a data object, you can add a listener to its event. Whenever anything changes in storage, that event fires. Here’s sample code to listen for saved changes:

We can take this idea even further. In this example we have an options page that allows the user to toggle a «debug mode» in the extension (implementation not shown here). Changes to this setting are immediately saved to sync storage by the options page and the background script uses to apply the setting as soon as possible.

Since service workers are not always running, Manifest V3 extensions sometimes need to asynchronously load data from storage before they execute their event handlers. To do this, the below snippet uses an async event handler that waits for the global to be populated before executing its logic.

Краткая история локального хранилища до HTML5

Вначале был только один Internet Explorer. По крайней мере, Майкрософт хотел, чтобы мир так думал. С этой целью в рамках Первой Великой Войны браузеров Майкрософт изобрел очень много вещей и включил их в свой браузер-который-завершил-войну — Internet Explorer. Одна из этих вещей была названа DHTML Behaviors, а одна из форм поведения называется userData.

UserData позволяет веб-странице хранить до 64 Кб данных на каждый домен в иерархической XML-подобной структуре. Доверенные домены, такие как интранет-сайты могут хранить в десять раз больше. И эй, 640 Кб должно быть достаточно для всех. IE не представил какой-либо способ изменить эти соглашения, поэтому нет способа увеличить объем доступной памяти.

В 2007 году Google запустила Gears, открытый плагин для браузера направленный на обеспечение дополнительных возможностей. Мы уже обсуждали Gears ранее в контексте обеспечения геолокации в Internet Explorer. Gears предоставляет API для встроенной базы данных на основе SQLite. После получения единственного разрешения от пользователя, Gears может хранить неограниченное количество данных для каждого домена в таблицах базы данных SQL.

В то же время Брэд Нойберг и другие продолжали вкалывать на dojox.storage чтобы обеспечить единый интерфейс для всех этих разных плагинов и API. На 2009 год dojox.storage может автоматически определить (и обеспечить универсальный интерфейс) Adobe Flash, Gears, Adobe AIR и ранний прототип хранилища HTML5, который был реализован только в старых версиях Firefox.

В обзоре этих решений есть закономерность: все они либо специфичны для одного браузера, либо полагаются на сторонние плагины. Несмотря на героические усилия, они все радикально различаются интерфейсами, имеют разные ограничения по хранению и предоставляют разные способы взаимодействия с пользователем. Вот проблема, которую в HTML5 необходимо было решить: обеспечить стандартный API, который изначально поддерживается несколькими браузерами без необходимости полагаться на сторонние плагины.

HTML Tutorial

HTML HOMEHTML IntroductionHTML EditorsHTML BasicHTML ElementsHTML AttributesHTML HeadingsHTML ParagraphsHTML StylesHTML FormattingHTML QuotationsHTML CommentsHTML Colors
Colors
RGB
HEX
HSL

HTML CSSHTML Links
Links
Link Colors
Link Bookmarks

HTML Images
Images
Image Map
Background Images
The Picture Element

HTML TablesHTML Lists
Lists
Unordered Lists
Ordered Lists
Other Lists

HTML Block & InlineHTML ClassesHTML IdHTML IframesHTML JavaScriptHTML File PathsHTML HeadHTML LayoutHTML ResponsiveHTML ComputercodeHTML SemanticsHTML Style GuideHTML EntitiesHTML SymbolsHTML EmojisHTML CharsetHTML URL EncodeHTML vs. XHTML

The localStorage Object

The localStorage object stores the data with no expiration date. The data
will not be deleted when the browser is closed, and will be available the next day, week, or year.

Example

// StorelocalStorage.setItem(«lastname», «Smith»);// Retrieve
document.getElementById(«result»).innerHTML = localStorage.getItem(«lastname»);

Example explained:

  • Create a localStorage name/value pair with name=»lastname» and value=»Smith»
  • Retrieve the value of «lastname» and insert it into the element with id=»result»

The example above could also be written like this:

// StorelocalStorage.lastname = «Smith»;// Retrieve
document.getElementById(«result»).innerHTML = localStorage.lastname;

The syntax for removing the «lastname» localStorage item is as follows:

localStorage.removeItem(«lastname»);

Note: Name/value pairs are always stored as strings.
Remember to convert them to another format when needed!

The following example counts the number of times a user has clicked a button.
In this code the value string is converted to a number to be able to increase the counter:

Example

if (localStorage.clickcount) {  localStorage.clickcount = Number(localStorage.clickcount) + 1;
} else {  localStorage.clickcount = 1;}
document.getElementById(«result»).innerHTML = «You have clicked the button » +
localStorage.clickcount + » time(s).»;

Объекты localStorage и sessionStorage

Работа с веб-хранилищами в JavaScript выполняется через объекты и . Объекты и находятся в глобальном объекте .

Они имеют одинаковый набор свойств и методов:

  • — получить значение элемента хранилища по ;
  • — установить для указанное значение (если в хранилище уже есть данные с этим ключом, то они будут переписаны);
  • — удалить элемент по ;
  • — удалить все элементы;
  • — получить элемент по его индексу (в основном используется для перебора);
  • — свойство, с помощью которого можно получить количество элементов в хранилище;

Примеры

1. Пример, в котором добавим элемент в веб-хранилище, получим его значение по ключу, а затем удалим его:

// 1 - добавим в LocalStorage элемент с ключом «bgColor» и значением «green»
localStorage.setItem('bgColor', 'green');
// 2 - получим значение по ключу «bgColor» и сохраним его в переменную «bgColor»
var bgColor = localStorage.getItem('bgColor');
// 3 - удалим элемент из хранилища по ключу
localStorage.removeItem('bgColor');

2. Пример, в котором удалим все элементы из хранилища:

localStorage.clear();

3. Пример, в котором перебём все элементы LocalStorage:

<div id="result"></div>
...
<script>
  var output = '';

  // localStorage.length - количество элементов в хранилище
  for (var i = 0, length = localStorage.length; i < length; i++) {
    // localStorage.key(i) - получение ключа элемента по его индексу
    output += 'Ключ: ' + localStorage.key(i) + "; Значение: " + localStorage.getItem(localStorage.key(i)) + ".&lt;br&gt;";
  }
  
  document.querySelector('#result').innerHTML = output;
</script>

4. Пример, в котором запишем JavaScript объект в LocalStorage:

// 1 - JavaScript объект
var obj = {
  prop1: 'value1',
  prop2: 'value2',
  prop3: 'value3'
  // ...
}
// 2 - Сохраним JavaScript объект в LocalStorage преобразовав его в строку JSON
localStorage.setItem('mykey', JSON.stringify(obj));

// ...

// 3 - Получим из LocalStorage значение по ключу «mykey», если он там есть (т.е. если его значение не равно null)
if (localStorage.getItem('mykey')) {
  // 4 - Получим из LocalStorage значение по ключу «mykey», преобразуем его с помощью метода JSON.parse в JavaScript объект
  obj = JSON.parse(localStorage.getItem('mykey'));
}

Из этого примера видно, что если вам нужно в LocalStorage записать объект, то одним из вариантов решения этой задачи может стать его сериализация в строку JSON с помощью функции .

5. Пример, в котором проверим поддерживает ли браузер веб-хранилище?

if (typeof(Storage) !== 'undefined') {
  // код для localStorage
} else {
  // браузер не поддерживает веб-хранилище
}

6. Пример, в котором покажем как можно добавить дату к записям, а затем её использовать для их очистки (например, удалять записи, если со времени её записи прошло уже более 30 дней):

// пример данных, которые мы получили из LocalStorage
var
  viewsItems = [
    {
      id: '1608467',
      title: '6.26" Смартфон Huawei Nova 5T 128 ГБ фиолетовый',
      timestamp: 1583020800000 // 01.03.2020
    },
    {
      id: '1348312',
      title: '6.1" Смартфон Huawei P30 128 ГБ синий',
      timestamp: 1585872000000 // 03.04.2020
    },
    {
      id: '1394820',
      title: '6.1" Смартфон Apple iPhone 11 128 ГБ черный',
      timestamp: 1586476800000 // 10.04.2020
    }
  ],
  newViewsItems = [];

// сформируем из массива viewsItems новый массив newViewsItems только из тех данных, timestamp которых не более 30 дней
for (var i = 0, length = viewsItems.length; i < length; i++) {
  if (viewsItems.timestamp > (Date.now() - 1000 * 60 * 60 * 24 * 30)) {
    newViewsItems.push(viewsItems);
  }
}

// например, если текущая дата 11.04.2020, то в массив newViewsItems попадут все элементы, кроме первого

// сохраним новый массив, очищенный от старых записей в LocalStorage
localStorage.setItem('viewsItems', JSON.stringify(newViewsItems));

JavaScript

JS Array
concat()
constructor
copyWithin()
entries()
every()
fill()
filter()
find()
findIndex()
forEach()
from()
includes()
indexOf()
isArray()
join()
keys()
length
lastIndexOf()
map()
pop()
prototype
push()
reduce()
reduceRight()
reverse()
shift()
slice()
some()
sort()
splice()
toString()
unshift()
valueOf()

JS Boolean
constructor
prototype
toString()
valueOf()

JS Classes
constructor()
extends
static
super

JS Date
constructor
getDate()
getDay()
getFullYear()
getHours()
getMilliseconds()
getMinutes()
getMonth()
getSeconds()
getTime()
getTimezoneOffset()
getUTCDate()
getUTCDay()
getUTCFullYear()
getUTCHours()
getUTCMilliseconds()
getUTCMinutes()
getUTCMonth()
getUTCSeconds()
now()
parse()
prototype
setDate()
setFullYear()
setHours()
setMilliseconds()
setMinutes()
setMonth()
setSeconds()
setTime()
setUTCDate()
setUTCFullYear()
setUTCHours()
setUTCMilliseconds()
setUTCMinutes()
setUTCMonth()
setUTCSeconds()
toDateString()
toISOString()
toJSON()
toLocaleDateString()
toLocaleTimeString()
toLocaleString()
toString()
toTimeString()
toUTCString()
UTC()
valueOf()

JS Error
name
message

JS Global
decodeURI()
decodeURIComponent()
encodeURI()
encodeURIComponent()
escape()
eval()
Infinity
isFinite()
isNaN()
NaN
Number()
parseFloat()
parseInt()
String()
undefined
unescape()

JS JSON
parse()
stringify()

JS Math
abs()
acos()
acosh()
asin()
asinh()
atan()
atan2()
atanh()
cbrt()
ceil()
clz32()
cos()
cosh()
E
exp()
expm1()
floor()
fround()
LN2
LN10
log()
log10()
log1p()
log2()
LOG2E
LOG10E
max()
min()
PI
pow()
random()
round()
sign()
sin()
sqrt()
SQRT1_2
SQRT2
tan()
tanh()
trunc()

JS Number
constructor
isFinite()
isInteger()
isNaN()
isSafeInteger()
MAX_VALUE
MIN_VALUE
NEGATIVE_INFINITY
NaN
POSITIVE_INFINITY
prototype
toExponential()
toFixed()
toLocaleString()
toPrecision()
toString()
valueOf()

JS OperatorsJS RegExp
constructor
compile()
exec()
g
global
i
ignoreCase
lastIndex
m
multiline
n+
n*
n?
n{X}
n{X,Y}
n{X,}
n$
^n
?=n
?!n
source
test()
toString()

(x|y)
.
\w
\W
\d
\D
\s
\S
\b
\B
\0
\n
\f
\r
\t
\v
\xxx
\xdd
\uxxxx

JS Statements
break
class
continue
debugger
do…while
for
for…in
for…of
function
if…else
return
switch
throw
try…catch
var
while

JS String
charAt()
charCodeAt()
concat()
constructor
endsWith()
fromCharCode()
includes()
indexOf()
lastIndexOf()
length
localeCompare()
match()
prototype
repeat()
replace()
search()
slice()
split()
startsWith()
substr()
substring()
toLocaleLowerCase()
toLocaleUpperCase()
toLowerCase()
toString()
toUpperCase()
trim()
valueOf()

Summary

Web storage objects and allow to store key/value in the browser.

  • Both and must be strings.
  • The limit is 5mb+, depends on the browser.
  • They do not expire.
  • The data is bound to the origin (domain/port/protocol).
Shared between all tabs and windows with the same origin Visible within a browser tab, including iframes from the same origin
Survives browser restart Survives page refresh (but not tab close)

API:

  • – store key/value pair.
  • – get the value by key.
  • – remove the key with its value.
  • – delete everything.
  • – get the key number .
  • – the number of stored items.
  • Use to get all keys.
  • We access keys as object properties, in that case event isn’t triggered.

Storage event:

  • Triggers on , , calls.
  • Contains all the data about the operation (), the document and the storage object .
  • Triggers on all objects that have access to the storage except the one that generated it (within a tab for , globally for ).

Итого

IndexedDB можно рассматривать как «localStorage на стероидах». Это простая база данных типа ключ-значение, достаточно мощная для оффлайн приложений, но простая в использовании.

Лучшим руководством является спецификация, текущая версия 2.0, но также поддерживаются несколько методов из 3.0 (не так много отличий) версии.

Использование можно описать в нескольких фразах:

  1. Подключить обёртку над промисами, например idb.
  2. Открыть базу данных:
    • Создание хранилищ объектов и индексов происходит в обработчике .
    • Обновление версии – либо сравнивая номера версий, либо можно проверить что существует, а что нет.
  3. Для запросов:
    • Создать транзакцию (можно указать readwrite, если надо).
    • Получить хранилище объектов .
  4. Затем для поиска по ключу вызываем методы непосредственно у хранилища объектов.
  5. Если данные не помещаются в памяти, то используйте курсор.

Демо-приложение:

Результат
index.html

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector