在IE8中的选择日期,Jquery datepicker弹出窗口没有关闭

时间:2009-11-09 22:33:11

标签: asp.net jquery jquery-ui

我有一个带有开始日期字段的网络表单。我已将jquery datepicker绑定到txt字段。现在,当我在FF中选择日期时,将在文本框中填充所选日期,并关闭日历弹出窗口。但是,当我在IE8中执行相同操作时,所选日期将填充在文本框中,但弹出窗口仍保持打开状态。我还注意到,一旦我在弹出日历中选择了一个日期,就会生成脚本错误。

我正在使用jquery 1.3.2,jquery-ui 1.7.2和.NET 3.5。这是我的代码示例:

<script type="text/javascript">
  $(document).ready(function() {
    $("#<%=txtStartDate.ClientID%>").datepicker({
      changeMonth: true,
      changeYear: true,
      showButtonPanel: true,
      showOn: 'button',
      buttonImage: '/_layouts/images/CALENDAR.GIF',
      buttonImageOnly: true
    });
  });
</script>
<div id="stylized">
  <asp:ValidationSummary ID="vs" runat="server" CssClass="messages-error" HeaderText=" Action required before the form can be submitted." ForeColor="" ValidationGroup="sh" />
  <div class="formrow">
    <div class="ms-formlabel formlabel">
      <asp:Label ID="lblStartDate" runat="server" CssClass="ms-standardheader" AssociatedControlID="txtStartDate">Start Date:</asp:Label>
    </div>
    <div class="ms-formbody formfield">
      <asp:RequiredFieldValidator ID="reqStartDate" runat="server" ControlToValidate="txtStartDate" ErrorMessage="Start Date is a required field." Text="*" Display="Dynamic" ValidationGroup="sh"></asp:RequiredFieldValidator>
      <asp:CompareValidator ID="cvStartDate" runat="server" ControlToValidate="txtStartDate" ErrorMessage="Date must be in the format MM/DD/YYYY" Text="*" Display="Dynamic" ValidationGroup="sh" Operator="DataTypeCheck" Type="Date"></asp:CompareValidator>
      <asp:TextBox ID="txtStartDate" runat="server"></asp:TextBox>
      <span class="formMessage">ex. MM/DD/YYYY</span>
    </div>
  </div>
  <div id="buttonrow">
    <asp:Button ID="btnSubmit" runat="server" Text="Submit" CssClass="ms-ButtonHeightWidth" OnClick="Submit_Click" ValidationGroup="sh" />
    <asp:Button ID="btnCancel" runat="server" Text="Cancel" CssClass="ms-ButtonHeightWidth" OnClick="Cancel_Click" CausesValidation="false" />
  </div>
</div>

这是我选择日期时在IE中遇到的脚本错误:

'length'为null或不是对象

WebResource.axd的

以下是抛出错误的代码:

function ValidatorOnChange(event) {
  if (!event) {
    event = window.event;
  }
  Page_InvalidControlToBeFocused = null;
  var targetedControl;
  if ((typeof(event.srcElement) != "undefined") && (event.srcElement != null)) {
    targetedControl = event.srcElement;
  }
  else {
    targetedControl = event.target;
  }
  var vals;
  if (typeof(targetedControl.Validators) != "undefined") {
    vals = targetedControl.Validators;
  }
  else {
    if (targetedControl.tagName.toLowerCase() == "label") {
        targetedControl = document.getElementById(targetedControl.htmlFor);
        vals = targetedControl.Validators;
    }
  }
  var i;
  for (i = 0; i < vals.length; i++) {
    ValidatorValidate(vals[i], null, event);
  }
  ValidatorUpdateIsValid();
}

它发生在最后的for循环中的.length上。 Vals为null,在之前的if / else中找不到。我已经逐步完成了javascript和if(typeof(targetedControl.Validators)!=“undefined”)返回false,然后if(targetedControl.tagName.toLowerCase()==“label”)也返回false。因此长度为null或不是对象错误。

