【问题标题】:What's the most efficient way to handle displaying a dialog/modal in JavaScript?在 JavaScript 中处理显示对话框/模式的最有效方法是什么?
【发布时间】:2011-12-01 09:10:22
【问题描述】:

[UPDATE:] 这里是一个测试链接(如果你不想克隆 repo)http://jsfiddle.net/integralist/g9EPu/

当鼠标悬停在网络应用中的某些链接上时,我需要显示很多对话框/模式。

目录 (tl;dr)

  • 我以前是怎么处理的
  • 我最近如何尝试
  • 哪个更好?
  • 鼠标进入/离开呢?

我以前是怎么处理的

我通常这样做的方式是使用事件委托。

所以我将一个事件处理程序添加到容器中,然后检查相关元素是否成为目标,然后显示相关对话框。

我通常有一个对话框,我可以更改内容并重新定位(保存有许多不同的 HTML 标记)。

如果mouseover 事件(用于链接)被触发,那么我会显示对话框。

如果mouseout 事件(用于链接)被触发,那么我将隐藏对话框。

如果我mouseout 触发了事件处理程序的链接,那么我通常需要设置一个计时器来延迟隐藏对话框(足够长),这样我就可以将鼠标悬停在对话框上,该对话框本身会清除由@设置的计时器987654329@的链接。

然后我将mouseout 事件绑定到对话框,这样当用户将鼠标从对话框上移开时,我就可以隐藏对话框。

在这个阶段我遇到了两个问题,第一个几乎一直在发生,另一个是我最近注意到的一个边缘案例,这促使我尝试寻找更好的解决方案......

  1. 对话框有“x”个子元素,将鼠标滚动到子元素上会导致触发对话框的mouseout 事件,因此我需要检查该元素是否有父元素对话框本身,如果是,请不要尝试隐藏对话框。

  2. 元素上使用这种技术时,我发现当鼠标移动得太快时,不会触发 mouseout/over 事件。

    我最近如何尝试

    例如代码参见:https://github.com/Integralist/Mouse-Over-Out-Script(您应该能够克隆 repo 并在本地运行 index.html 文件以查看发生了什么)

    但要简单解释一下……

    我们将mousemove 事件绑定到document.documentElement 元素(但如果需要,您可以在document.body 上执行此操作),然后我们存储鼠标位置的x/y 坐标。我们提供对“检查”方法的公共 API 访问,该方法让我们知道鼠标的位置是否在我们提供给“检查”的元素上方(我们测量元素尺寸并将其添加到其 x/y 坐标)。

    在上面的 repo 中,我们有一个日历,当特定日期有事件时,它会显示一个对话框。我们存储了所有有事件的

    并且我们为每个 设置了一个计时器(这是因为我们需要不断调用 'check' 方法来查看那个 将鼠标悬停在上面)。

    因此可能有 31+(因为我们显示的是下个月的前几天)显示对话框的机会,因此设置了 31+ 个计时器!

    这个示例代码库现在可以工作,而我使用事件委托的第一个版本不是。

    哪个更好?

    我担心mousemove 版本的性能,因为它可能会使用大量计时器(取决于您在单个页面中需要多少对话框)。在我上面的日历示例中,最多可以运行 31 个以上的计时器!

    鼠标进入/离开呢?

    我知道这些事件存在,如果所有浏览器都支持它,那么我可以安全地使用第一个版本,而不必检查导致错误的 mouseout/over 事件被触发的子元素。但无论如何,我不相信这会修复事件日历的示例,其中移动鼠标过快意味着浏览器不会触发

    的 mouseout/over 事件。无论哪种方式,我知道您可以填充它,因为 jQuery 提供了 mouseenter/leave 事件,但是查看他们的代码我无法让它适用于我的脚本(因为我不使用 jQuery 或任何其他通用库 - ps,并且我不希望,所以请不要建议作为选项)。

    非常感谢有人可以为我提供任何帮助/建议或指导。

    【问题讨论】:

    • 与其要求我们为您的文件制作本地副本来测试问题,不如您在jsfiddle.netjsbin.com 上为我们设置它
    • 更新了链接,但如果你错过了,这里又是:jsfiddle.net/integralist/g9EPu
    • 你能重组你的HTML吗?如果给定的 events 部分/div 元素是其关联的日历日期元素的子元素,这将使您的鼠标悬停事件管理更简单。如果在你的 JS 运行之前不能改变 HTML,你会考虑在之后改变它来达到同样的目的吗?
    • @thisgeek 我特意使用了一个弹出窗口
      来处理所有事件,因为另一种方法是将 31 多个弹出窗口元素(及其所有 HTML)加载到页面,这会将页面权重增加到不可接受的水平 (imo)。

标签: javascript modal-dialog dom-events mouseevent mousemove


【解决方案1】:

对话框有“x”个子元素,将鼠标滚动到子元素上会导致触发对话框的 mouseout 事件,因此我需要检查元素是否有父元素,即对话框本身,如果是,则不要尝试隐藏对话框。

要解决这个问题:在您的事件代码中,只需使用函数“isAncestor”(见下文)

/*
 * element = the "target" in your mouseout event handler
 * other = the node you really want to check if you're over
 */
isAncestor: function(element, other)
{
    while ( element && element != other ) element = element.parentNode;
    return ( element != null && element != undefined );
}

所以在你的元素的鼠标移出代码中(我们称之为“itemElement”),你会像这样检查它:

//We're really mousing out, close dialog
if ( !isAncestor( mouseOutEvent.target, itemElement ) )
{
    ...do something ...
}

【讨论】:

  • 这适用于为每个具有“has-events”类的单个 绑定鼠标悬停/移出事件但在使用事件委托时不起作用(例如绑定表元素的单个事件,然后检查鼠标悬停的元素是否是具有“has-events”类的td)。
  • 例如,如果我使用事件委托,那么当我将鼠标滚动到具有“has-events”类的 td 上时,它会显示对话框,但如果我稍微移动鼠标,我最终可能会移动到该 td 的子元素上,这会导致 td 的 mouseout 事件,然后是子元素上的 mouseover 事件。使用您的 isAncestor 函数,我无法告诉它哪个 td 是我真正检查鼠标的那个(不存储对它的引用)。
  • I have no way to tell it which td is the one I'm really checking mouse out for (without storing a reference to it) 不,实际上很简单。当触发 mouseout 时,只需检查目标的祖先是否具有“has-events”。如果是这样,那么您完全取消/静音 mouseout 事件。
  • 顺便说一句,您的建议几乎有效,但当用户太快地从一个元素移出时,因为 mouseout 事件没有触发(或在下一个元素上拾取鼠标悬停),您的建议几乎有效。有关示例,请参见 jsfiddle.net/PA2SY
  • 另外,这个讨论与我最初的观点之一“2.当在 元素上使用这种技术[即事件委托]时,我发现当鼠标移动时太快了 mouseout/over 事件没有被触发” - 因此我选择了 mousemove 尝试。似乎使用您的 isAncestor() 函数的最后一个示例遇到了同样的问题。
猜你喜欢
相关资源
最近更新 更多
热门标签