jQuery UI日期选择器上的日期范围选择器

20

我使用jquery ui创建了一个日期范围选择器,您可以使用同一行内的日历来选择两个日期。

在此处查看我的fiddle:http://jsfiddle.net/kVsbq/4/

JS

$(".datepicker").datepicker({
    minDate: 0,
    numberOfMonths: [12, 1],
    beforeShowDay: function (date) {
        var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
        var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
        return [true, date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2)) ? "dp-highlight" : ""];
    },
    onSelect: function (dateText, inst) {
        var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
        var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
        if (!date1 || date2) {
            $("#input1").val(dateText);
            $("#input2").val("");
            $(this).datepicker();
        } else {
            $("#input2").val(dateText);
            $(this).datepicker();
        }
    }
});

我希望能够实现像这样的范围选择器:http://jsfiddle.net/D3wLX/1/

如果您选择一个较早的日期,则该日期将自动成为范围中的第一个日期,并突出显示中间的日期。目前,在我的原始jQuery UI解决方案中,它只会将较早的日期放在第二个输入框中,而不会突出显示中间的日期。

7个回答

23
你的脚本正是我所需要的。我复制了你的原始代码,并仅对你的onSelect进行了微小的调整,以使它按照你的要求正常工作。
onSelect: function(dateText, inst) {
    var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
    var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
    var selectedDate = $.datepicker.parseDate($.datepicker._defaults.dateFormat, dateText);

            
    if (!date1 || date2) {
        $("#input1").val(dateText);
        $("#input2").val("");
        $(this).datepicker();
    } else if( selectedDate < date1 ) {
        $("#input2").val( $("#input1").val() );
        $("#input1").val( dateText );
        $(this).datepicker();
    } else {
        $("#input2").val(dateText);
        $(this).datepicker();
    }
}

你原来的部分缺少的是一个简单的检查,将当前选择的日期值与已经捕获的日期值进行比较。
这是我修改后的代码: https://jsfiddle.net/7xwm6bqt/

好的,这是一个非常棒的解决方案。我有一个问题,因为我正在尝试完全理解它是如何/为什么工作的。选择 onSelect 方法标记选定的日期是有道理的。但是让我感到困惑的是,为什么 beforeShowDay 有实际添加高亮类到日期的行。我认为 beforeShowDay 是在构建日历时调用的,在显示每天之前调用。我在构建我的12个月日历(我已经合并了这些操作)时使用它来突出预订日期。那么,当您选择(onSelect)日期时,它是如何再次被调用的呢?谢谢,Thom - Thom

7
我在这里找到了答案:

http://www.benknowscode.com/2012/11/selecting-ranges-jquery-ui-datepicker.html(该网站似乎已被黑客攻击)

很棒的教程。

