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

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

编辑添加:

  • 有问题的图书馆是 模拟时间轴 及其 Ajax 库;
  • 事件处理程序确实调用 SimileAjax.DOM.cancelEvent(domEvt), ,我认为根据名称可以取消事件的冒泡;
  • 只是为了让事情变得更复杂,我实际上正在做的是开始一个超时,如果没有被取消的话 moustout 显示弹出窗口,这是为了防止弹出窗口烦人地闪烁,但会产生相反的效果。

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

有帮助吗?

解决方案

是的,Javascript 是单线程的。即使使用像 Google Chrome 这样的浏览器,每个选项卡也有一个线程。

如果不知道如何尝试从另一个弹出窗口中取消一个弹出窗口,则很难说出问题的原因是什么。

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

其他提示

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

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

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

与 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));

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

不管怎样,希望这会有所帮助。

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

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

这是或多或少的工作版本。创建项目时,我们附加一个 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 毫秒。不确定为什么超时太短会导致发生多气泡事件,但我猜想在布置新添加的元素时,窗口事件的排队或某些事情可能仍然会发生。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top