jQuery的ready函数在对话框中被调用两次

11

我正在使用PHP脚本构建一个带有选项卡的jQuery对话框。该脚本在循环内使用“include”指令,遍历选项卡并包含其他脚本。每个包含的文件都具有选项卡数据和一个带有jQuery document.ready()函数的<script>标签。没有循环,它基本上是这样的:

<div id="tabDialog">
  <div id="tabs">
    <ul>
      <li><a href="#tab1'>Tab1</a></li>
      <li><a href="#tab2'>Tab2</a></li>
    </ul>
    <div id="tabContainer">
      <div id="tab1">
        <?php include "tab1.php"; ?>
      </div>
      <div id="tab2">
        <?php include "tab2.php"; ?>
      </div>
    </div>
  </div>
</div>

例如,tab1.php 可能会有以下内容:

<script type="text/javascript">
   $(document).ready (function () {
       alert ('tab1 loaded');
   });
</script>

问题在于,使用<div id="dialog">作为对话框的 DIV 创建和打开对话框时,文档的 ready 函数会再次被调用。以下是对话框代码:

 $("#tabDialog").dialog ({
   autoOpen: false,
   minWidth: 450,
   minHeight: 400,
   width: 600,
   height: 500
 }).dialog ('open');

这是什么原因,并且最好的解决方法是什么?我正在尝试将每个标签页的功能放在单独的文件中,因为它们可以在多种情况下使用,我不必复制与其相关的代码。

感谢任何帮助或建议。


有没有办法增加一些代码以查看更多的信息?仅凭这个很难弄清楚。 - spinon
由于您正在循环中,您确定在HTML中没有多次使用ID tabDialog吗?如果是这种情况,它将为每个#tabDialog div触发事件。 - pixeline
这是因为内容,包括脚本块被移动到了body的末尾(并再次执行)...虽然我不确定最好的纠正方法是什么,但所有这些脚本是否可以在单个外部文件中一次性包含? - Nick Craver
看起来你的JavaScript有障碍。这可能会有所帮助 - https://dev59.com/AU7Sa4cB1Zd3GeqP6L-I - konyak
7个回答

9

我相信我已经找到了原因并创建了一个相当不错的解决方案。当jQuery创建对话框时,它会在DOM中移动包含对话框内容的DIV(移到文档的最后)并用必要的脚手架包围该div以满足对话框的需求(可能是使用.append()函数或类似函数)。由于被动态包含JavaScript的DIV被移动到DOM中时,jQuery会在DIV被重定位后调用document.ready()函数(即第二次调用)。因此,在构建对话框之前,我会像这样删除对话框DIV内的每个脚本标签:

    $("#tabDialog").find ("script").remove ();
    $("#tabDialog").dialog ({
      autoOpen: true,
      minWidth: 450,
      minHeight: 400,
      width: 600,
      height: 500
    });

这样做会从最初加载的DIV中删除SCRIPT标签,但SCRIPT本身仍然存在。我仍在研究这个问题,因为我不完全理解动态加载的Javascript代码实际上“存放”在哪里,但我怀疑它位于DOM之外的某个地方。我在Chrome、Firefox和Exploder 8中验证了这一点。
我验证了最初包含在已加载DIV中的任何脚本仍然按预期工作,通过在DIV中放置一个按钮并分配一个.click()函数来演示这一点。这是一个小测试,可以证明这一点:
<html>
  <head>
    <link href="css/redmond/jquery-ui-1.8.1.custom.css" type="text/css" rel="stylesheet" media="screen" />
    <link href="css/style.css" type="text/css" rel="stylesheet" media="screen" />

    <script src="js/jquery-1.4.2.js" type="text/javascript"></script>
    <script src="js/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></script>
  </head>

  <body>
    <div id="dialogContents" style="display: none;">
      <div  style="border: 1px solid black; height: 98%;">
        <form id="testForm">
          <input type="text">
        </form>
        <button id="testButton">Test</button>
        <script type="text/javascript">
          $(document).ready (function () {
            alert ("ready");

            $("#testButton").click (function () {
              alert ('click');
            });
          });
        </script>
      </div>
    </div>
  </body>

  <script type="text/javascript">
    $(document).ready (function () {
      //
      // Remove all the scripts from any place in the dialog contents.  If we
      // do not remove the SCRIPT tags, the .ready functions are called a
      // second time.  Removing this next line of Javascript demonstrates this.
      //
      $("#dialogContents").find ("script").remove ();
      $("#dialogContents").dialog ({
        width: 300,
        height: 300,
        title: 'Testing...'
      });
    });
  </script>

