Статьи

Как создать красивый виджет календаря

В сегодняшнем премиум- уроке и скриншоте я собираюсь показать вам, как создать удобный виджет календаря. Мы будем использовать CSS3, чтобы придать ему блестящий вид, а затем добавим довольно приятную функциональность с помощью JavaScript.


Я слежу за блогом 365PSD , действительно аккуратным сайтом, который предлагает бесплатный PSD — обычно небольшой кусочек пользовательского интерфейса — каждый день. На 81-й день был действительно аккуратный виджет календаря. Я подумал, что создать настоящую вещь не составит большого труда, поэтому я покажу вам, как это сделать сегодня!



Мы начнем с построения нашей структуры HTML. Конечно, мы начнем со скелета:

01
02
03
04
05
06
07
08
09
10
11
<!DOCTYPE html>
<html>
<head>
    <meta charset=»utf-8″ />
    <title>Calendar Widget</title>
    <link rel=»stylesheet» href=»calendar.css» />
</head>
<body>
 
</body>
</html>

Итак, внутри тела мы начнем с div чтобы обернуть все это; тогда у нас будет три основных раздела:

01
02
03
04
05
06
07
08
09
10
11
12
13
<div id=»cal»>
    <div class=»header»>
 
    </div>
    <table id=»days»>
 
 
    </table>
    <div id=»cal-frame»>
 
 
    </div>
</div>

Во-первых, у нас есть div.header ; Оглядываясь назад на наш PSD, мы видим, что это соответствует верхней части, разделу, который содержит месяц, переключатели месяца и привязки. Тогда у нас есть таблица с названиями дней. Наконец, у нас есть div#cal-frame . Это календарная сетка.

Я открою вам один секрет: когда я изначально создавал этот пользовательский интерфейс календаря, у меня была только одна таблица с thead для дней и tbody для календарной сетки; но как только я начал писать JavaScript для переключения между месяцами, стало очевидно, что мне нужно использовать что-то более гибкое. Вы поймете почему, когда мы доберемся до JavaScript.

Итак, добавьте это в заголовок:

1
2
3
4
5
<span class=»left button» id=»prev»> &lang;
<span class=»left hook»>
<span class=»month-year» id=»label»> June 20&0
<span class=»right hook»>
<span class=»right button» id=»next»> &rang;

У нас есть пять элементов здесь; снаружи у нас есть левый и правый переключатели календаря; поскольку я не хотел использовать какие-либо изображения в этом проекте, я обнаружил HTML-объекты & lang; и & rang (⟨и⟩ соответственно). Затем у нас есть два пустых промежутка для привязок календаря. Наконец, у нас есть метка месяц / год в середине.

Содержание table#days довольно простое:

1
2
3
4
5
6
7
8
<td>sun</td>
<td>mon</td>
<td>tue</td>
<td>wed</td>
<td>thu</td>
<td>fri</td>
<td>sat</td>

Наконец, у нас есть мужество в div#cal-frame ; проверьте это, и тогда мы обсудим это:

1
2
3
4
5
6
7
8
9
<table class=»curr»>
    <tbody>
        <tr><td class=»nil»></td><td class=»nil»></td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
        <tr><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td class=»today»>11</td><td>12</td></tr>
        <tr><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td></tr>
        <tr><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td></tr>
        <tr><td>27</td><td>28</td><td>29</td><td>30</td><td class=»nil»></td><td class=»nil»></td><td class=»nil»></td></tr>
    </tbody>
</table>


Так что у нас здесь? По сути, мы создаем сетку календаря с таблицей (позже мы будем вставлять текущий месяц динамически). Соответствующие ячейки имеют номера даты; если ячейки пустые, они имеют класс «ноль»; наконец, сегодняшняя дата имеет класс «сегодня».

И действительно, это степень HTML; сейчас особо не на что смотреть, но вот что имеем:



Давайте начнем с некоторой среды:

01
02
03
04
05
06
07
08
09
10
11
body {
    background: #e0e0e0;
}
 
#cal {
    -moz-box-shadow:0px 3px 3px rgba(0, 0, 0, 0.25);
    -webkit-box-shadow:0px 3px 3px rgba(0, 0, 0, 0.25);
    margin:50px auto;
    font: 13px/1.5 «Helvetica Neue», Helvatica, Arial, san-serif;
    display:table;
}

