Реалізація можливості додання стикерів для нотаток за допомогою веб-сховища
() translation by (you can also view the original English article)
(* локальне сховище, HTML5-сховище, DOM-сховище; вбудована можливість зберігання даних на стороні клієнта (об'єм даних значно перевищує той, що надається кукі). Тут і надалі примітка перекладача). Локальне сховище HTML5 – щось на зразок кукі на стероїдах; його надзвичайно легко використовувати, і водночас воно являє собою настільки потужну функціональну можливість. У цьому посібнику я покажу вам, як реалізувати можливість додання «стикерів для нотаток», завдяки якій у ваших користувачів з'являється можливість робити постійні довгочасні нотатки при перегляді вашого сайту.
Крок 1: HTML
Через динамічну природу цього проекту нам особливо нічого програмувати, стосовно звичайної старої семантичної розмітки. Ми просто будемо симулювати веб-сторінку завдяки зведенню докупи деякого контенту-наповнювача.
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
<head>
|
4 |
<meta charset='utf-8' /> |
5 |
<title>HTML 5 complete</title> |
6 |
<link rel="stylesheet" href="default.css" /> |
7 |
<link rel="stylesheet" href="stickies/stickies.css" /> |
8 |
<!--[if IE]>
|
9 |
<script src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
10 |
<![endif]-->
|
11 |
</head>
|
12 |
<body>
|
13 |
<article>
|
14 |
<header>
|
15 |
<h1> Sample Article Title</h1> |
16 |
</header>
|
17 |
<p>Lorem ipsum dolor. . . </p> |
18 |
<!-- a few lorem-ipsum paragraphs later . . . -->
|
19 |
<footer>
|
20 |
<p>Copyright 2010 Andrew Burgess</p> |
21 |
</footer>
|
22 |
</article>
|
23 |
|
24 |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> |
25 |
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script> |
26 |
<script src="json2.js"></script> |
27 |
<script src="stickies/stickies.js"></script> |
28 |
<script>
|
29 |
</script>
|
30 |
</body>
|
31 |
</html>
|
Тут варто звернути увагу на декілька важливих моментів. Ми додаємо дав CSS-файли: перший, який ми назвали default.css
, використовується для додання простого стильового оформлення для сторінки. Далі йде спеціальний CSS-файл для додання стильового оформлення для наших стікерів для нотаток під назвою stickies.css
, і як ви бачите він розташовується в папці “stickies”. У нижній частині документу ми додаємо чотири скрипта:
- jQuery з CDN (* content delivery network – мережа доставки (розповсюдження) контенту) Google
- JQuery UI з CDN Google
- JSON2 з сайту Дугласа Крокфорда
- Наш власний
stickies.js
, що розташовується в папці “stickies”
Далі йде пустий елемент script, який ми будемо використовувати для того, щоб привести коду в дію.
І на цьому все з HTML!
Крок 2: CSS
Вміст default.css
надзвичайно простий:
1 |
body { |
2 |
margin:0; |
3 |
padding:0; |
4 |
background:#ccc; |
5 |
font:14px/1.5 "Helvetica Neue", Helvetica, Arial, san-serif; |
6 |
}
|
7 |
article, footer, header { display: block; } |
8 |
article { |
9 |
width:880px; |
10 |
background:#fff; |
11 |
margin:auto; |
12 |
padding:40px; |
13 |
}
|
14 |
article header { |
15 |
color:#474747; |
16 |
border-bottom:1px solid #474747 |
17 |
}
|
18 |
article footer { |
19 |
font-size:90%; |
20 |
color:#ccc; |
21 |
}
|
І все; тепер залишилось поглянути на CSS-код stickies.css
... проте у нас ще нема для нього HTML-розмітки. Так що давайте візьмемося за написання деякого коду JavaScript, і після цього ми розберемося з CSS-кодом для нотаток-наклейок.
Крок 3: JavaScript
Нижче наведено каркас нашого JavaScript-додатка:
1 |
var STICKIES = (function () { |
2 |
var initStickies = function () {}, |
3 |
openStickies = function () {}, |
4 |
createSticky = function (data) {}, |
5 |
deleteSticky = function (id) {}, |
6 |
saveSticky = function () {}, |
7 |
markUnsaved = function () {}; |
8 |
|
9 |
return { |
10 |
open : openStickies, |
11 |
init : initStickies |
12 |
};
|
13 |
}());
|
Ми використовуємо тут декілька цікавих прийомів. Перший – функція-вираз негайного виклику: може здатися, що ми присвоюємо функцію змінній STICKIES
, проте якщо ви придивитеся до кінця функції, то побачите, що ми її одразу ж і запускаємо. В якості натяку для нагадування нам, що це не звичайна функція, ми обгортаємо всю функцію в круглі дужки. Так що STICKIES
– не функція, а повернене тою функцією значення, що є об'єктом у нашому випадку.
Це приводить нас до наступного прийому (* комбінація функції та лексичного оточення, в якому її було об'явлено). Зверніть увагу на те, що з шести створених нами функцій тільки дві надаються користувачеві (в дійсності лише одна потрібна у випадку нашого додатка; якщо би ми хотіли додати можливість створення записів до вашого сайту, то могли би надати і функцію createSticky
и deleteSticky
). Незважаючи на те що функція-вираз негайного виклику перестає виконуватися ще перед тим, як ми навіть скористуємося цими методами, у нас буде можливість скористатися іншими функціями, які ми визначили.
Добре, давайте перейдемо до вмісту цих функцій.
initStickies
Ми почнемо з розгляду функції initStickies
:
1 |
var initStickies = function initStickies() { |
2 |
$("<div />", { |
3 |
text : "+", |
4 |
"class" : "add-sticky", |
5 |
click : function () { createSticky(); } |
6 |
}).prependTo(document.body); |
7 |
initStickies = null; |
8 |
},
|
Вона доволі проста. Ми будемо використовувати jQuery для створення елементів доволі часто, і ми використовуємо деякий спеціальний синтаксис у версії 1.4: передача літаралу об'єкта з налаштуваннями для елемента в якості другого параметру до функції jQuery. Тут ми створюємо кнопку для створення нової нотатки. Тобто нам потрібен новий елемент div
; ми задаємо в якості значення text ”+” і додаємо клас “add-sticky”; потім ми задаємо обробник для події click, щоб викликати метод createSticky
(важливо, щоб createSticky
викликався з функції, а не безпосередньо в якості обробника події click; це так, оскільки createSticky
може приймати єдиний параметр, і нам не потрібно, щоб це був об'єкт події). Нарешті, ми додаємо цей div
на початку елемента body. Ми завершуємо тим, що задаємо в якості значення initStickies
null
; так, ми по суті позбавляємося функції, яка виконується. Завдяки цьому гарантується, що ця функція буде виконуватися тільки раз; ми не хотіли би, щоб користувач нашого API ненароком додав безліч кнопок “add note” на сторінку.
openStickies
Давайте перейдемо до наступного методу – openStickies
:
1 |
openStickies = function openStickies() { |
2 |
initStickies && initStickies(); |
3 |
for (var i = 0; i < localStorage.length; i++) { |
4 |
createSticky(JSON.parse(localStorage.getItem(localStorage.key(i)))); |
5 |
}
|
6 |
},
|
Ми починаємо із запуску функції initStickies
... але для чого використовуємо той незвичайний синтаксис? Що ж, ви, напевно, знайомі з оператором &&
– булев оператор AND. Ви би звичайно використовували його в операторі if, щоб перевірити, чи виконуються декілька умов. За допомогою нього власне виконується наступне: обраховується значення першого виразу, і якщо їм є true, то далі обраховується значення другого виразу. У нашому випадку, якщо в якості значення initStickies
ще не було задано null, то функцію буде виконано. Завдяки цьому ми уникаємо з'явлення помилки, яка би виникла при спробі запуску змінної зі значенням null в якості функції.
Далі ми перебираємо кожний елемент у localStorage. Нижче описано, що відбувається в тому циклі for (зсередини назовні):
-
localStorage.key()
– чудова функція, яка повертає ім'я ключа значенняlocalStorage
; вона приймає число в якості параметра. Це чудовий спосіб перебору всіх елементівlocalStorage
. - Після отримання ключа елементу, що зберігається в localStorage, ми можемо передати його
localStorage.getItem()
для отримання його значення. - Потім ми передаємо це значення до
JSON.parse()
; цей метод постачається в складі бібліотеки Крокфорда. Оскільки ми зберігаємо декілька значень для кожної нотатки, то використовуємоJSON.stringify()
на іншому боці (* метод saveSticky) для перетворення об'єкта в JSON-рядок, який зберігаємо. Тут ми перетворюємо її з рядка назад в об'єкт. - Нарешті, ми передаємо цей об'єкт до методу
createSticky()
, за допомогою якого він перетворюється назад у стікер для нотаток.
createSticky
Тепер давайте поглянемо на той метод createSticky
.
1 |
createSticky = function createSticky(data) { |
2 |
data = data || { id : +new Date(), top : "40px", left : "40px", text : "Note Here" } |
3 |
|
4 |
return $("<div />", { |
5 |
"class" : "sticky", |
6 |
'id' : data.id |
7 |
})
|
8 |
.prepend($("<div />", { "class" : "sticky-header"} ) |
9 |
.append($("<span />", { |
10 |
"class" : "status-sticky", |
11 |
click : saveSticky |
12 |
}))
|
13 |
.append($("<span />", { |
14 |
"class" : "close-sticky", |
15 |
text : "trash", |
16 |
click : function () { deleteSticky($(this).parents(".sticky").attr("id")); } |
17 |
}))
|
18 |
)
|
19 |
.append($("<div />", { |
20 |
html : data.text, |
21 |
contentEditable : true, |
22 |
"class" : "sticky-content", |
23 |
keypress : markUnsaved |
24 |
}))
|
25 |
.draggable({ |
26 |
handle : "div.sticky-header", |
27 |
stack : ".sticky", |
28 |
start : markUnsaved, |
29 |
stop : saveSticky |
30 |
})
|
31 |
.css({ |
32 |
position: "absolute", |
33 |
"top" : data.top, |
34 |
"left": data.left |
35 |
})
|
36 |
.focusout(saveSticky) |
37 |
.appendTo(document.body); |
38 |
},
|
Так, він довгий, проте з ним буде не дуже важко розібратися. Для початку зверніть увагу, що ця функція приймає об'єкт 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 буде відображатися статус нотатки: saved або unsaved (* не збережено). Буде декілька способів збереження даних нотаток уlocalStorage
; ймовірно, що користувач буде думати, що завдяки натисненню ‘unsaved’ нотатку буде збережено, так що ми надаємо їм цю функціональну можливість. Другий елемент span,span.close-sticky
, буде кнопкою delete: при натисненні її користувачем ми видалимо нотатку зlocalStorage
за допомого методуdeleteSticky
. Ми передаємо до цього методу id нотатки. -
Далі ми додаємо ще один div до головного
div.sticky
; зверніть увагу, що ми задали в якості значення властивостіhtml
data.text
; при збереженні тексту нотатки ми використовуємо методhtml()
jQuery, оскільки при використанніtext()
видаляються переноси рядків. Також ми додаємоcontentEditable:true
для цього div, оскільки це контент нотатки. Тому для нього також задається класsticky-content
. Нарешті, при натисненні клавіши, коли користувач змінює його контент, нам потрібно помітити цей елемент div як unsaved, так що ми викликаємо відповідну функцію (яку скоро реалізуємо). -
Далі ми використовуємо можливість перетягування jQuery UI, щоб наш стікер для нотаток можна було переміщати. У нашому об'єкті, який передаємо в якості параметра, ми використовуємо властивість
handle
, щоб наші нотатки можна було переміщати тільки за допомогою верхньої панелі. Властивістьstack
– селектор для перетягуваних елементів, використовуваний, щоб вони могли «розташуватися поверх один одного»; завдяки вказанню його значення перетягувана в цей момент нотатка завжди розташовується зверху Нарешті, коли нотатка починає переміщатися, нам потрібно помітити її як “unsaved” (оскільки нам потрібно зберегти також і її координати), і по завершенні перетягування ми збережемо цю нотатку. -
Далі ми задаємо деякі стильові правила для нашого
div.sticky
; ми встановлюємо для нього абсолютне позиціонування і потім задаємо в якості значень його властивостей top та left ті, що містяться в об'єкті data. Завдяки цьому розташування та контент нотатки залишаться на попередньому місці, коли ми оновимо сторінку. -
Після цього ми задамо обробник подій для випадків, коли ми переміщаємо фокус за межі нотатки (по суті натискаємо за межами її після натиснення всередині неї): нам потрібно зберегти нотатку.з Нарешті, ми додамо її в кінці body. Вам для довідки: нижче наведено HTML-структуру, яку повинні отримати:
1 |
<div class="sticky ui-draggable" id="1281194825332" style="position: absolute; top: 40px; left: 40px;"> |
2 |
<div class="sticky-header"> |
3 |
<span class="sticky-status"></span> |
4 |
<span class="close-sticky">trash</span> |
5 |
</div>
|
6 |
<div contenteditable="true" class="sticky-content"> |
7 |
Note Here |
8 |
</div>
|
9 |
</div>
|
І це буда наша функція createSticky
.
deleteSticky
Далі йде функція deleteSticky
; вона дуже проста:
1 |
deleteSticky = function deleteSticky(id) { |
2 |
localStorage.removeItem("sticky-" + id); |
3 |
$("#" + id).fadeOut(200, function () { $(this).remove(); }); |
4 |
},
|
Як ви пам'ятаєте, функція deleteSticky
приймає id нотатки в якості свого параметра. localStorage.removeItem()
– ключовий метод тут: ми передаємо йому ключ збереженого локально значення, щоб видалити цю пару ключ-значення (Зверніть увагу, що коли ми зберігаємо дані нотатки, то додаємо на початку id “sticky-”). Потім ми знаходимо елемент із відповідним id, робимо так, щоб він поступово зник, і видаляємо його. Нотатку видалено!
saveSticky
Передостанній метод (saveSticky
), напевно, найважливіший у цьому посібнику: це клей, завдяки якому тут все працює.
1 |
saveSticky = function saveSticky() { |
2 |
var that = $(this), sticky = (that.hasClass("sticky-status") || that.hasClass("sticky-content")) ? that.parents('div.sticky'): that, |
3 |
obj = { |
4 |
id : sticky.attr("id"), |
5 |
top : sticky.css("top"), |
6 |
left: sticky.css("left"), |
7 |
text: sticky.children(".sticky-content").html() |
8 |
}
|
9 |
localStorage.setItem("sticky-" + obj.id, JSON.stringify(obj)); |
10 |
sticky.find(".sticky-status").text("saved"); |
11 |
},
|
Перший рядок – виконання деякого приведення (* посилання до div.sticky): є три різних елементи, з яких ми можемо викликати цю функцію. Для початку ми замінимо this
на that
завдяки можливостям jQuery; потім, якщо в елемента є або клас “sticky-status”, або клас “sticky-content”, то ми отримаємо його батьківський елемент, div.sticky
; якщо в нього нема ніякого з цих класів, то елементом є сам div.sticky
, так що ми просто скористаємося ним.
Потім нам потрібно отримати значення, які хочемо зберегти. Як бачите, ми отримуємо id, відступи зверху та зліва, і HTML-код дочірнього елемента з класом .sticky-content
; пам'ятайте, що ми використовуємо html()
замість text()
, оскільки хочемо зберегти переноси рядків. Потім ми використовуємо localStorage.setItem
для збереження даних. Пам'ятайте, що цей метод приймає два параметри: ключ та значення, які потрібно зберегти. Оскільки localStorage
зберігає тільки рядки, ми використовуємо JSON.stringify()
для перетворення об'єкта в рядок.
Нарешті, ми змінюємо статус нотатки на “saved.”
markUnsaved
У нас є одна остання функція, що є просто допоміжною функцією:
1 |
markUnsaved = function markUnsaved() { |
2 |
var that = $(this), sticky = that.hasClass("sticky-content") ? that.parents("div.sticky") : that; |
3 |
sticky.find(".sticky-status").text("unsaved"); |
4 |
}
|
Знов-таки, нам потрібно почати з приведення посилання до div.sticky
; після цього ми можемо просто отримати елемент span та вказати в якості його тексту “unsaved.”
Хочете – вірте, хочете – ні, це все був код JavaScript.
Крок 4: Допрацьований CSS-код
Тепер, коли ми знаємо, як виглядає розмітка для нашої нотатки, ми можемо додати для неї стильове оформлення. Воно доволі просте; проте перегляньте код, а я наведу декілька коментарів в кінці:
1 |
:focus { |
2 |
outline:0; |
3 |
}
|
4 |
.add-sticky { |
5 |
cursor: default; |
6 |
position:absolute; |
7 |
top:1px; |
8 |
left:1px; |
9 |
font-size:200%; |
10 |
background:#000; |
11 |
color:#fff; |
12 |
border:2px solid #fff; |
13 |
border-radius:40px; |
14 |
-webkit-border-radius:40px; |
15 |
-moz-border-radius:40px; |
16 |
text-align:center; |
17 |
line-height:25px; |
18 |
width:30px; |
19 |
height:30px; |
20 |
}
|
21 |
.add-sticky:hover { |
22 |
background: #474747; |
23 |
}
|
24 |
.sticky { |
25 |
width:300px; |
26 |
background:#fdfdbe; |
27 |
box-shadow:3px 3px 10px rgba(0,0,0,0.45); |
28 |
-webkit-box-shadow:3px 3px 10px rgba(0,0,0,0.45); |
29 |
-moz-box-shadow:3px 3px 10px rgba(0,0,0,0.45); |
30 |
}
|
31 |
.sticky-content { |
32 |
min-height:150px; |
33 |
border-left:3px double rgba(238, 150, 122, .75); |
34 |
margin-left:30px; |
35 |
padding:5px; |
36 |
}
|
37 |
.sticky-header { |
38 |
padding:5px; |
39 |
background:#f3f3f3; |
40 |
border-bottom:2px solid #fefefe; |
41 |
box-shadow:0 3px 5px rgba(0,0,0,0.25); |
42 |
-webkit-box-shadow:0 3px 5px rgba(0,0,0,0.25); |
43 |
-moz-box-shadow:0 3px 5px rgba(0,0,0,0.25); |
44 |
}
|
45 |
.sticky-status { |
46 |
color:#ccc; |
47 |
padding:5px; |
48 |
}
|
49 |
.close-sticky { |
50 |
background:#474747; |
51 |
float:right; |
52 |
cursor:default; |
53 |
color:#ececec; |
54 |
padding:1px 5px; |
55 |
border-radius:20px; |
56 |
-webkit-border-radius:20px; |
57 |
-moz-border-radius:20px; |
58 |
}
|
Тут є декілька моментів, на які варто звернути увагу:
- Деякі браузери додають контур навколо елементів за допомогою
contenteditable=true
, коли ви редагуєте контент. Нам це не потрібно, тому ми позбавляємося його завдяки додаванню правила:focus
. - Кнопка “Add Sticky” розташовується у верхньому лівому куті; вона трохи нагадує за зовнішнім виглядом “Add Dashboard Widget” в Mac OS X.
- Ми використовуємо властивості CSS3 border-radius та box-shadow (та їх відповідні варіанти з префіксами вендорів).
- Також ми використовуємо
rgba()
для задання кольорів тіней. Цей метод приймає чотири параметри: червоний, зелений та синій кольори, а також значення (* четвертий компонент коліру, використовуваний для контролю змішування кольорів з фоном або підлеглим об'єктом. У цьому разі його значення, що дорівнює 1,0, означає цілковиту непрозорість, а 0,0 - цілковиту прозорість об'єкта).
В усьому іншому це просто ваш стандартний CSS-код. Нижче показано, як повинна виглядати нотатка зі стильовим оформленням:

Крок 5: Запуск коду для реалізації стікерів
Тепер, коли в нас є наш API, прийшов час його запустити; ми можемо це зробити з додаткового пустого елемента script
у нашому файлі index.html
:
1 |
STICKIES.open(); |
Завершення: Остаточний результат
Що ж, на цьому все! Нижче показано кінцевий результат у дії:
Це все, що в мене було для вас на сьогодні; як ви плануєте використовувати HTML5-сховище для оживлення ваших веб-проектів? Дайте мені знати про це нижче в розділі для додання коментарів!