$.datepicker._defaults.onAfterUpdate = null;
var datepicker__updateDatepicker = $.datepicker._updateDatepicker;
$.datepicker._updateDatepicker = function( inst ) {
   datepicker__updateDatepicker.call( this, inst );
   var onAfterUpdate = this._get(inst, 'onAfterUpdate');
   if (onAfterUpdate)
      onAfterUpdate.apply((inst.input ? inst.input[0] : null),
         [(inst.input ? inst.input.val() : ''), inst]);
}
$(function() {
   var cur = -1, prv = -1;
   $('#jrange div')
      .datepicker({
            //numberOfMonths: 3,
            changeMonth: true,
            changeYear: true,
            showButtonPanel: true,
            beforeShowDay: function ( date ) {
                  return [true, ( (date.getTime() >= Math.min(prv, cur) && date.getTime() <= Math.max(prv, cur)) ? 'date-range-selected' : '')];
               },
            onSelect: function ( dateText, inst ) {
                  var d1, d2;
                  prv = cur;
                  cur = (new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay)).getTime();
                  if ( prv == -1 || prv == cur ) {
                     prv = cur;
                     $('#jrange input').val( dateText );
                  } else {
                     d1 = $.datepicker.formatDate( 'mm/dd/yy', new Date(Math.min(prv,cur)), {} );
                     d2 = $.datepicker.formatDate( 'mm/dd/yy', new Date(Math.max(prv,cur)), {} );
                     $('#jrange input').val( d1+' - '+d2 );
                  }
               },
            onChangeMonthYear: function ( year, month, inst ) {
                  //prv = cur = -1;
               },
            onAfterUpdate: function ( inst ) {
                  $('<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">Done</button>')
                     .appendTo($('#jrange div .ui-datepicker-buttonpane'))
                     .on('click', function () { $('#jrange div').hide(); });
               }
         })
      .position({
            my: 'left top',
            at: 'left bottom',
            of: $('#jrange input')
         })
      .hide();
   $('#jrange input').on('focus', function (e) {
         var v = this.value,
             d;
         try {
            if ( v.indexOf(' - ') > -1 ) {
               d = v.split(' - ');
               prv = $.datepicker.parseDate( 'mm/dd/yy', d[0] ).getTime();
               cur = $.datepicker.parseDate( 'mm/dd/yy', d[1] ).getTime();
            } else if ( v.length > 0 ) {
               prv = cur = $.datepicker.parseDate( 'mm/dd/yy', v ).getTime();
            }
         } catch ( e ) {
            cur = prv = -1;
         }
         if ( cur > -1 )
            $('#jrange div').datepicker('setDate', new Date(cur));
         $('#jrange div').datepicker('refresh').show();
      });
});
.wrapper {
   height: 600px;
}
#jrange input {
   width: 200px;
}
#jrange div {
   font-size: 9pt;
}
.date-range-selected > .ui-state-active,
.date-range-selected > .ui-state-default {
   background: none;
   background-color: lightsteelblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="wrapper">
   <div id="jrange" class="dates">
    <input />
    <div></div>
   </div>
</div>


11
链接已失效,无法访问。 - Gautam Krishnan
2
这个链接上所有的图片都失效了...这个网站开发者亟需更新他们的网站。 - Blairg23

4
我也在寻找一种方法来扩展Datepicker jQuery插件以利用Bootstrap样式,并发现了这个小宝石:Bootstrap-Date Range Picker,由Dan Grossman制作,其中展示了一些具有工作代码的实时示例。
此外,这是该项目的GitHub页面。
最后,这是设计简单而强大的截图:screenshot

2
谢谢,我需要这种代码。这是我的代码:
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script     src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

 <div id="Datepicker"></div>
<p>
<label><b>Checkin:</b></label> <label id="checkinDate"></label>
<label><b>Checkout:</b></label> <label id="checkoutDate"></label>
</p>

/** Display Checkin Datepicker and Checkout DatePicker */
<script>
datePicker();
function datePicker(){
   $(document).ready(function(){
      $( "#Datepicker" ).datepicker({
         dateFormat: "MM d, yy",
     minDate: 0,
     maxDate: "+3M +0D", 
         beforeShowDay: dateRange,
     onSelect: DRonSelect
      });
   });
}

function dateRange(date){
   var date1 = $.datepicker.parseDate("MM d, yy", $("#checkinDate").text());
   var date2 = $.datepicker.parseDate("MM d, yy", $("#checkoutDate").text());
   var isHighlight = date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2));
      $(document).ready(function(){
  // $("td.dp-highlight").text("Y");

});
   return [true, isHighlight ? "dp-highlight" : ""];
}

function DRonSelect(dateText, inst) {
   var date1 = $.datepicker.parseDate("MM d, yy", $("#checkinDate").text());
   var date2 = $.datepicker.parseDate("MM d, yy", $("#checkoutDate").text());
      if (!date1 || date2) {
         $("#checkinDate").text(dateText);
     $("#checkoutDate").text("");
         $("#Datepicker").datepicker();
      } 
      else {
         if ( $.datepicker.parseDate("MM d, yy", $("#checkinDate").text()) >= 
$.datepicker.parseDate("MM d, yy", dateText)) {
            $("#checkinDate").text(dateText);
            $("#checkoutDate").text("");
            $("#Datepicker").datepicker();
         }
         else {
        $("#checkoutDate").text(dateText);
            $("#Datepicker").datepicker();
         }
      }   
}
</script>

我的代码是从其他人那里得来的样本代码,但在日期选择、日期范围和高亮方面有所不同。我在JSFIDDLE中创建并保存了一段代码。

https://jsfiddle.net/kk585b4g/


你可以提供更多的理由,说明为什么你改变了某些部分。另一方面,抄袭其他答案也很简单。 - timiTao
嗨Timitao。这里的所有人都很友善,他们分享代码并在此发布代码。我更改了一些部分,因为我喜欢修改后的日期选择器的结果。另外,我不能给出更多原因,因为我只需要像分享代码/脚本这样的帮助。我希望你的代码也可以通过分享代码来帮助我。 - Marvin

1

伙计,你的代码真的是我所需要的!

有了Jamie Layne的修正,我决定用它来制作插件。

