父元素或更高 DOM 祖先的单个事件监听器:
有一个相对简单的解决方案我最初没有提到,因为我假设您认为在您的情况下不可行而将其驳回。该解决方案是,不是在单个子元素上分别有多个侦听器,而是在祖先元素上拥有一个侦听器,该侦听器会被其子元素上的所有事件调用。它可以设计为基于event.target、event.target.id 或更好的event.target.className 快速选择进一步处理(如果元素是事件处理程序的有效目标,则分配您创建的特定类)。根据您的事件处理程序正在做什么以及您已经在使用侦听器的祖先元素的百分比,单个事件处理程序可以说是更好的解决方案。拥有一个监听器可能减少了事件处理的开销。但是,任何实际的性能差异取决于您在事件处理程序中所做的事情以及您将在其上放置侦听器的祖先的孩子的百分比。
真正感兴趣的元素的事件监听器
您的问题询问您的代码 放置在被移动元素上的监听器。鉴于您似乎并不关心通过您无法控制的代码放置在元素上的侦听器,那么解决此问题的蛮力方法是让您保留一个侦听器列表以及放置它们的元素。
实施这种蛮力解决方法的最佳方法很大程度上取决于您将侦听器放置在元素上的方式、您使用的种类等。这是我们无法从问题中获得的所有信息。如果没有这些信息,就不可能对如何实现这一点做出已知的好选择。
仅使用通过selection.on() 添加的每种类型/命名空间的单个侦听器all:
如果每个 type.namespace 有一个监听器,并且通过 d3.selection.on() 方法全部添加,并且没有使用 Capture 类型的监听器,那么其实还是比较简单的。
当每种类型只使用一个监听器时,selection.on() 方法允许您读取分配给元素和类型的监听器。
因此,您的moveToFront() 方法可能变为:
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var typesOfListenersUsed = [ "click", "command", "mouseover", "mouseleave", ...];
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
var currentListeners={};
if(isIE) {
var element = this;
typesOfListenersUsed.forEach(function(value){
currentListeners[value] = element.selection.on(value);
});
}
this.parentNode.appendChild(this);
if(isIE) {
typesOfListenersUsed.forEach(function(value){
if(currentListeners[value]) {
element.selection.on(value, currentListeners[value]);
}
});
}
});
};
您不一定需要检查 IE,因为在其他浏览器中重新放置侦听器应该不会有什么坏处。但是,这会花费时间,最好不要这样做。
您应该能够使用它,即使您使用相同类型的多个侦听器,只需在侦听器列表中指定一个命名空间即可。例如:
var typesOfListenersUsed = [ "click", "click.foo", "click.bar"
, "command", "mouseover", "mouseleave", ...];
一般,同一类型的多个侦听器:
如果您使用不是通过d3 添加的侦听器,那么您需要实现一个通用方法来记录添加到元素的侦听器。
如何记录作为监听器添加的函数,你可以在原型中添加一个方法来记录你作为监听器添加的事件。例如:
d3.selection.prototype.recOn = function (type, func) {
recordEventListener(this, type, func);
d3.select(this).on(type,func);
};
然后使用d3.select(el).recOn('mouseleave',function(){}) 而不是d3.select(el).on('mouseleave',function(){})。
鉴于您使用的是通用解决方案,因为您不是通过d3 添加一些侦听器,因此您需要添加函数来包装调用,但您正在添加侦听器(例如addEventListener())。
然后,您需要一个函数,在 moveToFront() 中的 appendChild 之后调用该函数。它可以包含 if 语句以仅恢复侦听器if the browser is IE11, or IE。
d3.selection.prototype.restoreRecordedListeners = function () {
if(isIE) {
...
}
};
您将需要选择如何存储记录的侦听器信息。这在很大程度上取决于您如何实现我们不知道的代码的其他区域。可能记录元素上哪些监听器的最简单方法是在监听器列表中创建一个索引,然后将其记录为一个类。如果您使用的实际不同侦听器函数的数量很少,这可能是一个静态定义的列表。如果数量和种类很大,那么它可能是一个动态列表。
我可以对此进行扩展,但使其真正的健壮程度取决于您的代码。它可以简单到只处理 5 到 10 个实际不同的函数,您可以将它们用作侦听器。它可能需要像一个完整的通用解决方案一样健壮,以记录任何可能数量的听众。这取决于我们不了解您的代码的信息。
我希望其他人能够为您提供一个简单易用的 IE11 修复程序,您只需设置一些属性,或者调用一些方法来让 IE 不丢弃侦听器。但是,蛮力方法会解决这个问题。