3

基本上,我将一个open处理程序附加到我的 jQuery UI 工具提示,它对触发工具提示的元素执行一些检查。到目前为止我得到了什么:

$(document).tooltip({
  open: function(e, ui) {
    var el = e.toElement/* || e.relatedTarget*/;
    console.log(el.offsetWidth, el.scrollWidth);
    if (el.offsetWidth === el.scrollWidth) {
      ui.tooltip.hide();
    }
  }
});

上面的检查可以防止工具提示出现,除非元素水平溢出,这是流体布局的一部分。正如您在这个jsBin中看到的那样,它在 Chrome 中运行良好。

但是,在 Firefox 中,event.toElementundefined. 通过阅读围绕 SO 的主题,我认为这event.relatedTarget将是一个合适的替代品,但事实并非如此。在event.toElement引用当前悬停的项目时,event.relatedTargetin amouseover引用了指点设备退出的元素,这是根据W3C 规范的正确行为(类似于 Chrome 的event.fromTarget)。

我也尝试过event.target,但是event.currentTarget这些this引用指向document它是绑定了工具提示事件处理程序的节点。浏览Tooltip API页面也无济于事。

我不确定我是否忽略了一些非常基本的东西,或者我是否应该尝试更少的正统方法。

有没有办法从open在 Firefox 中工作的工具提示处理程序内部获取对触发元素的引用?或者是否有一些神奇的 jQuery UI 工具提示选项/方法可以以更简单/类似的方式实现这种期望的行为?

4

2 回答 2

7

这是一个相当骇人听闻的解决方案,但这是我刚刚找到的临时修复。编辑:在完成了下面的第一个跨浏览器解决方案之后,它根本就不那么骇人听闻了。下面列出的#1#4#2解决方案应该是可用的。

jQuery Event 对象有一个 hiddenoriginalEvent属性,在问题的情况下,它是一个对本机mouseover事件的引用。因此event.originalEvent.target可用于 Chrome 和 Firefox。

open: function(e, ui) {
  var el = e.originalEvent.target;
  if (el.offsetWidth === el.scrollWidth) {
    ui.tooltip.hide();
  }
}

垃圾桶

当涉及到旧的 IE 支持时,您将不得不使用event.srcElementwhen event.targetis not present。

var el = e.originalEvent.target || e.originalEvent.srcElement;

垃圾桶


#1 跨浏览器解决方案

最后,当触发工具提示的元素内有嵌套元素时,您必须使用.closest()传递与工具提示的委托选择器相同的过滤器的方法来对其进行猴子补丁(items选项,默认[title]:not([disabled])为 UI 1.10.2):

var el = $(e.originalEvent.target || e.originalEvent.srcElement).closest($(this).tooltip('option', 'items'))[0];

垃圾桶

这基本上是 Tooltip Widget 在内部执行的操作,如此处所示


#2 替代的跨浏览器解决方案

通过使用简单的 DOM 查询不需要这么多变通方法的替代解决方案:

var el = $('[aria-describedby="'+ui.tooltip[0].id+'"]')[0];

垃圾桶


#3 内部方法滥用

这不应该被使用,但是通过覆盖内部_open方法,您可以访问一个targetjQuery 对象,该对象包含作为参数传递给它的事件的目标元素。问题是,您甚至无法访问它的tooltip小部件,.tooltip('widget')因为它是“未创建”的,尽管它已经存在于 DOM 中。您可以使用内部_find方法来解决它,该方法将通过 ID 执行DOM 查询,因此您可以hideshow动画开始后立即执行它,而它的生命周期不受影响 - 它会mouseleave像往常一样在那里并被删除,但会沿着这个display:none循环。

var bk_open = $.ui.tooltip.prototype._open;
$.ui.tooltip.prototype._open = function(event, target, content) {
  bk_open.apply(this, arguments);
  if (target[0].offsetWidth === target[0].scrollWidth) {
    this._find(target).hide();
  }
};

$(document).tooltip();

垃圾桶

那个 DOM 查询_find是相当不必要的,所以我们也可以扩展内部_tooltip方法,它返回一个包含该元素的 jQuery 对象,这样我们就可以在我们的覆盖执行tooltip之前使用 JS 的词法范围来保存对工具提示元素的引用:_open

var tooltipproto = $.ui.tooltip.prototype,
    bk_open = tooltipproto._open,
    bk_tooltip = tooltipproto._tooltip,
    $tooltip;
tooltipproto._open = function(event, target, content) {
  bk_open.apply(this, arguments);
  if (target[0].offsetWidth === target[0].scrollWidth) {
    $tooltip.hide();
  }
};
tooltipproto._tooltip = function(element) {
  return ($tooltip = bk_tooltip.apply(this, arguments));
};
$(document).tooltip();

垃圾桶

当然,由于_tooltip内部方法接收targetas 参数并返回tooltip,因此可以仅覆盖此方法来执行整个操作,但是由于此方法返回后tooltipshown ,因此这setTimeout(fn, 0)可能会导致不希望的闪烁效果。

对于如此简单的事情来说,这过于hackish、繁琐和冗长。


#4 清洁解决方案

“干净”,即不使用未记录的方法、属性、原型覆盖或 DOM 查询。回到第一个片段,我们所需要的只是对触发工具提示的元素的引用。该元素由在处理程序之前调用this的函数内部引用,因此我们可以使用词法范围来存储该引用的上一级:contentopen

var el;
$(document).tooltip({
  content: function() {
    el = this;
    return this.title;
  },
  open: function(e, ui) {
    if (el.offsetWidth === el.scrollWidth) {
      ui.tooltip.hide();
    }
  }
});

垃圾桶

请注意,我content在上面的代码片段中的自定义函数删除了 jQuery 的默认 HTML 标记剥离(因为我喜欢在工具提示中使用 HTML),但是如果您使用用户输入的数据动态填充标题属性,这可能是一个问题,所以以防万一您想保留原始content处理程序的功能:

var el,
    bk_content = $.ui.tooltip.prototype.options.content;
$(document).tooltip({
  content: function() {
    el = this;
    return bk_content.apply(this, arguments);
  },
  open: function(e, ui) {
    if (el.offsetWidth === el.scrollWidth) {
      ui.tooltip.hide();
    }
  }
});

垃圾桶


我将在 jQuery UI bugtracker 上开一张票,要求同时实现此功能。这是它:

工具提示:公开在打开/关闭处理程序中触发工具提示的元素

于 2013-04-22T04:04:16.053 回答
2

只是为了完整起见:小部件包含属性tooltips,一个对象,其中包含所有打开的工具提示的 id 作为键和关联的目标元素作为值。

var widget = $(this).data("ui-tooltip");
var el = widget.tooltips[ui.tooltip[0].id][0];

它很干净,因为您可以直接引用元素,但也有点脏,因为它是一个可能会更改的实现细节。

也就是说,类似的东西ui.target确实很好。

于 2013-04-22T09:20:08.453 回答