Довольно очевидно, а? После установки цвета фона мы центрируем виджет календаря по горизонтали и присваиваем ему тень блока. Конечно, мы устанавливаем шрифт. Но почему мы устанавливаем дисплей на стол? По умолчанию, div будет отображаться в блоке, что означает, что он займет всю доступную ширину; отображая его в виде таблицы, он будет занимать наименьшую ширину, какую только может (хотя все еще содержит свои дочерние элементы), и все равно будет элементом блока

Далее давайте сосредоточимся на строке заголовка:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
#cal .header {
    cursor:default;
    background: #cd310d;
    background: -moz-linear-gradient(top, #b32b0c, #cd310d);
    background: -webkit-gradient(linear, left top, left bottom, from(#b32b0c), to(#cd310d));
    height: 34px;
    position: relative;
    color:#fff;
    -webkit-border-top-left-radius: 5px;
    -webkit-border-top-right-radius: 5px;
    -moz-border-radius-topleft: 5px;
    -moz-border-radius-topright: 5px;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    font-weight:bold;
    text-shadow:0px -1px 0 #87260C;
    text-transform: uppercase;
}
#cal .header span {
    display:inline-block;
    line-height:34px;
}

Вот первая часть стиля заголовка; мы начинаем с установки курсора на указатель; Кстати, текст не будет выделяться. Затем мы установим красный цвет фона; однако, если браузер поддерживает это, мы будем использовать фоновый градиент: не забудьте добавить его как для Mozilla, так и для webkit! Затем установите высоту 34px; мы установим положение относительно, потому что дети будут абсолютно позиционированы; относительно положения родительского элемента, дочерние элементы будут располагаться абсолютно напротив родительского элемента, а не тела. Установите цвет текста на белый, закруглите верхний левый и правый углы и сделайте шрифт жирным. Затем нанесите небольшую тень текста, чтобы текст выглядел с отступом. Наконец, преобразуйте текст в верхний регистр.

Каждый из элементов в заголовке является span ; каждый из них будет отображаться в виде встроенного блока. Кроме того, дайте им высоту строки 34px (высота заголовка).

Эти промежутки также имеют некоторые специальные классы, поэтому давайте посмотрим на них:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
#cal .header .hook {
    width: 9px;
    height: 28px;
    position: absolute;
    bottom:60%;
    border-radius:10px;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    background:#ececec;
    background: -moz-linear-gradient(right top, #fff, #827e7d);
    background: -webkit-gradient(linear, right top, right bottom, from(#fff), to(#827e7d));
    box-shadow:0px -1px 2px rgba(0, 0, 0, 0.65 );
    -moz-box-shadow:0px -1px 2px rgba(0, 0, 0, 0.65 );
    -webkit-box-shadow:0px -1px 2px rgba(0, 0, 0, 0.65 );
}
.right.hook {
    right:15%;
}
.left.hook {
    left: 15%;
}

Во-первых, у нас есть класс «крючка»; помните, это зацепки или привязки календаря. Мы установим ширину и высоту. Затем разместите его абсолютно. Затем мы переместим его снизу вверх на 60%. Мы завернем за угол достаточно, чтобы привязки выглядели круглыми. Затем мы установим цвет фона; если браузер поддерживает градиенты, мы заменим сплошной фон градиентом. Затем мы дадим им тень от коробки.

Затем мы будем использовать классы местоположения, чтобы расположить крючки горизонтально; если элемент имеет класс «hook» и «right», переместите его на 15% вправо; если у него есть «левый» класс, переместите его на 15% слева.

Теперь у нас есть кнопки переключения месяца:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#cal .header .button {
    width:24px;
    text-align:center;
    position:absolute;
}
#cal .header .left.button {
    left:0;
    -webkit-border-top-left-radius: 5px;
    -moz-border-radius-topleft: 5px;
    border-top-left-radius: 5px;
    border-right:1px solid #ae2a0c;
}
#cal .header .right.button {
    right:0;
    top:0;
    border-left:1px solid #ae2a0c;
    -webkit-border-top-right-radius: 5px;
    -moz-border-radius-topright: 5px;
    border-top-right-radius: 5px;
}
#cal .header .button:hover {
    background: -moz-linear-gradient(top, #d94215, #bb330f);
    background: -webkit-gradient(linear, left top, left bottom, from(#d94215), to(#bb330f));
}

Мы установим ширину этих кнопок и отцентрируем текст; конечно, нам нужно позиционировать их абсолютно также. Затем, для левой кнопки, мы перемещаем ее полностью влево и округляем верхний левый угол. Для правой кнопки она идет вправо и закругляет верхний правый угол.

Наконец, мы добавим эффект наведения для кнопок; конечно, мы будем использовать градиент.

Есть еще один элемент для стиля: это метка месяца.

1
2
3
4
5
#cal .header .month-year {
    letter-spacing: 1px;
    width: 100%;
    text-align: center;
}

Мы будем использовать letter-spacing чтобы дать персонажам немного больше передышки. Затем мы дадим диапазон шириной 100% и отцентрируем текст. Поскольку все элементы родного брата расположены абсолютно, полная ширина делает именно то, что мы хотим.

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

Хорошо, давайте перейдем к дневным заголовкам.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#cal table {
    background:#fff;
    border-collapse:collapse;
}
#cal td {
    color:#2b2b2b;
    width:30px;
    height:30px;
    line-height:30px;
    text-align:center;
    border:1px solid #e6e6e6;
    cursor:default;
}
#cal #days td {
    height:26px;
    line-height: 26px;
    text-transform:uppercase;
    font-size:90%;
    color:#9e9e9e;
}
#cal #days td:not(:last-child) {
    border-right:1px solid #fff;
}