现在我不确定日期选择器弹出窗口是否在IE中关闭而且WebResources.axd文件中的脚本错误是否是相关错误,但我是这样倾向的。谁能告诉我为什么弹出窗口没有关闭?

10 个答案:

答案 0 :(得分:27)

选择日期后,datepicker会触发INPUT元素上的更改事件,但ASP.Net验证程序会选择点击事件,源代码为A元素,然后尝试在A元素上找到验证器,而不是INPUT。通过检查验证器的event.srcElement函数内的ValidatorOnChange可以观察到这一点。在IE以外的浏览器中,event.type为'change',event.target正确为INPUT

虽然no-op函数onSelect: function() { }通过覆盖日期选择器的默认.change()的内置onSelect来防止错误,但它也会阻止验证程序触发。以下是两者的解决方法:

onSelect: function() {
  this.fireEvent && this.fireEvent('onchange') || $(this).change();
}

这使用普通的.change()触发器,除了在IE上,需要使用.fireEvent来使事件对象与更改而不是点击相关联。

答案 1 :(得分:16)

这似乎是一种错误,但在datepicker声明中添加这一行应解决它:

onSelect: function() {}

答案 2 :(得分:3)

上面提供的解决方案只能防止错误发生。

在datepicker上:

onSelect : function(dateText, inst){ inst.input.trigger('cValidate')

并将事件绑定到日历输入元素。

.bind('cValidate', function (event) { window.ValidatorOnChange(event); });

这将使用正确的事件args(输入字段)激活validatorchanged事件。

答案 3 :(得分:2)

原因

root bug(我认为它可能是一个功能,或者可能是一个已知的IE bug的解决方法?)在ASP.Net的ValidatorHookupEvent中:

var func;
if (navigator.appName.toLowerCase().indexOf('explorer') > -1) {
    func = new Function(functionPrefix + " " + ev);
}
else {
    func = new Function("event", functionPrefix + " " + ev);
}

因此,在默认情况下,没有注册其他onchange,这会将输入的onchange设置为等同于

function() { ValidatorOnChange(event); }
IE中的

function(event) { ValidatorOnChange(event); }

在其他浏览器中。因此,如果您使用IE,则传递给ValidatorOnChange的事件将为window.event(因为window是全局对象)。

建议的解决方案

如果您不想破解ASP.Net脚本,那么我认为处理此问题的最好方法是检测已损坏的事件处理程序并替换它。与此处的其他建议一样,我提供了onSelect以包含在datepicker选项中。

onSelect: function(dateStr, datePicker) {
    // http://stackoverflow.com/questions/1704398
    if (datePicker.input[0].onchange.toString().match(/^[^(]+\(\)\s*{\s*ValidatorOnChange\(event\);\s*}\s*$/)) {
        datePicker.input[0].onchange = ValidatorOnChange;
    }
    datePicker.input.trigger("change");
}

我正在全球范围内使用

$.datepicker.setDefaults({
    onSelect: function(dateStr, datePicker) {
        etc.
    }
});

答案 4 :(得分:1)

由于为您生成了ValidatorOnChange代码,因此您看起来并没有做错任何事情;它创建vals对象的方式有问题,似乎在ie8上结束为空。

它是been asked before,解决方案是使用no-op函数覆盖onSelect函数。

这不是唯一一种验证器问题。这是一个具有自动填充功能的vaguely similar issue

答案 5 :(得分:1)

修复......

onSelect:function(){}

如果问题出在依赖于服务端事件处理程序验证输入的CustomValidator上,那么

..似乎不起作用。

这里提到了其他一些修复......

http://dev.jqueryui.com/ticket/4071

问题在于IE的事件处理与其他浏览器不同,ASP Net提供的客户端验证代码没有对其作者未考虑的情况做出优雅反应。

答案 6 :(得分:1)

这是jQuery datepickers和ASP验证控件的地方性问题。 正如您所说,错误的元素交叉触发ASP NET javascript验证例程,然后M $代码抛出错误,因为例程中的触发元素未定义。

我通过决定M $应该更强大地编写代码并因此重新声明一些M $验证器代码来处理未定义的元素,从而与我见过的任何其他人不同地解决了这个问题。我所看到的其他所有内容基本上都是jQuery方面的解决方法,并且可以削减可能的功能(例如,使用click事件而不是更改)。

失败的位是

   for (i = 0; i < vals.length; i++) {
        ValidatorValidate(vals[i], null, event);
    }

在尝试获取未定义的'vals'的长度时抛出错误。

我刚刚添加了

if (vals) {
    for (i = 0; i < vals.length; i++) {
        ValidatorValidate(vals[i], null, event);
    }
}

她很高兴。重新声明整个违规功能的最终代码如下。我将其作为脚本包含在我的母版页或页面的底部中(因此它在之后发生默认声明并替换了早期版本)。

是的,如果M $决定将来更改其验证码,这会破坏向上兼容性。但人们希望他们能解决它,然后我们可以完全摆脱这个补丁。

//  Fix issue with datepicker and ASPNET validators: redeclare MS validator code with fix
 function ValidatorOnChange(event) {
    if (!event) {
        event = window.event;
    }
    Page_InvalidControlToBeFocused = null;
    var targetedControl;
    if ((typeof (event.srcElement) != "undefined") && (event.srcElement != null)) {
        targetedControl = event.srcElement;
    }
    else {
        targetedControl = event.target;
    }
    var vals;
    if (typeof (targetedControl.Validators) != "undefined") {
        vals = targetedControl.Validators;
    }
    else {
        if (targetedControl.tagName.toLowerCase() == "label") {
            targetedControl = document.getElementById(targetedControl.htmlFor);
            vals = targetedControl.Validators;
        }
    }
    var i;
    if (vals) {
        for (i = 0; i < vals.length; i++) {
            ValidatorValidate(vals[i], null, event);
        }
    }
    ValidatorUpdateIsValid();
}

答案 7 :(得分:1)

更改jquery.ui.datepicker.js第1504行

'" href="#"  >' + printDate.getDate() + '</a>')

'" href="javascript:DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
       inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);"  >' + printDate.getDate() + '</a>')

