localStorage на примере уведомления пользователя о дате посещения ресурса (User visit notification)
Опубликовано Romanzhivo - 13 июня 2016, 05:06
Если вам понадобилось уведомить пользователя о дате и времени последнего посещения им сайта, а писать полноценный механизм с сохранением в базу данных и извлечением даты и времени нет желания, то на помощь придёт локальное хранилище — localStorage. Данная статья посвящена описанию localStorage и обсуждению практического примера — скрипта уведомления (User Visit Notification), который работает полностью на стороне клиента, а также обсуждению особенностей отдельных частей кода. Являясь новичком в JavaScript, я пытаюсь понять, как устроен язык, как его можно использовать и где граница соприкосновения разметки, дизайна и функционала.
Можно сразу перейти к демо или скачиванию.
Локальное хранилище, или localStorage
localStorage — это объект-свойство в языке Яваскрипт для глобального объекта Window, которое позволяет хранить до 5 мегабайт данных на протяжении сколь угодно длительного периода, то есть без ограничений по времени. В отличие от другого объекта-свойства — sessionStorage, — localStorage хранит данные даже после завершения сессии, то есть даже после закрытия вкладки или окна браузера. И localStorage, и sessionStorage являются частью Веб-хранилища (Web Storage API). Данные веб-хранилища доступны только для каждого домена отдельно, то есть, нельзя получить доступ со страниц одного сайта к содержимому localStorage или sessionStorage другого сайта.
Задавшись вопросом, как же в конечном итоге информация, записанная в объект localStorage, хранится на стороне клиента, я опубликовал этот вопрос на ru.stackoverflow.com и ребята подкинули ссылок на ответы по похожим вопросам, вот самый объективный. В целом, механизм хранения данных зависит от реализации браузера и он не всегда очевиден: в Chrome, по всей видимости, данные localStorage сохраняются в отдельном файле, а Firefox хранит данные в webappsstore.sqlite.
Конечно же, локальное хранилище не подходит для сохранения конфиденциальной информации о пользователях, такой как учётные данные или данные авторизации, однако для уведомления о последнем посещении сайта его вполне можно использовать. Если пользователь опытный, он, конечно, сможет удалить или изменить данные в localStorage, для этого достаточно из консоли вызвать у объекта localStorage один из его методов. Но в большинстве случаев локальное хранилище оказывается удобным для хранения мета-информации о документе или сессии, вроде фрагментов текста, языковых настроек, разметки или даже изображений, и переживать по поводу находчивого пользователя не стоит.
Методы и свойства localStorage
Данные в localStorage, как и в любой другой яваскрипт-объект, записываются в виде пары «ключ»: «значение», однако, в отличие от обычных объектов, у localStorage есть ряд особенностей. Например, значения свойств localStorage всегда имеют строковый тип данных, поэтому для получения числа обычно используется преобразование при помощи JS-функций parseInt() или Number(). Этой особенностью localStorage очень похож на JSON-объекты, поэтому для преобразования записываемых и извлекаемых данных можно использовать методы JSON.stringify() и JSON.parse(), вот предложенное одним участником на stackoverflow хорошее решение. Помимо этого, как известно, в JavaScript свойство length доступно только массивам, и объекты по умолчанию не имеют данного свойства, однако объект localStorage является исключением и ему доступно свойство length. Свойство length является единственным предустановленным свойством localStorage и единственным, которое хранит числовой тип данных. При этом, в отличие от нативных массивов Яваскрипта, для объекта localStorage это свойство является только читаемым (read only) и не доступно для изменения.
Прямо сейчас вы можете открыть консоль, нажав F12, и проверить, что хранится в localStorage.
Манипуляция данными происходит при помощи основных методов localStorage. Согласно документации, у localStorage существует всего 5 предустановленных методов и 1 свойство:
Методы localStorage | Свойства localStorage |
---|---|
.key() — принимает в качестве параметра целое число (индекс свойства-ключа) и возвращает имя свойства (ключа) в соответствии с принятым числом. Как и в массивах, нумерация начинается с нуля.
Параметры: индекс ключа Типы параметров: число |
length — единственное предустановленное свойство localStorage;
возвращает число — количество записанных в localStorage элементов |
.getItem() — принимает 1 параметр — имя свойства (ключа) и возвращает соответствующее значение.
Параметры: имя ключа Типы параметров: строка |
— |
.setItem() — принимает 2 параметра — имя ключа, значение; записывает пару «ключ: значение» в локальное хранилище.
Параметры: имя ключа, значение ключа Типы параметров: строка |
— |
.removeItem() — принимает в качестве единственного параметра имя ключа и удаляет соответствующий элемент (пару «ключ: значение») из localStorage.
Параметры: имя ключа Типы параметров: строка |
— |
.clear() — метод удаляет все ранее записанные элементы («ключ: значение») из локального хранилища; не принимает параметры и ничего не возвращает.
Параметры: нет Типы параметров: нет |
— |
Поскольку localStorage представляет собой js-объект, для него доступна обычная нотация при создании и чтении свойств (ключей). Это значит, что вместо методов .setItem() и .getItem() можно использовать следующие варианты записи
localStorage.свойство = "значение свойства";
или
localStorage["свойство"] = "значение свойства";
и чтения
localStorage.свойство
— вернёт «значение свойства».
или
localStorage["свойство"]
— также вернёт «значение свойства»
Кстати, пока писал статью, обнаружил любопытную вещь: оказывает, Яваскрипт поддерживает возможность записи названий переменных, функций, объектов, свойств объектов и т.д. на русском языке или на любом другом, использующем кириллические символы. И если вы скопируете пример выше в консоль браузера, то код будет работать 🙂
User Visit Notification
Теперь пример того, как можно использовать localStorage. Скрипт уведомления пользователя о последнем посещении сайта я назвал «User Visit Notification». На самом деле, не знаю, насколько часто может возникать задача уведомления пользователя на стороне клиента, однако скрипт может пригодиться в качестве учебного примера.
Для работы скрипта понадобятся:
- Библиотека jQuery версии 3.0.0 — на данный момент самая актуальная версия, однако скрипт точно будет работать с предыдущими версиями вплоть до 2.1.4 включительно и, скорее всего, будет работать на более ранних версиях;
- Разметка и стилевое оформление уведомления;
- Сам скрипт уведомления
Тело скрипта заключено в самовызывающуюся именованную функцию, которая имеет собственную область видимости и не попадает в глобальный объект, применительно к браузеру это означает, что функция не является свойством глобального объекта Window. Благодаря этому подключение скрипта не затронет глобальные переменные. Вызвать по имени такую функцию не получится, но при необходимости можно просто сделать её глобальной.
(function checkVisitTime() { var notifWrapp = $('.notif-wrapper'); var notifNotification = $('.notif-notification'); var notifOverlay = $('.notif-overlay'); var closeButton = $('.notif-close'); var dateTimeWrapp = $('.date-time'); var notifText = $('.notif-notification .text'); var notifDate = $('.date-time .date'); var notifTime = $('.date-time .time'); var actualTime = new Date(); if(actualTime.getSeconds() < 10) { var seconds = '0' + actualTime.getSeconds(); } else { var seconds = actualTime.getSeconds(); } if(actualTime.getMinutes() < 10) { var minutes = '0' + actualTime.getMinutes(); } else { var minutes = actualTime.getMinutes(); } if(actualTime.getHours() >= 10 && actualTime.getHours() <= 23) { var hours = actualTime.getHours(); } else { var hours = '0' + actualTime.getHours(); } if(actualTime.getDate() < 10) { var currentDate = '0' + actualTime.getDate(); } else { var currentDate = actualTime.getDate(); } if(actualTime.getMonth() + 1 < 10) { var currenMonth = '0' + (actualTime.getMonth() + 1); } else { var currenMonth = actualTime.getMonth() + 1; } var currentYear = actualTime.getFullYear(); var currentTime = hours + ':' + minutes + ':' + seconds; function showNotif() { notifWrapp.removeClass('notif-hide'); notifNotification.fadeIn(800); notifOverlay.fadeIn(800); var notifHeight = notifNotification.outerHeight(); notifNotification.css('top', -notifHeight).animate({ top: 0 }, 800); closeButton.click(function() { hideNotif(1); }); } function showFirstDate() { dateTimeWrapp.remove(); notifText.html('Вы у нас впервые :)'); showNotif(); hideNotif(); } function showLastDate(lastDate, lastMonth, lastYear, lastTime) { notifText.html('С возвращением! Мы Вас так заждались! <br /> Последний раз Вы заходили к нам'); notifDate.html(lastDate + '.' + lastMonth + '.' + lastYear); notifTime.html(lastTime); showNotif(); hideNotif(); } function hideNotif(close) { function hide() { var notifHeight = notifNotification.outerHeight(); notifNotification.animate({ top: -notifHeight }, 800, function() { notifWrapp.remove(); }); notifOverlay.fadeOut(800); } if(close === 1) { hide(); } else { setTimeout(function() { hide(); }, 5000); } } if(!localStorage.lastTime) { localStorage.lastDate = currentDate; localStorage.lastMonth = currenMonth; localStorage.lastYear = currentYear; localStorage.lastTime = currentTime; showFirstDate(); } else { if(localStorage.lastDate[0] == 0) { var lastDate = '0' + parseInt(localStorage.lastDate); } else { var lastDate = parseInt(localStorage.lastDate); } if(localStorage.lastMonth[0] == 0) { var lastMonth = '0' + parseInt(localStorage.lastMonth); } else { var lastMonth = parseInt(localStorage.lastMonth); } var lastYear = parseInt(localStorage.lastYear); if(lastDate != currentDate || lastMonth != currenMonth || lastYear != currentYear) { var lastTime = localStorage.lastTime; showLastDate(lastDate, lastMonth, lastYear, lastTime); } localStorage.lastDate = currentDate; localStorage.lastMonth = currenMonth; localStorage.lastYear = currentYear; localStorage.lastTime = currentTime; } })();
В самом начале функции мы определяем переменные — элементы разметки. Таким образом, можно сказать, что скрипт привязан к разметке и стилям.
var notifWrapp = $('.notif-wrapper'); var notifNotification = $('.notif-notification'); var notifOverlay = $('.notif-overlay'); var closeButton = $('.notif-close'); var dateTimeWrapp = $('.date-time'); var notifText = $('.notif-notification .text'); var notifDate = $('.date-time .date'); var notifTime = $('.date-time .time');
Особенности формата даты и времени в JavaScript
Далее — нам нужно определить переменные с датой и временем. Для начала создадим с помощью функции-конструктора Date() объект, который назовём, например, actualTime:
//Создаём с помощью функции-конструктора Date() новый объект actualTime, //в котором будут записаны текущая дата и время var actualTime = new Date();
Нативные методы вывода даты и времени в Яваскрипт имеют некоторые особенности: во-первых, подсчёт номера месяца осуществляется как и в массивах с ноля, то есть декабрь — это 11-й месяц в Яваскрипт. Во-вторых, если значение секунды, минуты, часа, даты или месяца меньше 10, то соответствующий метод — .getSeconds(), .getMinutes() и т.д. — вернёт просто однозначное число без цифры 0 впереди. К примеру, если сегодня 19 июня 2016 года, и мы вызовем actualTime.getMonth()
, то метод вернёт число 5 вместо ожидаемого значения 06.
Для того чтобы записать дату и время в традиционном формате, нужны некоторые преобразования:
//Корректируем вывод даты и времени, добавляя 0, если //значение секунды, минуты, часа, даты или месяца меньше 10 if(actualTime.getSeconds() < 10) { var seconds = '0' + actualTime.getSeconds(); } else { var seconds = actualTime.getSeconds(); } if(actualTime.getMinutes() < 10) { var minutes = '0' + actualTime.getMinutes(); } else { var minutes = actualTime.getMinutes(); } if(actualTime.getHours() >= 10 && actualTime.getHours() <= 23) { var hours = actualTime.getHours(); } else { var hours = '0' + actualTime.getHours(); } if(actualTime.getDate() < 10) { var currentDate = '0' + actualTime.getDate(); } else { var currentDate = actualTime.getDate(); } if(actualTime.getMonth() + 1 < 10) { var currenMonth = '0' + (actualTime.getMonth() + 1); } else { var currenMonth = actualTime.getMonth() + 1; } var currentYear = actualTime.getFullYear(); var currentTime = hours + ':' + minutes + ':' + seconds;
Теперь определим основные функции показа и скрытия уведомления:
//Определяем основные функции показа/скрытия уведомления function showNotif() { notifWrapp.removeClass('notif-hide'); notifNotification.fadeIn(800); notifOverlay.fadeIn(800); var notifHeight = notifNotification.outerHeight(); notifNotification.css('top', -notifHeight).animate({ top: 0 }, 800); closeButton.click(function() { hideNotif(1); }); } function showFirstDate() { dateTimeWrapp.remove(); notifText.html('Вы у нас впервые :)'); showNotif(); hideNotif(); } function showLastDate(lastDate, lastMonth, lastYear, lastTime) { notifText.html('С возвращением! Мы Вас так заждались! <br /> Последний раз Вы заходили к нам'); notifDate.html(lastDate + '.' + lastMonth + '.' + lastYear); notifTime.html(lastTime); showNotif(); hideNotif(); } function hideNotif(close) { function hide() { var notifHeight = notifNotification.outerHeight(); notifNotification.animate({ top: -notifHeight }, 800, function() { notifWrapp.remove(); }); notifOverlay.fadeOut(800); } if(close === 1) { hide(); } else { setTimeout(function() { hide(); }, 5000); } }
И наконец, проверим наличие записей и сами записи в локальном хранилище и выведем соответствующее уведомление:
//Проверяем содержимое локального хранилища if(!localStorage.lastTime) { //Если в localStorage нет записи lastTime - //добавляем записи текущей даты и времени localStorage.lastDate = currentDate; localStorage.lastMonth = currenMonth; localStorage.lastYear = currentYear; localStorage.lastTime = currentTime; //Показываем уведомление о первом посещении showFirstDate(); } else { //В ином случае извлекаем из localStorage дату и месяц последнегоо посещения, //преобразовываем их к нужному виду и переопределяем соответствующие переменные if(localStorage.lastDate[0] == 0) { var lastDate = '0' + parseInt(localStorage.lastDate); } else { var lastDate = parseInt(localStorage.lastDate); } if(localStorage.lastMonth[0] == 0) { var lastMonth = '0' + parseInt(localStorage.lastMonth); } else { var lastMonth = parseInt(localStorage.lastMonth); } //Извлекаем год последнего посещения и переопределяем lastYear var lastYear = parseInt(localStorage.lastYear); //Если хотя бы один параметр не соответствует текущей дате - //выводим уведомление с датой и временем последнего посещения if(lastDate != currentDate || lastMonth != currenMonth || lastYear != currentYear) { var lastTime = localStorage.lastTime; showLastDate(lastDate, lastMonth, lastYear, lastTime); } //Записываем в локальное хранилище текущие дату и время //в качестве даты и времени последнего посещения на будущее localStorage.lastDate = currentDate; localStorage.lastMonth = currenMonth; localStorage.lastYear = currentYear; localStorage.lastTime = currentTime; }
Пример с возможностью смены даты и очистки localStorage можно посмотреть по ссылке.
Буду благодарен за комментарии и отзывы.