Мы начнем с двух немного более общих селекторов: заголовок дня и сетка календаря являются таблицами, поэтому первое правило применяется к ним обоим: мы устанавливаем фон на белый цвет и сворачиваем границы. Когда границы таблицы свернуты, они не имеют отступов между ними, а смежные ячейки имеют общие границы. Затем, для всех ячеек таблицы, мы дадим им цвет текста, установим их ширину, высоту и высоту строки равными 30 пикселям, и центрируем текст. Все они получают рамку и курсор по умолчанию (стрелка / указатель);

Затем мы добавим некоторые конкретные стили для ячеек таблицы в таблице дней: немного уменьшим их высоту и высоту строки, убедитесь, что они прописные, и сбросим размер шрифта и цвет текста. ( Примечание: в сопровождающем #day я написал #day вместо #days в селекторе для третьего блока выше и никогда не исправлял его; убедитесь, что вы правильно #days !)

Для чего нужно последнее правило? Ну, в настоящее время на дневных именах ячеек есть светло-серые границы. Мы хотим изменить цвет границ справа на белый, чтобы они не были видны. Однако мы не хотим делать это до последней ячейки в строке. Итак, мы можем использовать два псевдокласса. : не будет принимать исключающий селектор «параметр». : last-child получает последний дочерний элемент из уже выбранных нами элементов: в данном случае это ячейки таблицы. Затем мы просто устанавливаем правильную границу для сплошного белого цвета.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
#cal #cal-frame td.today {
    background:#ededed;
    color:#8c8c8c;
    box-shadow:1px 1px 0px #fff inset;
    -moz-box-shadow:1px 1px 0px #fff inset;
    -webkit-box-shadow:1px 1px 0px #fff inset;
}
#cal #cal-frame td:not(.nil):hover {
    color:#fff;
    text-shadow: #6C1A07 0px -1px;
    background:#CD310D;
    background: -moz-linear-gradient(top, #b32b0c, #cd310d);
    background: -webkit-gradient(linear, left top, left bottom, from(#b32b0c), to(#cd310d));
    -moz-box-shadow:0px 0px 0px;
    -webkit-box-shadow:0px 0px 0px;
}

Эти два правила нацелены на календарную сетку. Для ячейки таблицы с классом «сегодня» мы устанавливаем фон на светло-серый, а текст — на темно-серый. Далее, мы устанавливаем тень блока: это белая тень, но мы не используем размытие, поэтому это белая линия; мы подталкиваем его вверх и вправо на один пиксель, чтобы получить эффект вторичной границы. Обратите внимание, что мы добавляем «вставку» в объявление тени блока, чтобы тень находилась внутри ячейки.

Следующее правило применяет эффект наведения ко всем ячейкам таблицы в сетке календаря, кроме тех, которые имеют класс «nil»; мы устанавливаем текст на белый и добавляем текстовую тень. Затем мы меняем фон на красный, используя градиент, когда можем. Мы включили удаление теней от ячеек специально для ячейки «сегодня».

Есть особый случай, о котором мы еще не упоминали; возьмите свой ближайший календарь — нет, не iCal, я говорю о реальном бумажном календаре мертвых деревьев — и посмотрите, о, скажем, октябрь 2010 года. Вы заметите, что на последней неделе есть удвоенная ячейка, с 24- м и 31- м на одном и том же квадрате. Нам нужно это сделать, так что давайте сделаем это.

Мы отметим это, поместив каждую дату в интервал внутри ячейки таблицы.

01
02
03
04
05
06
07
08
09
10
#cal #cal-frame td span {
    font-size:80%;
    position:relative;
}
#cal #cal-frame td span:first-child {
    bottom:5px;
}
#cal #cal-frame td span:last-child {
    top:5px;
}