</html>

我非常感谢这个帖子中提供的帮助!


1
这对我来说是个解决方法!我在一段HTML代码中有一个onready的<script>$(function(){...});</script>块,通过.dialog()转换成对话框。我的函数被调用了两次。将它移到被dialog()处理之外的HTML之外解决了这个问题。谢谢! - zombat
非常有帮助,理解为什么会发生这种情况。谢谢! - dansan

1
我也遇到了这个问题,但在我的情况下原因是不同的。我在用作对话框容器的 div 元素内部有一个自闭合的 div 元素。当我将自闭合元素替换为闭合标签时,文档准备好函数停止两次触发,只触发一次,如预期所示。
例如,这会导致文档准备好函数触发两次:
$("#foo").dialog({
  // ...
});

...

<div id="foo" title="My Dialog">
  <div id="bar" />
</div>

而这只会触发一次文档就绪函数:

$("#foo").dialog({
  // ...
});

...

<div id="foo" title="My Dialog">
  <div id="bar"></div>
</div>

1

我并没有太多使用 .dialog() 的经验,但是你的脚本中需要使用 jQuery 的 ready() 方法吗?

看起来 .dialog() 有一些回调选项可以利用。

标签页中的脚本:

    <script type="text/javascript">
        function onOpen() { alert('tab1 loaded') };
    </script>

对话框:

$(this).dialog ({
    autoOpen: false,
    minWidth: 450,
    minHeight: 400,
    width: 600,
    height: 500,
    open: function(event, ui) { onOpen(); } // call function in script
}).dialog ('open');

1

所以我必须说,即使我知道对话框确实维护了自己的状态,但我并不100%确定为什么会发生这种情况。这可能是其中之一的原因。但我可能完全错了。但解决方法是使用类似于这样的东西:

$(document).one('ready', function () {
   alert ('tab1 loaded');
});

这将确保它仅在页面加载时运行一次。


