Локальное хранилище HTML5 подобно куки, которое сидит на стероидах - это очень простой в использовании инструмент и, вместе с тем, очень мощный. В данном уроке мы создадим функционал "липких заметок", которые позволят посетителям использовать постоянные заметки при просмотре сайта.
Шаг 1: HTML
Так как сущность данного проекта является динамической, то HTML кода обычной разметки будет мало. Мы просто имитируем веб страницу располагая на ней заполняющий контент:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>HTML 5 complete</title>
<link rel="stylesheet" href="default.css" />
<link rel="stylesheet" href="stickies/stickies.css" />
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<article>
<header>
<h1> Демонстрационная статья (язык: латынь)</h1>
</header>
<p>Lorem ipsum dolor. . . </p>
<!-- несколько параграфов с текстом на латыни (просто для примера) . . . -->
<footer>
<p>Демонстрация для сайта RUSELLER.COM</p>
</footer>
</article>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
<script src="json2.js"></script>
<script src="stickies/stickies.js"></script>
<script>
</script>
</body>
</html>
Есть несколько важных замечаний: мы включили два CSS файла - первый является простым стилем для страницы с именем default.css, второй предназначен для формирования липких заметок и называется stickies.css (он располагается в папке “stickies”). Внизу мы включаем скрипты:
jQuery из хранилища CDN Google
JQuery UI из хранилища CDN Google
JSON2 с сайта
Douglas Crockford
Наш собственный stickies.js, который расположен в папке “stickies”
В самом конце добавлен пустой тег script, который будет использоваться для запуска механизма.
Вот и весь HTML код!
Шаг 2: CSS
Содержание default.css очень простое:
body {
margin:0;
padding:0;
background:#ccc;
font:14px/1.5 "Helvetica Neue", Helvetica, Arial, san-serif;
}
article, footer, header { display: block; }
article {
width:880px;
background:#fff;
margin:auto;
padding:40px;
}
article header {
color:#474747;
border-bottom:1px solid #474747
}
article footer {
font-size:90%;
color:#ccc;
}
Есть еще CSS файл stickies.css, но для него еще нет разметки. Поэтому мы приступим к разбору JavaScript кода, а по его завершении вернемся к CSS для заметок.
Шаг 3: JavaScript
Вот скелет нашего приложения JavaScript:
var STICKIES = (function () {
var initStickies = function () {},
openStickies = function () {},
createSticky = function (data) {},
deleteSticky = function (id) {},
saveSticky = function () {},
markUnsaved = function () {};
return {
open : openStickies,
init : initStickies
};
}());
Мы используем несколько интересных техник. Первое, данная функция является самовызываемой. На первый взгляд может показаться, что мы назначаем функцию переменной STICKIES, но если взглянуть в конец кода функции, то вы увидите, что она выполняется прямо здесь. Подсказка: указанием на то, что это необычная функция, является факт заключения всего кода в круглые скобки. Таким образом, STICKIES является не функцией, а возвращаемым из данной функции значением - в нашем случае объектом.
Другая используемая техника программирования:
замыкание. Несмотря на то, что мы создаем шесть функций, только две из них видны пользователю (в действительности для нашего примера необходима только одна из них, но если мы планируем обеспечить поддержку создания заметок на любом сайте, то надо сделать видимыми createSticky и deleteSticky). Мы сможем использовать другие функции, которые были определены, хотя самовызываемая функция закончит выполнение до того, как начнется использование открытых методов.
Теперь перейдем к содержанию функций.
initStickies
Начнем с просмотра функции initStickies:
var initStickies = function initStickies() {
$("<div />", {
text : "+",
"class" : "add-sticky",
click : function () { createSticky(); }
}).prependTo(document.body);
initStickies = null;
},
Код очень простой. Мы используем jQuery, чтобы создать элемент. Используется специальный синтаксис версии 1.4: объект передается литерально с указанием свойств элемента в качестве второго параметра функции jQuery. Здесь мы делаем кнопку для создания новой заметки. Нам нужен новый div, мы устанавливаем текст ”+” и задаем класс “add-sticky”. Затем мы устанавливаем функцию обработчик события click для вызова метода createSticky (важно вызвать createSticky внутри функции, а не устанавливать в качестве непосредственного обработчика события click, так как createSticky получает единичный параметр и нам не нужно, чтобы это был объект события). Затем мы добавляем данный div в начало секции body документа. В конце функции мы устанавливаем initStickies в значение null. Таким образом мы избавляемся от только что выполненной функции. Так гарантируется выполнение функции только один раз, потому что нужно предотвратить создание нескольких кнопок "добавить заметку" на странице пользователем нашего API.
openStickies
Рассмотрим следующий метод openStickies:
openStickies = function openStickies() {
initStickies && initStickies();
for (var i = 0; i < localStorage.length; i++) {
createSticky(JSON.parse(localStorage.getItem(localStorage.key(i))));
}
},
Начинается выполнение функции initStickies … , но что это за странная синтаксическая конструкция? Вероятно, вы знакомы с оператором && : логическое И. Обычно его используют при создании множественных условий в выражении if. В действительности он делает следующее: вычисляет первое выражение, и если оно имеет значение true, то оператор && вычисляет второе выражение. В данном случае, если initStickies еще не установлен в значение null, то выполняется функция инициализации. Таким образом мы избегаем ошибки, которая возникает при попытке запустить функцию через переменную, в которой установлено значение null.
Затем организован цикл по всем элементам в локальном хранилище. Вот что происходит в цикле:
localStorage.key()- это великолепная функция, которая возвращает имя ключа для значения localStorage. Она получает номер в качестве параметра. Это отличный способ для организации цикла по всем элементам в localStorage.
Как только мы получили ключ для сохраненного элемента, передаем его функции localStorage.getItem(), чтобы получить значение.
Затем, полученное значение передается в функцию JSON.parse() из библиотеки мистера Crockford. Так как мы храним несколько значений для одной заметки, то для преобразования объекта в строку JSON используется функция JSON.stringify() там, где мы сохраняем заметки в локальном хранилище. Здесь происходит конвертация строки в объект.
В заверении мы передаем объект в функцию createSticky(), которая превращает его в "липкую заметку".
createSticky
Теперь разберем метод createSticky.
createSticky = function createSticky(data) {
data = data || { id : +new Date(), top : "40px", left : "40px", text : "Пишем заметку здесь" }
return $("<div />", {
"class" : "sticky",
'id' : data.id
})
.prepend($("<div />", { "class" : "sticky-header"} )
.append($("<span />", {
"class" : "status-sticky",
click : saveSticky
}))
.append($("<span />", {
"class" : "close-sticky",
text : "Удалить",
click : function () { deleteSticky($(this).parents(".sticky").attr("id")); }
}))
)
.append($("<div />", {
html : data.text,
contentEditable : true,
"class" : "sticky-content",
keypress : markUnsaved
}))
.draggable({
handle : "div.sticky-header",
stack : ".sticky",
start : markUnsaved,
stop : saveSticky
})
.css({
position: "absolute",
"top" : data.top,
"left": data.left
})
.focusout(saveSticky)
.appendTo(document.body);
},
Да, он длинный, но не очень сложный. Отметим, что данный метод получает объект data, как видно в разборе метода openStickies. Таким образом мы передаем сохраненные данные в метод. Однако, если если ничего не передается в метод (то есть мы создаем совершенно новую заметку), будет создан объект заметки по умолчанию. Таким образом все заметки создаются в одной точке, и создание каждой заметки начинается с определения конфигурации. Заметьте, что для id используется +new Date(). Предшествующий унарный оператор сложения конвертирует дату, полученную с помощью new Date(), в число, таким образом данное выражение представляет собой количество миллисекунд с 1 января 1970 до текущего момента. Очевидно, что данное число постоянно меняется. Таким образом мы получаем отличный способ задать уникальный идентификатор для каждой заметки.
Остальная часть функции является длинной строкой связанных в цепочку методов jQuery. Перед тем, как разобраться с процессом представленной функции, отметим, что будет возвращаться в качестве результата. Если мы собираемся представлять данный метод для использования нашего маленького API другими разработчиками, то функция должна возвращать ссылку на элемент div заметки.
Итак, вот что происходит:
Сначала мы создаем div, который является оболочкой для заметки. С помощью удобного синтаксиса jQuery 1.4 мы устанавливаем ему класс “sticky” и id из объекта data.
Затем, перед созданным только что элементом добавляем еще один div, который получает класс“sticky-header”. В div.sticky-header затем добавляются два тега span. Первый, span.sticky-status, получает в качестве обработчика события click функцию saveSticky. Нужно отметить, что в действительности данный span выводит статус заметки: сохранена или не сохранена. Существует несколько способов для сохранения заметкой данных в localStorage. Возможно пользователь захочет, чтобы нажатие на ‘не сохранено’ сохраняло данные заметки. Поэтому мы предоставляем ему такую возможность. Второй span.close-sticky будет кнопкой для удаления: когда пользователь нажмет на нее, заметка будет удалена из localStorage с помощью метода deleteSticky. Методы передается id заметки.
Затем мы добавляем еще один div к основному div.sticky. Заметьте, что мы присваиваем свойству html значение data.text. Когда мы сохраняем текст заметки, используется метод jQuery’s html(), потому что метод text() выбрасывает из текста символы окончания строки. Также мы устанавливаем contentEditable:true для данного div, потому что содержимое заметки можно редактировать. Данный div получает класс sticky-content. В завершение, когда нажимается любая клавиша на данном div (что означает редактирование содержимомго заметки пользователем), нужно отметить заметку как несохраненную, для чего вызвается соответствующая функция.
Теперь используем метод jQuery UI draggable, чтобы сделать нашу заметку перемещаемой. В объекте парметров устанавливаем свойство handle, чтобы перемещать заметки можно было только за заголовок. Свойство stack содержит селектор перемещаемого элемента, установка данного значения выводит перемещаемый элемент поверх всего контента. При перемещении заметки, мы отмечаем ее как "не сохраненную" (потому что меняются координаты, и их нужно будет сохранить), а когда перемещение заканчивается, вызываем функцию сохранения.
Затем устанавливаем стили для div.sticky. Он позиционируется абсолютно, а значениям свойств top и left присваиваются соответствующие значения из объекта data. Таким образом будет сохраняться позиция заметки, так же как и ее содержание, при обновлении страницы.
В завершении, устанавливается обработчик события focusout для заметки: нужно сохранять заметку.
Последняя строка добавляет новую заметку к документу.
Вот такая структура HTML кода должна генерироваться:
<div class="sticky ui-draggable" id="1281194825332" style="position: absolute; top: 40px; left: 40px;">
<div class="sticky-header">
<span class="sticky-status"></span>
<span class="close-sticky">trash</span>
</div>
<div contenteditable="true" class="sticky-content">
Note Here
</div>
</div>
deleteSticky
Теперь разберем код функции deleteSticky:
deleteSticky = function deleteSticky(id) {
localStorage.removeItem("sticky-" + id);
$("#" + id).fadeOut(200, function () { $(this).remove(); });
},
Функция deleteSticky получает в качестве параметра id заметки, которую надо удалить. Используется метод localStorage.removeItem(): мы передаем ему ключ для значения в локальном хранилище чтобы удалить пару ключ-значение (при сохранении данных заметки мы добавляем “sticky-” перед id). Затем находим элемент с заданным id в структуре документа, выключаем его, и удаляем.
saveSticky
Это самая важная функция. saveSticky является клеем, который все соединяет в единое целое.
saveSticky = function saveSticky() {
var that = $(this), sticky = (that.hasClass("sticky-status") || that.hasClass("sticky-content")) ? that.parents('div.sticky'): that,
obj = {
id : sticky.attr("id"),
top : sticky.css("top"),
left: sticky.css("left"),
text: sticky.children(".sticky-content").html()
}
localStorage.setItem("sticky-" + obj.id, JSON.stringify(obj));
sticky.find(".sticky-status").text("сохранено");
},
Первая строчка функции выполняет операцию определения ссылки на div.sticky: существует три элемента, из которых она может быть вызвана. Сначала мы преобразуем с помощью jQuery this (оборачиваем в $()) и присваиваем его переменной that. Если элемент имеет класс либо “sticky-status” либо “sticky-content”, то получаем родительский div.sticky. Если элемент не имеет никакого из указанных классов, то это сам div.sticky, и мы будем использовать его.
Затем надо получить значения, которые мы хотим сохранять. Мы получаем id, смещение от левого верхнего угла окна, и HTML потомка .sticky-content (помните, что мы используем html() вместо text(), потому что мы хотим сохранить символы конца строки). Затем мы используем метод localStorage.setItem для сохранения данных. Он принимает два параметра: ключ и значение для хранения. Так как localStorage хранит только строки, мы используем JSON.stringify() для конвертации объекта в строку.
В завершении изменяем статус заметки на “сохранено”.
Последняя функция, которая выполняет вспомогательные функции:
markUnsaved = function markUnsaved() {
var that = $(this), sticky = that.hasClass("sticky-content") ? that.parents("div.sticky") : that;
sticky.find(".sticky-status").text("не сохранено");
}
Здесь снова выполняется определение ссылки на div.sticky. Как только мы это сделаем, то можно будет просто найти элемент span статуса и установить текст “не сохранено”.
Код JavaScript готов.
Шаг 4: CSS, остальная часть
Теперь мы знаем, какая разметка будет в заметки и можем задать стили для нее. Все очень просто:
:focus {
outline:0;
}
.add-sticky {
cursor: default;
position:absolute;
top:1px;
left:1px;
font-size:200%;
background:#000;
color:#fff;
border:2px solid #fff;
border-radius:40px;
-webkit-border-radius:40px;
-moz-border-radius:40px;
text-align:center;
line-height:25px;
width:30px;
height:30px;
}
.add-sticky:hover {
background: #474747;
}
.sticky {
width:300px;
background:#fdfdbe;
box-shadow:3px 3px 10px rgba(0,0,0,0.45);
-webkit-box-shadow:3px 3px 10px rgba(0,0,0,0.45);
-moz-box-shadow:3px 3px 10px rgba(0,0,0,0.45);
}
.sticky-content {
min-height:150px;
border-left:3px double rgba(238, 150, 122, .75);
margin-left:30px;
padding:5px;
}
.sticky-header {
padding:5px;
background:#f3f3f3;
border-bottom:2px solid #fefefe;
box-shadow:0 3px 5px rgba(0,0,0,0.25);
-webkit-box-shadow:0 3px 5px rgba(0,0,0,0.25);
-moz-box-shadow:0 3px 5px rgba(0,0,0,0.25);
}
.sticky-status {
color:#ccc;
padding:5px;
}
.close-sticky {
background:#474747;
float:right;
cursor:default;
color:#ececec;
padding:1px 5px;
border-radius:20px;
-webkit-border-radius:20px;
-moz-border-radius:20px;
}
Здесь есть нескоолько интересных моментов:
Некоторые браузеры выводят обводку вокруг элементов, у которых установлено свойство contenteditable=true, воо время редактирования контента. Нам такое функционирование будет мешать, поэтому мы избавляемся от него с помощью объявления :focus.
Кнопка “Добавить заметку” позиционируется в верхний левый угол.
Мы используем свойства CSS3 border-radius и box-shadow (с соответствующими префиксами).
Также используется rgba() для определения цвета теней. Она принимает четыре параметра: значения красного, зеленого и синего цвета, а также значение альфа (прозрачность).
Все остальное является обычным CSS. Заметка должна выглядеть так:
Шаг 5: Запуск заметок в работу
Теперь, когда наш API готов, можно запустить его в работу на сайте. Можно сделать это из дополнительного пустого тега script в нашем файле index.html:
STICKIES.open();
Готово!