Во-первых, мы позиционируем span относительно друг друга и сокращаем их шрифт на волосок; Затем мы перемещаем первый вверх на 5px, а второй вниз на 5px.

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

1
2
3
4
5
6
#cal #cal-frame table.curr {
    float:left;
}
#cal #cal-frame table.temp {
    position:absolute;
}

Тот, который мы исчезаем, будет иметь класс «temp»; новый, который мы вводим, чтобы остаться (на некоторое время), будет иметь класс «curr».

И это все для CSS! Теперь давайте перейдем к некоторой функциональности.


Мы сделаем функциональность нашего календаря простой в использовании; в свете этого мы начнем с этого скелета:

Итак, мы создаем три функции внутри нашей функции CALENDAR; один инициализирует виджет календаря, второй будет перемещаться между месяцами, а третий фактически будет создавать сетку календаря; обратите внимание на эту строку после него: createCal.cache = {} ; мы тоже это обсудим!

Мы также создали три переменные вверху: мы дадим значения wrap и label в init , но месяцы — это массив с названиями месяцев.

Вот содержание нашей функции init :

Мы начнем с предоставления wrap и label соответствующие значения: обратите внимание, что мы используем селектор, переданный init чтобы найти wrap, но возвращаемся к «#cal», если он не указан. Затем мы связываем события клика со следующей и предыдущей кнопками календаря; они вызывают функцию switchMonth ; если мы хотим следующий календарь, мы передаем true, иначе мы передаем false.

Однако switchMonth также может принимать еще два параметра; мы будем использовать их для события click на ярлыке. Когда пользователь нажимает на название месяца, мы переключаемся на текущий месяц; Итак, мы передадим текущий месяц и год, которые мы можем получить из объекта JavaScript Date . Не забудьте установить следующий параметр на ноль!

Еще одна вещь (и бонусный совет, которого нет в скринкасте!): Когда пользователь загружает страницу, мы хотим загрузить правильный месяц за месяц, который жестко запрограммирован. Самый простой способ сделать это — вызвать jQuery щелчок по метке без каких-либо параметров; это моделирует щелчок мыши и переносит календарь на текущий месяц.

Давайте перейдем к функции switchMonth :

Мы установим несколько переменных вверху; мы разбиваем метку на массив с именем curr ; мы также создаем переменную calendar и выбираем год календаря, который в данный момент отображается.

Тогда все усложняется. Я использовал условные операторы JavaScript здесь, поэтому я могу поместить все это в одну строку. Вместо того, чтобы пытаться объяснить это, проверьте это: вот что они делают:

Вы можете понять, почему условный оператор привлекателен: это всего одна строка. Вот расширенная версия переменной года:

В конце всего, month и year будут правильными значениями для календаря, который мы пытаемся показать пользователю. Если вам удобнее, вы можете заменить эти две строки фрагментами выше.

Далее мы создаем календарь и настраиваем DOM соответственно:

Что находится в объекте календаря, который возвращается из функции createCal ? Это объект, как это:

Мы обсудим, почему свойство календаря является методом, когда мы приступим к его созданию. Сейчас вернемся к размещению на странице. Мы возьмем фрейм календаря и найдем текущий календарь. Затем мы удалим класс «curr» и применим класс «temp»; затем мы помещаем новый календарь (который, кстати, поставляется с классом «curr»), затем исчезаем и удаляем старый.

Ну, у нас есть только еще одна функция: createCal .

Вот наш старт: переменные. У нас есть day , установленный на 1; у нас есть две переменные для итерации: i и j . Затем мы выясняем, с какого дня недели начинается месяц; мы можем сделать это, создав объект Date для первого дня месяца и вызвав getDay .

Затем мы создаем массив, который содержит количество дней в каждом месяце; для февраля нам нужно учесть високосные годы, поэтому для вычисления этого используйте другое троичное выражение.