<div id="tabDialog"> <div id="tabs"> <div id="tabContainer"> <div id="tab1"> <script> $(document).one('ready', function () { alert('选项卡1已加载'); }); </script> </div> <div id="tab2"> <script> $(document).one('ready', function () { alert('选项卡2已加载'); }); </script> </div> </div> </div> </div> <a href="javascript:Launch();">测试</a> <script> function Launch() { $("#tabDialog").dialog({ autoOpen: false, }).dialog('open'); } </script> 这对我有用。将代码缩短以适应评论。 - spinon
忘了提到我是针对 jQuery 版本 1.3.2 运行这个的。 - spinon
你忘记将底部的document.ready更改为document.one了。我刚刚用那个更改运行了代码,它只运行了一次。不过它运行两次倒是很有趣。 - spinon
我在搜索另一个线程。jQuery对话框是否“克隆”了作为对话框使用的div?克隆是指完全复制DIV的内容吗?如果是这样,那么我认为问题就出在这里,因为脚本也会被复制,从而导致DOM中存在两次 :(. - Dave
@spinon - 对我来说,这两种方法都可以解决问题。我讨厌这个“特性”仍然是jQuery UI的一部分... - TheCloudlessSky
显示剩余2条评论

0
这是页面的结果文本。我查看了源代码,然后删除了页面上的任何多余内容,以使其更简单。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
    <head>
        <link href="css/redmond/jquery-ui-1.8.1.custom.css" type="text/css" rel="stylesheet" media="screen" />
        <link href="css/style.css" type="text/css" rel="stylesheet" media="screen" />
        <script src="js/jquery-1.4.2.min.js" type="text/javascript"></script>
        <script src="js/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></script>
    </head>

    <body>
        <div id="tabDialog" style="position: relative; display: none;" title="Test Dialog">
            <div id="tabs" style="position: absolute; top: 5px; bottom: 40px; left: 3px; right: 3px;">
                <ul>
                    <li><a href='#tab1'>Tab #1</a></li><li><a href='#tab2'>Tab #2</a></li>
                </ul>

                <div class="tab_container" style="position: absolute; top: 35px; bottom: 0px; left: 1px; right: 1px; overflow: auto;">
                    <div id='tab1' class='tabPage ui-dialog-content'>
                        <form id="tab1Form">
                            More testing... <input class="keypressMonitor" type="text">
                        </form>
                        Testing...<br/>
                        Testing...<br/>

                        <script type="text/javascript">
                            $(document).ready (function () {
                                alert ('tab1 loaded');
                                $("#tab1Form").bind ('save', function () {
                                    alert ("in tab1Form.save ()");
                                });
                            });
                        </script>
                    </div>

                    <div id='tab2' class='tabPage ui-dialog-content'>
                        <form id="tab2Form">
                            <div style="position: absolute; left: 1px; right: 1px; top: 1px; bottom: 1px;">
                                Testing: <input class="keypressMonitor" type="text">

                                <textarea id="testArea" class="keypressMonitor tinymce" style="position: absolute; top: 30px; bottom: 2px; left: 2px; right: 2px;"></textarea>
                            </div>
                        </form>

                        <script type="text/javascript">
                            $(document).ready (function () {
                                $("#tab2Form").bind ('save', function () {
                                    alert ("in tab2Form.save ()");
                                });
                            });
                        </script>
                    </div>
                </div>
            </div>

            <div id="dialogButtons" style="position: absolute; bottom: 3px; left: 3px; right: 15px; text-align: right; height: 32px;">
                <button class="applyButton" disabled>Apply</button>
                <button class="okButton" disabled>Ok</button>
                <button class="cancelButton">Cancel</button>
            </div>
        </div>

        <script type="text/javascript">
            $(document).ready (function () {
                $("#tabs").tabs ();
                $("button").button ();

                /**
                 * Pressing the cancel button simply closes the dialog.
                 */
                $(".cancelButton").click (function () {
                    $("#tabDialog").dialog ("close");
                });

                $("#tabDialog").dialog ({
                    open: function () {
                    },
                    autoOpen: true,
                    minWidth: 450,
                    minHeight: 400,
                    width: 600,
                    height: 500,
                    height: 'auto'
                });
            });
        </script>
    </body>
</html> 

0
将您的脚本放入create方法中:
$.dialog({
    <your parameters>
    create: function() {
        <your script>
    }
}

使用这种方法,您的脚本只会在创建对话框时被调用一次,而不是两次!

0

你可能不需要使用 .dialog('open') 方法,而是使用选项 autoOpen : true。


没错,但是每个ready()函数仍然被调用了两次 :(. - Dave
从技术上讲,您可以在同一页中有多个document.ready()调用。重要的是调用您的dialog()函数。请参阅我对您问题的评论。您能否向我们展示一个显示错误的页面的完整HTML代码? - pixeline
我会尝试看看能放在哪里,或者尝试创建一个更小的例子。我“认为”问题的根本原因是包含的 PHP 文件的<script>标签位于 DIV 内部,而该 DIV 本身位于对话框函数内部。如果我查看页面,复制结果源代码,并将脚本移出 DIV,则只调用一次。有关 jQuery 对话框的某些内容会导致 ready() 函数被再次调用。 - Dave
我将代码放在答案中,因为它太大了无法放在评论中。 - Dave

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