使用 div 包装 ASP.NET 网络表单会导致双重异步回发,但仅使用 jQuery,而不是正常的 DOM。为什么?

Wrapping ASP.NET web form with div causes double async postback, but only with jQuery, not normal DOM. Why?

为什么会这样?

  1. 采用带有单个 UpdatePanel 和按钮的普通 ASP.NET Web 表单。
  2. 在 ASP.NET 客户端 pageLoad 事件便利函数中(如果愿意,可以使用 jQuery DOM 准备好)将表单元素包装在新创建的 div 元素中使用 jQuery.
  3. 单击该按钮并注意 发送了两个异步 (xhr) post-backs(使用 F12 浏览器开发人员工具中的 Net 选项卡查看它们)。

下面是一些重现问题的示例代码:

<%@ Language="C#" %>
<!DOCTYPE html>
<html>
<head><title>Double async postback when form element wrapped in new div after page load</title></head>
<body>
    <form id="frmTest" runat="server">
        <asp:ScriptManager ID="smTest" runat="server" ScriptMode="Release" />
        <asp:UpdatePanel ID="upTest" runat="server">
        <ContentTemplate>
            <asp:Button ID="btnTest" runat="server" Text="Test Postback"/>
        </ContentTemplate>
        </asp:UpdatePanel>
    </form>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js" type="text/javascript"></script>
    <script type="text/javascript">
        function pageLoad() {
            // jQuery:
            $("<div></div>").insertBefore("form").append($("form"));
            // DOM-only:
            //document.body.insertBefore(document.createElement("div"), document.forms[0]).appendChild(document.forms[0]);
        }
    </script>
</body>
</html>

(我正在使用针对 .NET Framework 4.5.2 和 jQuery 1.7.1 的普通 web.config 文件。我能够在 Firefox 40.0.3 中重现此文件,Chrome 45.0.2454.85 和 IE 11。)

现在请注意,如果您注释掉 jQuery 行并取消注释 DOM-only 行,则只会发送一个 postback。

此外,如果您在页面加载之前将表单标记包装在 div 中(即在静态 HTML 中),它可以正常工作(没有双重 postback。)

我为什么要关心这个 matter/why?许多 jQuery 插件(例如 jPanelMenu)将 body 标签(或大部分)的全部内容包装在 div 中以确保相对定位的祖先。

因此,正如经常发生的那样,在将正确的问题与尽可能完整和简洁的演示代码放在一起的过程中,我相信我找到了答案。

我发现 如果我将 jQuery 升级到 1.9.0,问题就会消失。但是根据他们的文档,1.9.0 中的 many changes happened at 1.9.0 that could potentially break old plugins (which my project has several of.) I have a feeling that this problem is related to this change

Loading and running scripts inside HTML content

Prior to 1.9, any HTML-accepting method (e.g., $(), .append(), or .wrap()) executed any scripts in the HTML and removed them from the document to prevent them from being executed again. This still broke in situations where a script might be removed and reinserted into the document using methods such as .wrap(). As of 1.9, scripts inserted into a document are executed, but left in the document and tagged as already executed so they won't be executed again even if they are removed and reinserted.

每个带有 ScriptManager/UpdatePanel 控件的 ASP.NET Web 表单都有 WebResource.axd 和 ScriptResource.axd <script> 标签插入靠近表单元素内容的底部。这些提供了客户端 AJAX 框架。我相信 jQuery 在将表单节点附加到新的 div 节点时会重新加载这些脚本,因此会复制回发事件处理程序。我现在唯一的问题是为什么这对我来说如此难以察觉。我没有看到任何附加的事件处理程序,也没有看到脚本的额外查找(甚至缓存)。但这是一个单独的问题... :)