6

DIV我有一个带有s的网页,其中包含一个mouseover旨在显示弹出信息气泡的处理程序。我不希望一次显示多个信息气泡。但是当用户将鼠标快速移动到两个项目上时,有时会出现两个气泡。这不应该发生,因为显示弹出窗口的代码取消了之前的弹出窗口。

如果这是一个多线程系统,那么问题将很明显:有两个线程试图显示一个弹出窗口,它们都取消现有的弹出窗口,然后弹出自己的弹出窗口。但我假设 JavaScript 始终是单线程运行的,这会阻止这种情况。我错了吗?事件处理程序是否异步运行,在这种情况下我需要同步访问共享数据,或者我应该在库代码中寻找错误以取消弹出窗口?

编辑添加:

  • 有问题的库是SIMILE Timeline及其 Ajax 库;
  • 事件处理程序确实调用SimileAjax.DOM.cancelEvent(domEvt)了,我假设基于名称取消了事件的冒泡;
  • 只是为了让事情变得更复杂,我实际上正在做的是开始一个超时,如果没有被moustout显示弹出窗口取消,这是为了防止弹出窗口令人讨厌地闪烁但令人讨厌地产生相反的效果。

我会再戳一下,看看我能不能找出哪里出错了。:-)

4

7 回答 7

9

是的,Javascript 是单线程的。即使使用像谷歌浏览器这样的浏览器,每个标签也有一个线程。

在不知道您是如何尝试取消另一个弹出窗口的情况下,很难说出问题的原因是什么。

如果您的 DIV 相互嵌套,您可能会遇到事件传播问题。

于 2008-10-02T11:23:09.927 回答
2

我不知道您正在使用的库,但如果您只是想一次显示一个工具提示......请使用享元对象。基本上,蝇量级是制造一次并反复使用的东西。想想一个单例类。因此,您静态调用一个类,该类在第一次调用时会自动创建一个自身的对象并存储它。一种情况是,每个静态都引用同一个对象,因此您不会得到多个工具提示或冲突。

我使用 ExtJS,他们将工具提示和消息框作为享元元素。我希望你的框架也有轻量元素,否则你只需要制作自己的单例并调用它。

于 2009-05-20T05:47:39.247 回答
1

它在浏览器中是单线程的。事件处理程序在一个线程中异步运行,非阻塞并不总是意味着多线程。你的一个div是另一个的孩子吗?因为事件在 dom 树中像气泡一样从子节点传播到父节点。

于 2008-10-02T11:24:47.867 回答
1

和 pkaeding 说的类似,不看你的标记和脚本就很难猜出问题所在;但是,我敢说您没有正确停止事件传播和/或您没有正确隐藏现有元素。我不知道您是否使用了框架,但这里有一个使用 Prototype 的可能解决方案:

// maintain a reference to the active div bubble
this.oActiveDivBubble = null;

// event handler for the first div
$('exampleDiv1').observe('mouseover', function(evt) {
    evt.stop();
    if(this.oActiveDivBubble ) {
        this.oActiveDivBubble .hide();
    }
    this.oActiveDivBubble = $('exampleDiv1Bubble');
    this.oActiveDivBubble .show();

}.bind(this));

// event handler for the second div
$('exampleDiv2').observe('mouseover'), function(evt) {
    evt.stop();
    if(this.oActiveDivBubble) {
        this.oActiveDivBubble.hide();
    }
    this.oActiveDivBubble = $('exampleDiv2Bubble');
    this.oActiveDivBubble .show();
}.bind(this));

当然,这可以通过获取具有相同类的所有元素、遍历它们并对每个元素应用相同的事件处理函数来进一步概括。

无论哪种方式,希望这会有所帮助。

于 2008-10-02T11:33:21.947 回答
1

仅供参考:从 Firefox 3 开始,有一个与此讨论非常相关的更改:导致同步 XMLHttpRequest 请求的执行线程被分离(这就是接口在同步请求期间不会冻结在那里的原因)并且执行继续。同步请求完成后,其线程也会继续。它们不会同时执行,但是依赖于单线程停止而同步过程(请求)发生的假设不再适用。

于 2008-10-02T12:41:08.633 回答
0

可能是显示器刷新速度不够快。根据您使用的 JS 库,您可能可以对弹出的“显示”效果稍加延迟。

于 2008-10-02T11:32:43.337 回答
0

这是工作版本,或多或少。创建项目时,我们附加一个mouseover事件:

var self = this;
SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mouseover", function (elt, domEvt, target) {
    return self._onHover(labelElmtData.elmt, domEvt, evt);
});

这会调用一个设置超时的函数(不同项目的预先存在的超时首先被取消):

MyPlan.EventPainter.prototype._onHover = function(target, domEvt, evt) {            
    ... calculate x and y ...
    domEvt.cancelBubble = true;
    SimileAjax.DOM.cancelEvent(domEvt);
    this._futureShowBubble(x, y, evt);

    return false;
}
MyPlan.EventPainter.prototype._futureShowBubble = function (x, y, evt) {
    if (this._futurePopup) {
        if (evt.getID() == this._futurePopup.evt.getID()) {
            return;
        } else {
            /* We had queued a different event's pop-up; this must now be cancelled. */
            window.clearTimeout(this._futurePopup.timeoutID);
        } 
    }
    this._futurePopup = {
        x: x,
        y: y,
        evt: evt
    };    
    var self = this;
    this._futurePopup.timeoutID =  window.setTimeout(function () {
            self._onTimeout();
    }, this._popupTimeout);
}

如果它在被取消之前触发,这反过来会显示气泡:

MyPlan.EventPainter.prototype._onTimeout = function () {
    this._showBubble(this._futurePopup.x, this._futurePopup.y, this._futurePopup.evt);

};

MyPlan.EventPainter.prototype._showBubble = function(x, y, evt) {
    if (this._futurePopup) {
        window.clearTimeout(this._futurePopup.timeoutID);
        this._futurePopup = null;
    }        
    ...

    SimileAjax.WindowManager.cancelPopups();
    SimileAjax.Graphics.createBubbleForContentAndPoint(...);
};

现在这似乎可行,我将超时设置为 200 毫秒而不是 100 毫秒。不知道为什么超时太短会导致多气泡的事情发生,但我猜窗口事件的排队或在布置新添加的元素时可能仍在发生某些事情。

于 2008-10-02T13:24:57.530 回答