Наконец, у нас есть очень важная переменная calendar , которая является массивом.

Далее мы хотим использовать это свойство cache мы createCal функцией createCal . (Поскольку все в JavaScript является объектом, даже функции могут иметь свойства.)

Вот что происходит: есть вероятность, что пользователь будет «запрашивать» один и тот же месяц более одного раза. Как только мы создадим это в первый раз, нет необходимости делать это снова; мы поместим его в кеш и вытащим позже.

Если у этого объекта cache есть свойство с названием года, который мы ищем, мы можем проверить наличие месяца; если мы уже сделали месяц, мы вернем этот кешированный объект. Если для года нет собственности, мы сделаем это, потому что нам нужно будет указать месяц, который мы собираемся создать.

Если мы пройдем этот пункт, то нам нужно начать создавать календарь на запрошенный месяц.

Это сложный бит; хотя переменная haveDays имеет значение true, мы знаем, что в месяце осталось дней. Поэтому мы будем использовать наш итератор i для добавления недельного массива в массив календаря. Затем мы используем цикл for в итераторе j , пока он меньше 7; так как мы начинаем с 0, это даст нам 7 дней для массива нашей недели. В нашем цикле есть три случая.

Во-первых, нам нужно проверить, находимся ли мы в первую неделю месяца; если мы, мы не обязательно начнем в первый день. Мы уже знаем, с какого дня начинается месяц; это в нашей переменной startDay . Поэтому, если j === startDay , мы находимся в подходящий день для начала, поэтому мы поместим значение day в правильный слот. Затем мы увеличиваем day и startDay day на единицу. В следующий раз в цикле for j и startDay будут одинаковыми, поэтому они будут работать до конца недели.

Если мы не на первой неделе ( i !== 0 ), мы позаботимся о том, чтобы у нас оставались дни, чтобы добавить их в календарь; если так, мы вставляем их на место. Наконец, если мы не на первой неделе, и у нас нет дней, чтобы добавить к месяцу, вместо этого мы добавим пустую строку. Затем мы установим для haveDays значение false.

В конце мы проверим, больше ли число дней, чем число дней в месяце; если это так, мы установим для haveDays значение false. Это для особого случая, когда месяц заканчивается в субботу.

Конечно, не забывайте увеличивать i только за пределами цикла for!

Мы не хотим, чтобы в нашем календаре было больше 5 недель; если день или два разливаются на неделю 6, мы разбиваем ячейки на неделе 5, как мы подготовили в нашем CSS. Итак, если в массиве Calendar есть 6- й массив, мы будем его зацикливать. Затем, если содержимое элемента массива не является пустой строкой, мы переназначим значение ячейки непосредственно «над» строкой 6: мы обернем это значение в промежуток и объединим другой промежуток с соответствующим значением строки 6 внутри. Это имеет смысл, верно?

Как только мы все на месте, мы вырежем последний элемент из calendar .

Теперь пришло время объединить каждую неделю в нашем calendar ; мы зациклим каждый из них в цикле for и превратим записи в строки таблицы, где каждый день находится внутри ячейки таблицы. Затем мы превратим все это в объект jQuery, собрав все строки таблицы вместе и обернув их таблицей. Затем мы добавим класс «curr» в эту таблицу.

Все пустые ячейки таблицы (мы можем использовать псевдо-селектор jQuery: empty, чтобы найти их), получить класс «nil».

Если мы создаем календарь на текущий месяц, мы найдем ячейку на сегодня и присвоим ей класс «сегодня»; мы можем найти его, передав функцию методу jQuery filter. Функция возвращает true, если текст ячейки соответствует дате.

Наконец, мы создадим наш готовый объект и поместим его в кеш. Почему мы делаем свойство calendar функцией? Что ж, если мы просто вернем объект jQuery, как только мы добавим его в календарь, а затем перейдем к другому месяцу, таблица будет удалена из DOM; позже, если мы вернемся к этому месяцу, элемент не будет отображаться, потому что кэш ссылается на тот же элемент DOM. Поэтому мы используем метод clone jQuery для возврата копии элемента DOM. Затем метка получает название месяца из массива месяцев и объединяет его с годом. Наконец, мы возвращаем объект.

Были сделаны! Вернувшись в файл index.html, мы добавим тег сценария со следующим:

Это оно! Вот как выглядит наш готовый продукт!


Но я не могу показать вам функциональность; вам придется проверить код самостоятельно! Спасибо за прочтение!