Выбор диапазона дат с помощью jQuery UI Datepicker

Для очередного проекта понадобился виджет для выбора даты. Очевидным, по крайней мере для меня, решением было 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));