Делаем постоянные липкие заметки с использованием локального хранилища HTML5

Локальное хранилище 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

Последняя функция, которая выполняет вспомогательные функции:

	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();

Готово! Смотрим демонстрационную страницу и исходный код.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: net.tutsplus.com/tutorials/html-css-techniques/building-persistant-sticky-notes-with-local-storage/
Перевел: Сергей Фастунов
Урок создан: 9 Сентября 2010
Просмотров: 27330
Правила перепечатки


5 последних уроков рубрики "Для сайта"

  • Эффекты блочного раскрытия

    Сегодня мы хотели бы поделиться несколькими идеями касающегося эффекта блочного раскрытия, которую вы можете использовать для своих проектов.

  • 15 полезных .htaccess сниппета для сайта на WordPress

    Если вы хотите существенно повысить уровень безопасности вашего сайта на WordPress, то вам не избежать конфигурации файла .htaccess. Это позволит не только уберечься от целого ряда хакерских атак, но и организовать перенаправления, а также решить задачи связанные с кэшем.

  • 20 бесплатных тем для WordPress в стиле Material Design

    Material Design — это набирающий обороты тренд от Google. В данной подборке собраны бесплатные темы для WordPress, выполненные в этом популярном стиле.

  • 20 сайтов с креативным MouseOver эффектом

    Эффекты на то и существуют чтобы впечатлять наших посетителей. В этой подборке собрано несколько десятков ресурсов, чьи создатели очень постарались впечатлить своих посетителей.

  • 45+ бесплатных материалов для веб дизайнеров за август 2016

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

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 9 Сентября 2010 14:52
    Dicky_forester
    Потрясающе! Этим можно воспользоваться для хранения переменных и значений в on-line расчетах? не загружая базу данных. Я правильно понял?
  • 9 Сентября 2010 16:27
    Kesha_php
    Хех... напишите статью как обновлять браузер простому юзеру... иначе эта статья про html5 не найдёт применения ещё долго время
  • 9 Сентября 2010 16:37
    Dicky_forester
    Кому надо тот обновит. Остальные обойдутся и без заметок, это не жизненно важно.
  • 9 Сентября 2010 21:51
    НеБот
    Креатив Гениален/Автор Молодец! Буду использовать! Kesha_php, +1! Пусть те, кто не знают, что такое Хром, Сафари, ФФ, или, на крайняк, Опера - пусть убйуцца апстену!
  • 13 Сентября 2010 19:26
    rubyx
    Хорошая примычка) мне нравится лично, её во всех "вебсферах" использовать можно... :)
  • 25 Декабря 2010 08:59
    makdoom
    Идея вдохновляет, но в примере если заметка попадает за верхнюю границу пространства страницы то вернуть ее не возможно так же как и закрыть ее (
  • 8 Марта 2011 11:42
    Sadzi
    А у меня не заработало :-( Просто обновляет странице и добавляет к index.php знак "#" и все :-(
  • 8 Марта 2011 12:29
    Sadzi
    Точнее у меня кодировка не та и все иероглифами какими то а стоит utf8 как исправить?
  • 16 Августа 2011 10:01
    Slonoritsar
    Класс!!!
  • 24 Января 2013 18:14
    johnkolyan88
    При обновлении страницы пропадает заметка - как исправить?
    • 9 Мая 2013 15:34
      ghbrjkbcn
      а у меня всё нормально там сетевое хранилище используется
  • 1 Декабря 2013 19:06
    aplutov
    Всё бы супер, но contenteditable еще очень сырой, вот один фикс в моем блоге - http://plutov.by/post/contenteditable_remove_styles
^ Наверх ^