测试工作正常!

答案 8 :(得分:0)

我真的不知道问题是什么,但我确实注意到你没有设置ValidationGroup,并且你的两个验证器都设置了这个值。您可以尝试在TextBox中设置ValidationGroup =“sh”,看看是否有帮助。

答案 9 :(得分:0)

基于上述答案并提供上述评论的进一步细节,

显然,在IE9 +和其他浏览器中,您现在应该使用dispatchEvent来触发更改事件。 (Why does .fireEvent() not work in IE9?

datepicker中的OnSelect函数实际上有两个参数:

  1. 与datepicker相关联的文本框的值
  2. 具有与文本框

    匹配的id属性的对象
    mytextbox.datepicker({
        defaultDate: null,
        numberOfMonths: 1,
        dateFormat: DisplayDateFormat,
        onSelect: function (value, source) {
        }
    });
    
  3. 我看到的所有示例都使用document.getElementById()来检索文本框,我认为因为源对象与文本框具有相同的ID而不必看到。仔细研究后发现,source是一个对象,而不是textbox元素。我发现以下代码解决了这个问题:

        mytextbox.datepicker({
            defaultDate: null,
            numberOfMonths: 1,
            dateFormat: DisplayDateFormat,
            onSelect: function (value, source) {
    
                var ctrl = document.getElementById(source.id);
    
                if ("dispatchEvent" in ctrl) {
    
                    // IE9
                    var evt = document.createEvent("HTMLEvents");
                    evt.initEvent("change", false, true);
                    ctrl.dispatchEvent(evt);
                } else if ("fireEvent" in ctrl) {
    
                    // IE7/IE8
                    ctrl.fireEvent("onchange");
                } else {
    
                    $(ctrl).change();
                }
            }
        });
    

    更新:看来这种方法不再有效 - 不确定原因。它会停止抛出错误,但不会触发更改事件。