Проблема
Начиная работу над очередным сайтом понадобился datepicker. Самый известный такой datepicker — в jQuery UI, но так как jQuery UI в проекте не использовался — тянуть даже его часть не хотелось, принялся за поиски достойной альтернативы.
Требования следующие:
- Выбор даты, нескольких дат, интервала
- Простота настройки внешнего вида
- Желательно без каких-либо зависимостей кроме jQuery
Требования вполне логичные, ничего сверх естественного.
Каково было мое удивление, когда просмотрев десятка два плагинов я не нашел подходящего.
Для любопытных — сразу демо того, что получилось в результате.
Ближе всех к требованиям оказался DatePicker.
Но у него было несколько недостатков:
- Избыточная табличная верстка
- Старый стиль оформления картинками
- Просто старый, не развивался с 2009 года
- Несколько досадных багов
Решить их можно было кое-как разово, но было решено довести до ума, ведь не последний проект, да и другим может пригодиться.
Велосипед, так велосипед
Первое что захотелось изменить — убрать картинки для округления краев. Но картинки в ячейках таблицы. Убрал ячейки — отвалилась почти вся функциональность… Но ведь пути назад нет!
Потом пошел рефакторинг некоторых вещей, исправление багов, добавление мелких фич, снова исправление багов, и так по кругу.
На всё ушло около двух полных дней, и оно того стоило.
Что получилось
Как пример привожу код, генерируемый оригинальным DatePicker:
<div class="datepicker" id="datepicker_828" style="display: block; position: relative; width: 196px; height: 148px;"> <div class="datepickerBorderT"></div> <div class="datepickerBorderB"></div> <div class="datepickerBorderL"></div> <div class="datepickerBorderR"></div> <div class="datepickerBorderTL"></div> <div class="datepickerBorderTR"></div> <div class="datepickerBorderBL"></div> <div class="datepickerBorderBR"></div> <div class="datepickerContainer" style="width: 176px; height: 128px;"> <table cellspacing="0" cellpadding="0"> <tbody> <tr> <td> <table cellspacing="0" cellpadding="0" class="datepickerViewYears"> <thead> <tr> <th class="datepickerGoPrev"> <a href="#"> <span>◀</span> </a> </th> <th colspan="6" class="datepickerMonth"> <a href="#"> <span>2002 - 2013</span> </a> </th> <th class="datepickerGoNext"> <a href="#"> <span>▶</span> </a> </th> </tr> <tr class="datepickerDoW"> <th> <span>wk</span> </th> <th> <span>Mo</span> </th> <th> <span>Tu</span> </th> <th> <span>We</span> </th> <th> <span>Th</span> </th> <th> <span>Fr</span> </th> <th> <span>Sa</span> </th> <th> <span>Su</span> </th> </tr> </thead> <tbody class="datepickerMonths"> <tr> <td colspan="2"> <a href="#"> <span>Jan</span> </a> </td> <td colspan="2"> <a href="#"> <span>Feb</span> </a> </td> <td colspan="2"> <a href="#"> <span>Mar</span> </a> </td> <td colspan="2"> <a href="#"> <span>Apr</span> </a> </td> </tr> <tr> <td colspan="2"> <a href="#"> <span>May</span> </a> </td> <td colspan="2"> <a href="#"> <span>Jun</span> </a> </td> <td colspan="2"> <a href="#"> <span>Jul</span> </a> </td> <td colspan="2"> <a href="#"> <span>Aug</span> </a> </td> </tr> <tr> <td colspan="2"> <a href="#"> <span>Sep</span> </a> </td> <td colspan="2"> <a href="#"> <span>Oct</span> </a> </td> <td colspan="2"> <a href="#"> <span>Nov</span> </a> </td> <td colspan="2"> <a href="#"> <span>Dec</span> </a> </td> </tr> </tbody> <tbody class="datepickerDays"> <tr> <th class="datepickerWeek"> <a href="#"> <span>27</span> </a> </th> <td class="datepickerNotInMonth"> <a href="#"> <span>30</span> </a> </td> <td class=""> <a href="#"> <span>1</span> </a> </td> <td class=""> <a href="#"> <span>2</span> </a> </td> <td class=""> <a href="#"> <span>3</span> </a> </td> <td class=""> <a href="#"> <span>4</span> </a> </td> <td class="datepickerSaturday"> <a href="#"> <span>5</span> </a> </td> <td class="datepickerSunday"> <a href="#"> <span>6</span> </a> </td> </tr> <tr> <th class="datepickerWeek"> <a href="#"> <span>28</span> </a> </th> <td class=""> <a href="#"> <span>7</span> </a> </td> <td class=""> <a href="#"> <span>8</span> </a> </td> <td class=""> <a href="#"> <span>9</span> </a> </td> <td class=""> <a href="#"> <span>10</span> </a> </td> <td class=""> <a href="#"> <span>11</span> </a> </td> <td class="datepickerSaturday"> <a href="#"> <span>12</span> </a> </td> <td class="datepickerSunday"> <a href="#"> <span>13</span> </a> </td> </tr> <tr> <th class="datepickerWeek"> <a href="#"> <span>29</span> </a> </th> <td class=""> <a href="#"> <span>14</span> </a> </td> <td class=""> <a href="#"> <span>15</span> </a> </td> <td class=""> <a href="#"> <span>16</span> </a> </td> <td class=""> <a href="#"> <span>17</span> </a> </td> <td class=""> <a href="#"> <span>18</span> </a> </td> <td class="datepickerSaturday"> <a href="#"> <span>19</span> </a> </td> <td class="datepickerSunday"> <a href="#"> <span>20</span> </a> </td> </tr> <tr> <th class="datepickerWeek"> <a href="#"> <span>30</span> </a> </th> <td class=""> <a href="#"> <span>21</span> </a> </td> <td class=""> <a href="#"> <span>22</span> </a> </td> <td class=""> <a href="#"> <span>23</span> </a> </td> <td class=""> <a href="#"> <span>24</span> </a> </td> <td class=""> <a href="#"> <span>25</span> </a> </td> <td class="datepickerSaturday"> <a href="#"> <span>26</span> </a> </td> <td class="datepickerSunday"> <a href="#"> <span>27</span> </a> </td> </tr> <tr> <th class="datepickerWeek"> <a href="#"> <span>31</span> </a> </th> <td class=""> <a href="#"> <span>28</span> </a> </td> <td class=""> <a href="#"> <span>29</span> </a> </td> <td class=""> <a href="#"> <span>30</span> </a> </td> <td class="datepickerSelected"> <a href="#"> <span>31</span> </a> </td> <td class="datepickerNotInMonth"> <a href="#"> <span>1</span> </a> </td> <td class="datepickerNotInMonth datepickerSaturday"> <a href="#"> <span>2</span> </a> </td> <td class="datepickerNotInMonth datepickerSunday"> <a href="#"> <span>3</span> </a> </td> </tr> <tr> <th class="datepickerWeek"> <a href="#"> <span>32</span> </a> </th> <td class="datepickerNotInMonth"> <a href="#"> <span>4</span> </a> </td> <td class="datepickerNotInMonth"> <a href="#"> <span>5</span> </a> </td> <td class="datepickerNotInMonth"> <a href="#"> <span>6</span> </a> </td> <td class="datepickerNotInMonth"> <a href="#"> <span>7</span> </a> </td> <td class="datepickerNotInMonth"> <a href="#"> <span>8</span> </a> </td> <td class="datepickerNotInMonth datepickerSaturday"> <a href="#"> <span>9</span> </a> </td> <td class="datepickerNotInMonth datepickerSunday"> <a href="#"> <span>10</span> </a> </td> </tr> </tbody> <tbody class="datepickerYears"> <tr> <td colspan="2"> <a href="#"> <span>2002</span> </a> </td> <td colspan="2"> <a href="#"> <span>2003</span> </a> </td> <td colspan="2"> <a href="#"> <span>2004</span> </a> </td> <td colspan="2"> <a href="#"> <span>2005</span> </a> </td> </tr> <tr> <td colspan="2"> <a href="#"> <span>2006</span> </a> </td> <td colspan="2"> <a href="#"> <span>2007</span> </a> </td> <td colspan="2"> <a href="#"> <span>2008</span> </a> </td> <td colspan="2"> <a href="#"> <span>2009</span> </a> </td> </tr> <tr> <td colspan="2"> <a href="#"> <span>2010</span> </a> </td> <td colspan="2"> <a href="#"> <span>2011</span> </a> </td> <td colspan="2"> <a href="#"> <span>2012</span> </a> </td> <td colspan="2"> <a href="#"> <span>2013</span> </a> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> </div> </div>
и код, генерируемый PickMeUp:
<div class="pickmeup pmu-view-days" style="position: relative; display: inline-block;"> <div class="pmu-instance"> <nav> <div class="pmu-prev pmu-button">◀</div> <div class="pmu-month pmu-button">November, 2013</div> <div class="pmu-next pmu-button">▶</div> </nav> <nav class="pmu-day-of-week"> <div>Mo</div> <div>Tu</div> <div>We</div> <div>Th</div> <div>Fr</div> <div>Sa</div> <div>Su</div> </nav> <div class="pmu-months"> <div class="pmu-button">Jan</div> <div class="pmu-button">Feb</div> <div class="pmu-button">Mar</div> <div class="pmu-button">Apr</div> <div class="pmu-button">May</div> <div class="pmu-button">Jun</div> <div class="pmu-button">Jul</div> <div class="pmu-button">Aug</div> <div class="pmu-button">Sep</div> <div class="pmu-button">Oct</div> <div class="pmu-button">Nov</div> <div class="pmu-button">Dec</div> </div> <div class="pmu-days"> <div class="pmu-not-in-month pmu-button">28</div> <div class="pmu-not-in-month pmu-button">29</div> <div class="pmu-not-in-month pmu-button">30</div> <div class="pmu-not-in-month pmu-button">31</div> <div class=" pmu-button">1</div> <div class="pmu-saturday pmu-button">2</div> <div class="pmu-sunday pmu-button">3</div> <div class=" pmu-button">4</div> <div class=" pmu-button">5</div> <div class=" pmu-button">6</div> <div class=" pmu-button">7</div> <div class=" pmu-button">8</div> <div class="pmu-saturday pmu-button">9</div> <div class="pmu-sunday pmu-button">10</div> <div class=" pmu-button">11</div> <div class=" pmu-button">12</div> <div class=" pmu-button">13</div> <div class=" pmu-button">14</div> <div class=" pmu-button">15</div> <div class="pmu-saturday pmu-button">16</div> <div class="pmu-sunday pmu-button">17</div> <div class="pmu-selected pmu-button">18</div> <div class=" pmu-button">19</div> <div class=" pmu-button">20</div> <div class=" pmu-button">21</div> <div class=" pmu-button">22</div> <div class="pmu-saturday pmu-button">23</div> <div class="pmu-sunday pmu-button">24</div> <div class=" pmu-button">25</div> <div class=" pmu-button">26</div> <div class=" pmu-button">27</div> <div class=" pmu-button">28</div> <div class=" pmu-button">29</div> <div class="pmu-saturday pmu-button">30</div> <div class="pmu-not-in-month pmu-sunday pmu-button">1</div> <div class="pmu-not-in-month pmu-button">2</div> <div class="pmu-not-in-month pmu-button">3</div> <div class="pmu-not-in-month pmu-button">4</div> <div class="pmu-not-in-month pmu-button">5</div> <div class="pmu-not-in-month pmu-button">6</div> <div class="pmu-not-in-month pmu-saturday pmu-button">7</div> <div class="pmu-not-in-month pmu-sunday pmu-button">8</div> </div> <div class="pmu-years"> <div class="pmu-button">2007</div> <div class="pmu-button">2008</div> <div class="pmu-button">2009</div> <div class="pmu-button">2010</div> <div class="pmu-button">2011</div> <div class="pmu-button">2012</div> <div class="pmu-button">2013</div> <div class="pmu-button">2014</div> <div class="pmu-button">2015</div> <div class="pmu-button">2016</div> <div class="pmu-button">2017</div> <div class="pmu-button">2018</div> </div> </div> </div>
Код не идеален, но намного проще и понятнее, а значит и оформлять его проще.
Что ещё нового по сравнению с оригиналом:
- Поддержка указания конфигурационных опций в data-атрибутах
- Глобальная конфигурация плагина
- Независимость языка каждого отдельного календаря
- Стили в маленьком scss файле с конфигурационными переменными — если нужно только изменить цвета — идеально подходит
- Макет резиновый, размер зависит от размера шрифта корневого элемента, выглядит одинаково хорошо при любом размере шрифта
- Размер плагина меньше оригинала
- Все классы имеют префикс
pmu-
, корневой элемент имеет классpickmeup
- Кое-какие мелочи
Так выглядит содержимое scss файла:
$border-radius : .4em; $background : #000; $color : #eee; $color-hover : #88c5eb; $nav-color : $color; $nav-color-hover : $color-hover; $not-in-month : #666; $not-in-month-hover : #999; $disabled : #333; $selected-background : #136a9f; $not-in-month-selected-background : #17384d; $day-of-week : $not-in-month-hover; @mixin display-flex() { display : -ms-flexbox; display : -webkit-flex; display : flex; } .pickmeup { background : $background; border-radius : $border-radius; display : none; position : absolute; * { -moz-box-sizing : border-box; box-sizing : border-box; } .pmu-instance { display : inline-block; height : 13.8em; padding : .5em; text-align : center; width : 15em; .pmu-button { color : $color; cursor : pointer; outline : none; text-decoration : none; } .pmu-button:hover { color : $color-hover; } .pmu-not-in-month { color : $not-in-month; } .pmu-disabled, .pmu-disabled:hover { color : $disabled; cursor : default; } .pmu-selected { background : $selected-background; } .pmu-not-in-month.pmu-selected { background : $not-in-month-selected-background; } nav { @include display-flex(); color : $nav-color; line-height : 2em; *:hover { color : $nav-color-hover; } .pmu-prev, .pmu-next { height : 2em; width : 1em; } .pmu-month { width : 12em; } } .pmu-years, .pmu-months { * { display : inline-block; line-height : 3.6em; width : 3.5em; } } .pmu-day-of-week { color : $day-of-week; cursor : default; } .pmu-day-of-week, .pmu-days { * { display : inline-block; line-height : 1.5em; width : 2em; } } .pmu-day-of-week * { line-height : 1.8em; } } &:not(.pmu-view-days) .pmu-days, &:not(.pmu-view-days) .pmu-day-of-week, &:not(.pmu-view-months) .pmu-months, &:not(.pmu-view-years) .pmu-years { display : none; } }
Итог
Размер минифицированного плагина:
* 14.8 KiB JavaScript (4.2 KiB gzip)
* 1.8 KiB CSS (650 B gzip)
C целью упрощения верстки поддерживается IE10+, и актуальные версии других браузеров (при желании можно сделать поддержку IE9-, но у меня такого желания нет, написал всё-таки в первую очередь для себя).
Искренне надеюсь, кому-то кроме меня этот jQuery плагин пригодится, буду рад конструктивной критике и pull request-ам.
В ближайшем будущем можно встроить другие локализации, в репозитории bootstrap-datepicker их много разных.
ссылка на оригинал статьи http://habrahabr.ru/post/202640/
Добавить комментарий