这里是jsfiddle的链接:http://jsfiddle.net/dxLRm/35/(链接已于2014年01月01日更新)

由于我必须展示一些代码,以下是我的代码:

(function ($) {
$.prototype.rangedatepicker = function (o,x,y) {
    var dp = $.datepicker,
        cl = dp.markerClassName,
        di = 'data-rdp-i',
        df = 'data-rdp-f';

    switch(o)
    {
        case 'option':
            return $(this).datepicker('option');
        case 'hide':
            return $(this).datepicker('hide');
        case 'show':
            return $(this).datepicker('show');
        case 'getInitialDate':
            return dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||'');
        case 'getFinalDate':
            return dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
        case 'getRange':
            var ini=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||''),
                fin=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
            return (!ini&&!fin)?null:[ini,fin];
        case 'getNumDays':
            var ini=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(di)||''),
                fin=dp.parseDate($(this).eq(0).datepicker('option','dateFormat'),$(this).eq(0).attr(df)||'');
            return (ini+0==0||fin+0==0)?0:Math.round((fin-ini)/86400000)+1;
        case 'removeRange':
            return $(this).attr(di,'').attr(df,'').datepicker('setDate',null);
        case 'destroy':
            return $(this).removeAttr(di).removeAttr(df).datepicker('destroy');
        case 'serialize':
            return this[0].id+'_initial='+this[0].getAttribute(di)+'&'+this[0].id+'_final='+this[0].getAttribute(df);
        default:
        var defaults={
            allowSelectOneDay: false,
            alwaysSetDateToFirstDay: true,
            rangeEnabled: true,
            rangeClass: 'ui-state-default ui-state-active'//'dp-highlight'
        };
            o = $.extend({}, defaults, $.datepicker._defaults, o);
        return $(this).each(function () {
            if (!$.datepicker) return;
            var t = this,
                hd = !! ((' ' + t.className + ' ').indexOf(' ' + cl + ' ') + 1);
            $(t).datepicker($.extend({}, o, {
                beforeShowDay: function (d) {
                    if (o.rangeEnabled) {
                        var d1 = dp.parseDate(o.dateFormat, t.getAttribute(di) || ''),
                            d2 = dp.parseDate(o.dateFormat, t.getAttribute(df) || ''),
                            y = (function (d) {
                                try {
                                    return o.beforeShowDay.call(t, d);
                                } catch (e) {}
                            })(d) || [true, '', null],
                            x = ((y && y[0] !== false) || !y) && d1 && ((d.getTime() == d1.getTime()) || (d2 && d >= d1 && d <= d2));
                        return (!d1||!d2)?y||[true,'',null]:[y[0]&&x, (x ? o.rangeClass || defaults.rangeClass : '') + (y[1] ? ' ' + y[1] : ''), y[2]];
                    } else {
                        return (function (d) {
                            try {
                                return o.beforeShowDay.call(t, d);
                            } catch (e) {}
                        })(d) || [true, '', null];
                    }
                },
                onSelect: function (dt, x) {
                    if (o.rangeEnabled) {
                        var i = t.getAttribute(di) || '',
                            f = t.getAttribute(df) || '',
                            d1 = dp.parseDate(o.dateFormat, i),
                            d2 = dp.parseDate(o.dateFormat, f),
                            s = dp.parseDate(o.dateFormat, dt);
                        if ((dt == i && dt == f) || (!o.allowSelectOneDay && ((dt == i && !f) || (dt == f && !i)))) {
                            t.removeAttribute(di);
                            t.removeAttribute(df);
                            $(t).datepicker('setDate', null);
                        } else if (!d1 || d2) {
                            t.setAttribute(di, dt);
                            t.removeAttribute(df);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', s);
                        } else if (s < d1) {
                            t.setAttribute(df, i);
                            t.setAttribute(di, dt);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', s);
                        } else {
                            t.setAttribute(df, dt);
                            o.alwaysSetDateToFirstDay && $(t).datepicker('setDate', d1);
                        }
                    } else {
                        t.removeAttribute(di);
                        t.removeAttribute(df);
                        $(t).datepicker('setDate', dp.parseDate(o.dateFormat, dt));
                    }

                    try {
                        if($(t).datepicker('getDate'))o.onSelect.call(t, dt, x);
                    } catch (e) {}
                }
            }));
        });
    }
};
})(window.jQuery);

你应该访问Fiddle并阅读待完成的任务列表!
欢迎任何想法或代码!

