Для очередного проекта понадобился виджет для выбора даты. Очевидным, по крайней мере для меня, решением было jQuery UI Datepicker . Но когда позже понадобилось выбирать не одну дату, а диапазон, я попал в тупик. В API не было ни какого упоминания о такой возможности. Поисковики все как один выдавали какие-то «самоделки» далеко не первой свежести.
Копнув глубже исходники Datepicker, я все-таки обнаружил возможность выбора диапазона. Реализуется все логика через событие «onSelect».
У экземпляра Datepicker есть флаг stayOpen
. Если его установить внутри события, то календарь не закроется после клика, а занесет выбранную дату в поле rangeStart
и позволит выбрать еще дату. Когда будет выбран конец диапазона, нужно сбросить флаг stayOpen
и обновить содержимое текстового поля.
Внимание! Плагин актуален только для jQuery UI 1.7.x. Для последних версий нужно использовать другую редакцию этого плагина.
(function ($) {
$.fn.daterange = function () {
// опции
var opts = $.extend({
"dateFormat": "dd.mm.yy",
"changeMonth": false,
"changeYear": false,
"numberOfMonths": 2,
"rangeSeparator": "-"
}, arguments[0] || {}, {
// обработчики событий datepicker
// закрытие
"onClose": function (dateText, inst) {
if ($.isFunction(opts.callback)) {
opts.callback.apply(this, arguments);
}
},
// выбор даты
"onSelect": function (dateText, inst) {
var textStart;
if (!inst.rangeStart) {
inst.stayOpen = true;
} else {
inst.stayOpen = false;
textStart = $.datepicker.formatDate(opts.dateFormat, inst.rangeStart);
if (textStart !== dateText) {
$(this).val(textStart + " " +
opts.rangeSeparator + " " + dateText);
}
}
}
});
return this.each(function () {
var input = $(this);
if (input.is("input")) {
input.datepicker(opts);
}
});
};
}(jQuery));
Еще одно событие, которое может пригодиться, это «onClose». Оно будет сгенерировано в момент закрытия календаря. В плагине я просто делегирую все исполнение внешнему обработчику, но на практике мне удобно было добавить туда больше функционала.
Плагин может быть проинициализирован любыми параметрами , которые используются в Datepicker. Так как обработчик события «onClose» переопределяется, то вместо него используется поле callback.
Второе дыхание
Обновлено 2013-06-19
Для последних версий jQuery UI вместо stayOpen
нужно использовать флаг
inline
. Однако это только запретит календарю закрыться. Нужно ещё позаботиться о сохранении первой выбранной даты.
(function ($) {
$.fn.daterange = function () {
// опции
var opts = $.extend({
"dateFormat": "dd.mm.yy",
"changeMonth": false,
"changeYear": false,
"numberOfMonths": 2,
"rangeSeparator": "-"
}, arguments[0] || {}, {
// обработчики событий datepicker
// закрытие
"onClose": function (dateText, inst) {
if ($.isFunction(opts.callback)) {
opts.callback.apply(this, arguments);
}
},
// выбор даты
"onSelect": function (dateText, inst) {
var textStart;
if (!inst.rangeStart) {
inst.inline = true;
inst.rangeStart = dateText;
} else {
inst.inline = false;
textStart = inst.rangeStart;
if (textStart !== dateText) {
$(this).val(textStart + " " +
opts.rangeSeparator + " " + dateText);
inst.rangeStart = null;
}
}
}
});
return this.each(function () {
var input = $(this);
if (input.is("input")) {
input.datepicker(opts);
}
});
};
}(jQuery));
Коментарии к заметке
Здравствуйте. Спасибо Вам за статью. Узнал несколько интересных приемов. Хотелось только уточнить, решение со stayOpen еще актуально или этот функционал тоже урезали в последних версиях? Может, я что-то делаю не так, но календарь все равно исчезает при выборе первой даты, да и в доках к дейтпикеру нет такого флага.
Нет, это решение уже тоже устарело. Точнее оно продолжает быть актуальным для jQuery UI 1.7.x, но для 1.8.x не годится.
В версии 1.8 команда jQuery полностью выпилила весь код, относящийся к stayOpen, и не добавила ничего аналогичного взамен, на сколько мне известно. Этот флаг никогда не был официально документирован, но долгое время присутствовал в разных версиях.
Вместо stayOpen можно ставить флаг inline.
Спасибо за информацию. Внёс изменения в статью.
Большое спасибо! Разрешите заметить несколько глюков и пожеланий:
Да, это дефект. Благодарю за сообщение о нём.
У плагина есть страничка в официальном репозитории. Следите за обновлениями.
Это правится очень просто:
Спасибо за статью, сэкномили время!
Совершенно верно. С Гитхаба можно скачать версию со всеми изменениями — https://github.com/mistakster/jquery-daterange. Плагин уже разросся до таких размеров, что публиковать его код на странице нет смысла.
Очень хорошо. Вам бы это в заголовок вынести. Видели в яндекс.метрика какой контрол для выбора диапазона дат? Рекомендую к нему стремиться :)
У Datepicker`а есть особенность — месяцы в двух экземплярах отображаются последовательно, при смене месяца в одном меняется месяц и в другом (одновременная отрисовка обоих). То есть существует предел диапазона — 2 месяца. Каким образом его можно увеличить?
Ryllaz, не совсем понял о каком пределе в 2 месяца идёт речь. Попытаюсь догадаться что ты имеешь в виду.
У jQuery UI Datepicker есть опция numberOfMonths . По-умолчанию она выставляется равной одному месяцу. Мне удобнее, когда появляются сразу два месяца. Теоретически можно задать любое количество месяцев. Эта опция влияет только на отображение календаря. Она не влияет на выбор диапазона.
Никакой проблемы нет выбрать первую дату, перелистать календарь на другой месяц или год и выбрать вторую дату.
http://www.seizedesign.com/tools/jquery-plugins/free-jquery-calendar-date-picker-plugin/
[…] jQuery UI Daterange […]
Just fyi… when you select only one date and then click away the date picker won’t come up again.
Hi Levi, thanks for your feedback.
I have already fixed this issue. Please, see demo page — http://mistakster.github.io/jquery-daterange/ and grab latest version from Github — https://github.com/mistakster/jquery-daterange
А что нужно сделать чтобы диапазон выбранных дат был не больше недели? К примеры я выбираю сегодняшнюю дату первую, тогда следующая не может быть больше чем +7 дней. Где прописать эти +7?
Валерий, такой опции нет. Эту логику нужно написать самому в хендлерах
onSelect
иonClose
. Когда пользователь выбирает первую дату, то в этом хендлере устанавливаете опцииminDate
maxDate
с нужными значениями. Когда пользователь выбирает вторую дату или закрывает календарь, то границы устанавливаете в исходное состояние.А как сделать инициализацию поля заранее известными значениями диапазона?
Vlad, я не знаю почему у вас это не сработало. Я сейчас попробовал запустить приведённый кусок кода и он отработал нормально.
Здравствуйте, а как получить количество дней в выбранном диапазоне?
Посмотрите moment.js