【问题标题】:How do you listen for two asynchronous, attached event handlers?你如何监听两个异步的、附加的事件处理程序?
【发布时间】:2014-06-20 22:17:44
【问题描述】:

我正在尝试监听两个 javascript 事件。两者都需要发生,但我不知道它们会以什么顺序发生。在这两种情况发生之前,我不想运行我的代码。我可以将延迟事件与 jQuery 的事件处理程序附加函数一起使用吗?

具体来说,我想等到带有远程加载内容的引导模式完成显示和加载。

点击此超链接显示模态,使用jquery的load方法加载超链接中的页面,并将加载页面的内容插入模态。

<a href="http://www.example.com" data-toggle="modal" data-target="#mymodal">Click Here</a>

目标模态:

<div class="modal fade" id="mymodal>
    <div class="modal-dialog">
        <div class="modal-content"></div>
    </div>
</div>

我尝试附加事件侦听器并使用承诺来了解两个事件何时触发的尝试失败。我认为这里的问题是承诺告诉我监听器已附加,而不是事件已触发。

var loaded = $(".modal").on('loaded.bs.modal', function(evt) {
    console.log("content loaded");
}).promise();
var shown = $(".modal").on('shown.bs.modal', function(evt) {
    console.log("modal visible");
}).promise();

$.when(loaded, shown).done(function(load, show) {
    console.log("content loaded and modal visible");
});

Demo on bootply

【问题讨论】:

  • @PSL 使用resolve() 完美运行。感谢您的帮助!
  • @Mr.Dave,据我所知,shown 事件似乎总是在loaded 事件之后触发。如果没有要显示的内容,则没有理由完成显示模式。我认为您只需收听shown 事件就安全了。

标签: jquery twitter-bootstrap events jquery-deferred


【解决方案1】:

.promise()fx 队列返回一个延迟承诺。由于您实际上并没有等待fx 队列,因此没有理由在这里使用该特定方法。

咨询bootstrap documentation

show.bs.modal - 此事件在调用 show 实例方法时立即触发。如果由单击引起,则单击的元素可用作事件的relatedTarget 属性。

shown.bs.modal - 当模式对用户可见时触发此事件(将等待 CSS 转换完成)​​。如果由单击引起,则单击的元素可用作事件的relatedTarget 属性。

loaded.bs.modal - 当模式使用远程选项加载内容时触发此事件。

Promise 是一次性的。解决后,您需要生成一个新的 Promise。

如果您知道事件将按特定顺序发生,那么依靠该顺序通常就足够了。例如,如果顺序总是:

  1. 显示
  2. 已加载
  3. 显示

然后将事件处理程序附加到shown 事件就足够了,因为它总是在loaded 事件之后发生。


我对文档的解释是,shown 事件将始终在loaded 事件之后触发(为什么要显示尚未出现的内容?),这意味着您可以只听@987654333 @事件,完全忽略loaded


由于我不能 100% 确定 实际 行为,为了这篇文章,我假设顺序也可能是:

  1. 显示
  2. 显示
  3. 已加载

我已将show 包含在列表中,因为它是一个一致的起点。在show 事件触发之后,您不会收到shownloaded 事件。这很有帮助,因为您可以使用它来生成新的延迟。

$('.modal').on('show.bs.modal', function () {
    var $this,
        data;
    $this = $(this);
    data = $this.data();
    if (data.shownDeferred) {
        //if this modal had been opened before, but never finished
        //showing it should be cancelled
        data.shownDeferred.reject();
    }
    data.shownDeferred = $.Deferred();
    if (data.loadedDeferred) {
        //if this modal had been opened before, but never loaded
        //it should be cancelled
        data.loadedDeferred.reject();
    }
    data.loadedDeferred = $.Deferred();

    $.when(data.shownDeferred, data.loadedDeferred).then(function () {
        $this.trigger('yourcustomeventname');
    });
}).on('loaded.bs.modal', function () {
    var dfd;
    dfd = $(this).data('loadedDeferred');
    if (dfd) {
        dfd.resolve();
    }
}).on('shown.bs.modal', function () {
    var dfd;
    dfd = $(this).data('shownDeferred');
    if (dfd) {
        dfd.resolve();
    }
});

通过上述设置,您可以将模式设置为在 loadedshown 事件都完成触发时触发您自己的自定义事件。


警告我没有测试过这段代码,所以可能有问题

【讨论】:

  • 包含节目是一个聪明的主意。我通常在“显示”之前看到“加载”,但我不相信引导程序有任何理由订购模型的动画和 jQuery 的加载。我现在正在测试这种方法,我会告诉你它是怎么回事。
  • 这很好用。正如您所提到的,生成新的 Deferreds 而不是 Promise 可以重复触发新事件。
【解决方案2】:

对于这个问题,Deferreds/Promises 就像一把大锤敲碎了坚果。即使不知道事件的顺序,也可以用布尔值而不是 Promise 来解决整个问题。

但是,事件的顺序是已知的。运行 test,您可以看到初始“表演”的行为与后续“表演”不同。事件顺序如下:

  • 首场演出 - 'show'、'loaded'、'shown'
  • 在随后的节目中 - 'show', 'shown'

换句话说,loaded 是一次性事件,shown 可靠地是最后一个事件。

因此,您可以简单地收听shown

$('.modal').on('shown.bs.modal', function () {
    //Do awesome stuff here
});

如果您想在模态显示之前抑制进一步的“显示”,那么它会稍微复杂一些,但也不算太糟糕 - 可能是这样的:

$('.modal').on('show.bs.modal', function () {
    var $this = $(this),
        data = $this.data('modal');
    if (!data) {
        data = $this.data('modal') = {};
    }
    if(!data.pending) {
        data.pending = true;
        $this.one('shown.bs.modal', function() {
            data.pending = false;
            //Do awesome stuff here
        });
    }
});

稍加思考,这可能会简化。

【讨论】:

    猜你喜欢
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多