0
寻找日期范围选择器时,我发现了这个页面。我尝试了大部分被建议的和在这里演示的想法,并把它们转换成一个易于使用和集成的扩展程序:https://github.com/BuroRaDer/DateRangePicker。请尝试演示页面以查看其工作原理。我猜我可以把它变成一个真正的jQuery扩展程序,但现在对它的工作方式感到满意。
实时演示:

这两个网站都使用了可用性日历模块,并已经将其集成到其中。


为什么要创建不同的帐户来一遍又一遍地发布相同的答案?你在这篇帖子中发布了与@Charu完全相同的答案。 - NSNoob
@NSNoob的评论已经过时(而且一开始就是不正确的),请删除。 - fietserwin

0

我正在寻找一个即使不是内联的版本也能正常工作的日期选择器。我想要能够点击输入字段来启动范围日期选择器。我能找到的所有范围日期选择器示例都是内联的(包括上面的mcestone和Jamie Layne版本,它们是这个分支代码的基础)。

这里是演示链接:http://jsfiddle.net/boson/pjffdtz2/

最难的部分似乎是让日期选择器在非内联时处理多个输入。如果您想在聚焦时打开日期选择器,日期选择器不会轻松处理两个输入 - 这肯定是一个“技巧”。如果将日期选择器与隐藏的输入相关联(display:none),在可见输入之前创建该隐藏的输入,然后使您的可见输入在单击事件上显示日期选择器,则一切都很好。

因此,我采用了原始答案,并进行了一些微小的更改:

  • 在html中,创建一个与日期选择器相关联的隐藏输入框。将其列在可见输入框之前
  • 在html中,使用可见输入框的单击事件来显示与隐藏输入框相关联的日期选择器。
  • 在Javascript日期选择器的onSelect事件中,将日期选择器临时置于inline模式,直到点击了两个日期为止。这允许您在关闭日期选择器之前单击多个日期(范围“至”日期和“从”日期),从而实现将日期选择器转换为多次单击日期选择器。
  • 在datepicker的onClose中,关闭inline模式。这允许某人单击输入字段再次打开日期选择器。
  • 在beforeShow中,将日期选择器下移几个像素,以便您可以看到输入字段和日期选择器

以下是代码:

$(function() {
  $(".rangepicker").datepicker({
    minDate: 0,
    numberOfMonths: [2, 1],
    beforeShow: function (input, inst) {
      var rect = input.getBoundingClientRect();
      setTimeout(function () {
       inst.dpDiv.css({ top: rect.top + 40, left: rect.left + 0 });
      }, 0);
    },
    beforeShowDay: function(date) {
      var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
      var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
      var isHighlight =
         date1 && ((date.getTime() == date1.getTime()) || (date2 && date >= date1 && date <= date2));
      return [true, isHighlight ? "dp-highlight" : ""];
    },
    onSelect: function(dateText, inst) {
      var date1 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input1").val());
      var date2 = $.datepicker.parseDate($.datepicker._defaults.dateFormat, $("#input2").val());
      var selectedDate = $.datepicker.parseDate($.datepicker._defaults.dateFormat, dateText);

      if (!date1 || date2) {
        $("#input1").val(dateText);  
        $("#input2").val("");        
      } else if (selectedDate < date1) {
          $("#input2").val($("#input1").val()); 
          $("#input1").val(dateText);  
      } else {
          $("#input2").val(dateText);  
      }
      $(this).data('datepicker').inline = true;           
      $(this).datepicker();
    },
    onClose: function() {
      // Since we went inline as soon as the date input was clicked
      // (to leave the datepicker up for both dates selection),
      // turn inline back off again so date input click will once again
      // display the datepicker
      $(this).data('datepicker').inline = false;
    }
  });
});
.dp-highlight .ui-state-default {
          background: #484;
          color: #FFF;
        }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<input type="text" id="input1_1" class="rangepicker" style="display: none">
<p>
    Dates:
    <label><b>To:</b></label>
    <input type="text" id="input1" onclick="$('.rangepicker').datepicker('show');">
    <label><b>From:</b></label>
    <input type="text" id="input2" onclick="$('.rangepicker').datepicker('show');">
    <button id="done">Done</button>
</p>

还有很多需要改进的地方。需要更好的输入验证。特别是希望在内联模式下使日期选择器的“完成”按钮起作用,但日期选择器并不适用于这种情况(如果日期选择器中有可设置的完成按钮标志,那就太好了)。因此,现在我在输入字段旁边放了一个俗气的完成按钮,实际上什么也没做(除了鼓励用户将焦点从日期选择器上移开以关闭日期选择器)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接