Хранение данных на клиенте. DOM Storage и его аналоги.
По мере того как web-странички превращаются в AJAX-приложения, им требуются все новые возможности.
Сложные выборки элементов DOM обеспечиваются некоторыми браузерами и почти всеми распространенными Javascript-фреймворками.
Кросс-доменные HTTP-запросы находят поддержку в стандартах и реализуются в новейших браузерах, включая Internet Explorer 8.
В этой статье пойдет речь о средствах для хранения большого количества данных на клиенте, в браузере, которые доступны уже сейчас.
В частности, Internet Explorer 5+, Firefox 2+, Safari 2+ не требуют для этого дополнительных плагинов и Flash.
UPDATE: статья устарела и будет переписана.
Почти во всех браузерах есть поддержка cookies.
На протяжении долгого времени cookies были единственным кросс-браузерным способом сохранить данные, которые будут доступны после перезагрузки страницы.
Однако у cookie есть две важных особенности:
- Не более 2 килобайт данных
- Данные идут на сервер при каждом HTTP-запросе
Средства хранения на клиенте предусматривают сотни килобайт и мегабайты данных, и не отсылают их на сервер при каждом HTTP-запросе.
А cookie можно продолжать использовать, например, для хранения сессии.
Firefox реализует стандарт хранения "Client-side session and persistent storage of name/value pairs", предложенный в спецификации HTML 5.
Для постоянного хранения данных в нем используется объект window.globalStorage[домен] , операции над которым можно производить точно так же, как над обычным javascript-объектом. При уходе с сайта и даже закрытии браузера globalStorage не меняется, так что все его свойства можно прочитать обратно.
Например:
storage = globalStorage[document.domain]
// записать значение
storage['userName'] = 'Vasya'
// прочитать значение
alert(storage['userName'])
// удалить значение
delete storage['userName']
// получить все значения
for(var name in storage) {
alert(name + ':' + storage[name])
}
При чтении/записи на элементе body инициируется всплывающее событие storage .
Поймать его можно, например, таким обработчиком:
window.addEventListener('storage', function(event) { ... })
Стандарт HTML 5 все еще в процессе развития. В старой редакции прочитанные значения имели тип StorageItem .
Версия Firefox 2.0.0.13 возвращает при чтении объект именно этого типа.
Из текущей редакции StorageItem убран. В ней возвращаемые хранилищем значения имеют более простой тип DOMString .
..А пока эти изменения не учтены разработчиками, рекомендуется преобразовать значения к String явным образом.
Например:
var test = "12345"
storage.test = test // сохранить -> String
test = storage.test // прочитать <- StorageItem
alert(test.length) // undefined, это же не строка
alert(test.constructor) // StorageItem
test = String(test) // сделали строку. Теперь все ок.
Ограничения на данные: ключи и значения - только строки.
Размер: 5MB на домен.
Ограничения безопасности - точно такие же, как на cookie.
Данные в globalStorage['site.ru'] можно сохранить только на самом site.ru , а прочитать - на blog.site.ru , но не на otherhost.ru .
Internet Explorer 8 реализует DOM Storage, в то время как версии начиная от 5й поддерживают собственный интерфейс: userData behavior.
Он работает посредством выделенного DOM-элемента, которому назначается behavior userData. В этот элемент загружается нужное пространство имен, и данные становятся доступны через атрибуты.
<span id="storageElement"></span>
<script>
storage = document.getElementById('storageElement')
if (!storage.addBehavior) {
alert("userData not available.")
} else {
// поставить userData behavior
storage.addBehavior("#default#userData")
// загрузить пространство имен
storage.load("namespace")
}
</script>
После инициализации можно работать с данными. Для записи изменений используется метод save .
function put(key, value) { // записать значение
storage.setAttribute(key, value)
storage.save("namespace")
}
function get(key) { // получить значение
return storage.getAttribute(key)
}
function remove(key) { // удалить значение
storage.removeAttribute(key)
storage.save("namespace")
}
Как это часто бывает с Internet Explorer, некоторые операции делаются неочевидным образом.
Так, например, получить все сохраненные данные из storage.attributes нельзя. Там хранятся только атрибуты самого HTML-элемента.
Данные же хранятся в свойстве storage.XMLDocument.documentElement.attributes .
Например, следующий код создает список вида ключ:значение.
var list = []
var attrs = storage.XMLDocument.documentElement.attributes
for(var i=0; i<attrs.length; i++) {
list.push(attrs[i].name+':'+attrs[i].value);
}
В отличие от DOM Storage, можно задать атрибут expires . Он устанавливается на уровне всего элемента и действует на все хранящиеся данные. Очистка данных происходит при вызове load .
var time = new Date(); // Start Time
time.setMinutes(time.getMinutes() + 1)
storage.expires = time.toUTCString();
Ключи и значения - только строки.
Способ работает при всех уровнях безопасности, кроме "Высокого".
При этом для сайтов в зоне Internet объем ограничен 128K на страницу и 1024K на домен, для локальных и интранет - лимит увеличен.
Ограничения безопасности - та же директория, тот же домен и протокол.
Дальше всех в поддержке стандарта хранения пошли разработчики WebKit.
В Safari реализовано локальное хранение в базе данных SQLite.
Набор операций включает в себя CREATE TABLE, INSERT, SELECT, REPLACE, индексы и многое другое, с рядом ограничений безопасности (например, нет LOAD DATA INFILE).
В отличие от DOM Storage и userData, этот интерфейс асинхронный. Все функции запросов к базе данных принимают в качестве аргументов две функции: callback - для обработки результатов и errback - для обработки ошибок.
Когда запрос завершается, вызывается один из этих обработчиков.
Продемонстрируем это на тестовой базе.
db = openDatabase("Test", "1.0", "Webkit Storage Example")
db.transaction(function(tx) {
tx.executeSql(
"CREATE TABLE IF NOT EXISTS test (key TEXT, value TEXT, unique(key))",
[],
function(tx, result) { alert("Success!") },
function(tx, error) { alert("Failure: "+error.message }
)
})
Сложновато с первого взгляда?
db.transaction создает транзакцию и передает ее функции-аргументу.
Код внутри function(tx) выполняется в одной транзакции.
Вызов tx.executeSql принимает аргументы:
- Запрос
- Аргументы подстановки
- Обработчик результата
- Обработчик ошибки
Следующий пример демонстрирует обработку запроса.
db.transaction(function(tx) {
tx.executeSql("SELECT value FROM test WHERE key=?", [key],
function(tx,result) {
alert("Количество результатов: "+result.rows.length)
alert("Поле value первого результата: "+ result.rows.item(0).value)
},
function(tx, error) { alert("Error!") }
)
})
Стандарт SQL-хранения также включает в себя поддержку версий схемы, указание размера базы данных в openDatabase и многое другое. Может существовать только одна версия схемы одновременно.
База существует только в рамках домена(полного домена, origin), на котором была создана. Поддомен не имеет доступа к базе домена.
На момент написания статьи разработчики WebKit планировали поддержку DOM Storage, но в nightly build ее не было.
На момент написания статьи Opera 9.5 (beta) не поддерживает ни DOM Storage ни Database Storage.
С другой стороны, разработчики планируют эту поддержку включить.
Там, где нет DOM Storage, для offline-хранения используют flash-интерфейс SharedObject . Он позволяет хранить самые разные объекты средствами Adobe Flash.
Пример ActionScript для работы с SharedObject :
// создать/получить namespace storage
storage = SharedObject.getLocal("storage");
// записать данные name => Вася
storage.data.name="Вася";
// сохранить объект
storage.flush()
// перечислить свойства объекта
for (var name in storage.data) {
trace(name + ":" + storage.data[name])
}
Чтобы работать с этим хранилищем из javascript, нужен способ коммуникации JS<->Flash.
В старых версиях Flash вызвать javascript можно через getURL('javascript:...') .
Передать значение во Flash можно установкой переменной flash-объекту. Эту переменную flash-ролик может считывать каждый кадр и предпринимать соответствующие действия.
Во Flash 8+ появился интерфейс ExternalInterface , который позволяет как указывать 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)["BridgeMovie"]
movie.sendFromJS(value);
document.getElementById('text').value = ''
}
Скачать исходники
Документация на ExternalInterface
Доступ к SharedObject ограничен роликами с того же домена.
Это принципиально отличается от Javascript, в котором доступ определяется адресом страницы а не скрипта, и делает возможным разного рода кросс-доменные трюки.
Ограничение по умолчанию на размер данных - в районе 100Kb, пользователь может уменьшить или увеличить его в специальном Flash-интерфейсе, который открывается при вызове ActionScript:
System.showSettings(1);
Во-первых, надо иметь Flash. Хранилище доступно только после инициализации Flash-ролика.
Много ошибок в различных версиях Flash затрагивают ExternalInterface, включая повреждение данных во время передачи JS->Flash.
Проще всего узнать о них:
Много работы над обходом багов провел Brad Neuberg для flash-хранилища в dojo:
- dojox/storage - различные хранилища, включая flash
- dojox/flash - кроссбраузерная js/flash коммуникация, необходима для dojox/storage
DOM Storage и аналогичные системы хранения - важный шаг к offline-работе web-приложений.
DOM Storage поддерживается всеми современными браузерами.
Интересным сектором для интеграции могут быть CRM-системы с нестабильным интернетом. Например, мини-точка продаж, в которой основная работа идет через интеренет, но хранение текущих договоров дублируется на рабочей машине.
Offline-хранилище может быть использовано для сохранения сложных состояний интерфейса - размеров окон, контрольных элементов и т.п.
Все изменения интерфейса посетителем мгновенно сохранятся в DOM Storage и восстановятся при следующем заходе на страницу без дополнительных механизмов сохранения интерфейса на сервере.
При этом объем хранимого состояния интерфейса вряд ли превысит ограничение в сотни килобайт. Идеальный объект для DOM Storage, не правда ли ?..
|
dojo svn, похоже, закрылся или переместился. Поменял ссылку на путь внутри dojo.
Данные, в globalStorage['site.ru'] можно сохранить только на самом site.ru, а прочитать - на blog.site.ru...
В FF3 данную возможность убрали по соображениям безопастности.
у меня в FF3 не сработал пример с хранилищем
Мне на запись
Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17 выдал ошибку Security error" code: "1000
Примеры на сайте работают в FF3 и FF2 у меня (FF2,3 под Linux).
storage.load("namespace") - на это строке выкидывает ошибку "разрешение отклонено"
DOM Storage и аналогичные системы хранения - важный шаг к offline-работе web-приложений.
Странная эта штука - DOM Storage - для одного НТML-документа
в Mozilla своя память, в IE-своя и, находясь на одной машине, друг друга не видят.
Можно ли в среде НТML/JS писать/читать определенный
(текстовый) файл, как это делается, например, в Си (Си++)?
Вообще, как подать параметры/данные из одного НТML-документа
в другой? Можно ли клиент-скрипт написать на Си (Си++)?
Уж если я правильно понял, то суть проблемы описываемой в статье, это хранение данных(объемом большим чем позволенный в cookies) на клиенте, ну и так сказать наиболее удобный для этого подход.
Как выше говорилось, почему бы не использовать google gears?
Ну и насколько я знаю, есть вот такое чудо, TaffyDB, которое решает все проблемы кроссбраузерности при этом предоставляя удобный интерфейс для работы с данными.
Возможно неправильно понял суть, поправьте...
А какие ограничения на название ключа? Например () нельзя использовать, а что еще?
Ещё ссылки на некоторые библиотеки для работы с локальными хранилищами:
YUI 2: Storage Utility
The Dojo Offline Toolkit
iLocalStorage
jStore
или я дурак или лыжи не едут. нет у меня в FireFox в версии 3.0.13 никакого globalStorage. В итоге кроме ошибки ничего данные скрипты не дают.
А в ишаке работает!
К альфе Opera 10.50 таки прикрутили DOM Storage и Database Storage.
Ну и новость!
Братан, тебе надо переписать статью.
Выкинуть все из статьи и описать window.localStorage.
Так как эта штука поддерживается FF 3.6, Opera 10.5, IE 8.0, Сhrome 4.0, Safari 4.0. Сейчас проверил, работает window.localStorage во всех этих браузерах. А для старых IE 6-7 оставить твое описание userData.
респект, мамбет!
psкак однако полезно читать каменты (иногда)
>все еще приходится использовать нужен
поправьте или не поправляйте. как хотите.
Здравствуйте, уважаемые!
Я намереваюсь использовать DOM Storage (Firefox) для хранения небольшой базы данных своего скрипта для Greasemonkey.
Задался вопросом: "А где же все эти данные находятся физически?"
У меня будет около 300-от записей (в каждой от 5-ти до 10-ти "ячеек").
Хотелось бы обнаружить этот файлик и сделать его резервную копию.
Это возможно?
Нашел:
C:\Documents and Settings\Администратор\Application Data\Mozilla\Firefox\Profiles\s6v6hjub.default\webappsstore.sqlite
Так, ребят, скаже чесно, в данном вопросе я вобще не бум бум, но понятно вещь нужная.
есть один ресурс, тяжёлый слегка, так вот , как и что именнно надо сделать, чтобы Опера стала его воспринемать как "хранилище" ??
какой скрипт там, тег , код или что ?
если не затруднит ответ по почте !!
спасибо за ранее.
P.S. www.vesti.ru - тоже не файл обменник и не хост, а Опера его видит как хранилище, и после этого открывает легче и быстрее.
В крайнем драфте html5 storage остаются sessionStorage и localStorage. Обе работают в FF а в остальном печально.
Вообще, нужно сначала думать, прежде чем браться сохранять интерфейс в локальном хранилище. Поскольку если пользователь зайдет на сайт с другой тачки, весь его любимый и привычный интерфейс работать не будет.
По этой причине я не вижу другого смысла в локальном хранении данных, кроме как для поддержки оффлайновой работы сайта. Может меня кто-нибудь поправить?
мануал по localStorage на MSDN:
http://msdn.microsoft.com/ru-ru/library/cc197062(v=vs.85).aspx
На Firefox 13.0a1 вдруг перестала работать globalStorage
консоль пишет: Error: globalStorage is not defined
и такой код уже не работает:
в чем причина и чем заменить globalStorage
На Firefox 13.0a1 вдруг перестала работать globalStorage
"Use of globalStorage is deprecated. Please use localStorage instead"
Firefox (Gecko) не стандарт. В их руководстве так прямо и сказано. Одно удивляет:- воткнул скрипт что писал для firefox а он полностью работоспособен в опере приятно было.
Вот чего получилось, если требуется независимая переменная
почему в стати указан размер cookies 2 kb
"Однако у cookie есть две важных особенности:
Не более 2 килобайт данных"
в примере ошибка, если кому вдруг пригодится, вот рабочий пример:
db = openDatabase("Test", "1.0", "Webkit Storage Example"," 5 * 1024 * 1024")
db.transaction(function(tx) {
tx.executeSql(
"CREATE TABLE IF NOT EXISTS test (key TEXT, value TEXT, unique(key))",
[],
function(tx, result) { alert("Success!") },
function(tx, error) { alert("Failure: "+error.message) }
)
})