jQuery UI datepicker:onSelect调用两次

时间:2012-07-04 08:54:10

标签: javascript jquery jquery-ui jquery-ui-datepicker enter

我注意到jQuery datepicker onSelect方法有一个奇怪的行为,你可以看一下this fiddle来测试它。

使用jQuery 1.7.2和jQuery UI 1.8.18进行测试,在Firefox,IE和Chrome中进行。

情景:

  1. 将重点放在文本字段(应显示日期选择器)
  2. 按enter键选择默认日期(the datepicker API表示可以完成)
  3. 注意onSelect被调用两次。
  4. 如果用鼠标选择日期,一切正常。

    这是一个错误吗?如果没有,我该如何避免这种行为?谢谢你的帮助。

5 个答案:

答案 0 :(得分:1)

是的,你是对的,即使我在一段时间后得到了这个错误,但却忽视了它。 但是这次我查看了我发现的datepicker脚本,因为它可能是bug。只有在您提到

时才能以特定方式重现
  • 点击字段/使用标签
  • 将焦点放在上面
  • 按enter

你将有两次调用回调函数'onSelect'。

这是因为在datepicker javascript中,'_ selectDate'函数用于获取从键事件处理函数(_doKeyDown() - > _selectDay() - > _selectDate())和两个函数调用的所选日期 调用似乎是bug的回调函数。


更新 -

我修改了jquery.ui.datepicker.js文件中的_doKeyDown函数。我在事件输入时修改了代码(keycode-13)。 所以现在回调函数'onSelect'在上面的场景中被调用一次。

以下是版本1.8.21的代码

_doKeyDown: function(event) {
        var inst = $.datepicker._getInst(event.target);
        var handled = true;
        var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
        inst._keyEvent = true;
        if ($.datepicker._datepickerShowing)
            switch (event.keyCode) {
                case 9: $.datepicker._hideDatepicker();
                        handled = false;
                        break; // hide on tab out
                case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' + 
                                    $.datepicker._currentClass + ')', inst.dpDiv);
                        //Update by Sunil start
                        if (sel[0]){
                            $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
                        }else{
                            $.datepicker._hideDatepicker();
                            return true;
                        }
                        //Update by Sunil end
                        return false;
                        break; // select the value on enter
                case 27: $.datepicker._hideDatepicker();
                        break; // hide on escape
                case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
                            -$.datepicker._get(inst, 'stepBigMonths') :
                            -$.datepicker._get(inst, 'stepMonths')), 'M');
                        break; // previous month/year on page up/+ ctrl
                case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
                            +$.datepicker._get(inst, 'stepBigMonths') :
                            +$.datepicker._get(inst, 'stepMonths')), 'M');
                        break; // next month/year on page down/+ ctrl
                case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
                        handled = event.ctrlKey || event.metaKey;
                        break; // clear on ctrl or command +end
                case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
                        handled = event.ctrlKey || event.metaKey;
                        break; // current on ctrl or command +home
                case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
                        handled = event.ctrlKey || event.metaKey;
                        // -1 day on ctrl or command +left
                        if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
                                    -$.datepicker._get(inst, 'stepBigMonths') :
                                    -$.datepicker._get(inst, 'stepMonths')), 'M');
                        // next month/year on alt +left on Mac
                        break;
                case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
                        handled = event.ctrlKey || event.metaKey;
                        break; // -1 week on ctrl or command +up
                case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
                        handled = event.ctrlKey || event.metaKey;
                        // +1 day on ctrl or command +right
                        if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
                                    +$.datepicker._get(inst, 'stepBigMonths') :
                                    +$.datepicker._get(inst, 'stepMonths')), 'M');
                        // next month/year on alt +right
                        break;
                case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
                        handled = event.ctrlKey || event.metaKey;
                        break; // +1 week on ctrl or command +down
                default: handled = false;
            }
        else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
            $.datepicker._showDatepicker(this);
        else {
            handled = false;
        }
        if (handled) {
            event.preventDefault();
            event.stopPropagation();
        }
    },

我希望这可以解决你的问题。


更新2 用户1042031回答了另一种解决方法,但没有完整的解决方案。在这里,您将找到您需要做的事情

$("#datepicker").datepicker({
     onSelect: function(dateText,inst) {       
         $.datepicker._hideDatepicker();
         inst.preventDefault();
    }
});

你需要删除警告,这会导致在输入字段上设置焦点,这将再次显示datepicker。这是调用onSelect一次,确保在按下回车键后隐藏了日期选择器。

DEMO

我正在复制解决方案的一部分只是为了兼顾两种方式:)

答案 1 :(得分:1)

我有一个非常难看的解决方案建议我发布它的一半感到羞耻。然而,绝望的时代需要采取绝望的措施。

var trigger = false;
var previousDate = "";

$("#datepicker").datepicker({

    onSelect: function(dateText, inst) {

        if (!trigger || (previousDate != dateText)) {
            // Do whatever you want to do
            alert("fire!");
            // I decided to alert
            trigger = true;
            previousDate = dateText;
        } else {
            trigger = false; 
        }
    }
});​

尝试这样的事情。我有几分钟的时间来测试它,它似乎工作。如果没有,那就改进想法。

如果你不理解黑客背后的可怕,丑陋的想法,我会解释。

干杯。

答案 2 :(得分:0)

使用

 inst.preventDefault();
像这样:

  $("#datepicker").datepicker({
     onSelect: function(dateText,inst) {       
         alert('s');
         inst.preventDefault();
    }
}); 

编辑:在选择日期后删除日历

解决方法是你需要使用removeclass:

 $( "#datepicker" ).datepicker({

       onSelect: function(dateText, inst) {          
       $(inst.dpDiv).find('.ui-state-highlight.ui-state-hover').removeClass('ui-state-highlight ui-state-hover');          

 },
});

答案 3 :(得分:0)

目前为止的最佳解决方案:

$("#datepicker").datepicker({
    onSelect: function(dateText, inst) {
        // do stuff
        $.datepicker._hideDatepicker();
        inst.preventDefault();
    }

});

inst.preventDefault()是最重要的部分:它会阻止onSelect()的双重触发。通过使用它,日期选择器在选择日期后不再隐藏,因此您必须致电$.datepicker._hideDatepicker()来执行此操作。

感谢user1042031和Sunil Chavan。

答案 4 :(得分:0)

对我来说似乎是个错误...我已经将它绑定到各种各样的异步调用中,并且它弄乱了所有东西...我想到的最佳解决方案是:

jQuery(".date-picker").each(function (index, item) {
    jQuery(item).removeClass("date-picker");
     item.ondpchange = item.onchange;
    jQuery(item).datepicker({
        format: "mm/dd/yyyy",
        autoclose: true,
        todayHighlight: true,

    }).on('changeDate', function (e) {
        if (item.ondpchange != null) {
            item.ondpchange();
        }
    });;
    item.onchange = null;


});