如果在DOM周围移动,SVG元素将丢失事件处理程序

Ser*_*rov 16 html javascript internet-explorer svg d3.js

我使用这个D3片段将SVG g元素移动到rest元素的顶部,因为SVG渲染顺序取决于DOM中元素顺序,并且没有z索引:

d3.selection.prototype.moveToFront = function () {
  return this.each(function () {
    this.parentNode.appendChild(this);
  });
};
Run Code Online (Sandbox Code Playgroud)

我这样运行:

d3.select(el).moveToFront()
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果我添加一个D3事件监听器,d3.select(el).on('mouseleave',function(){})然后使用上面的代码将元素移动到DOM树的前面,所有事件监听器都在Internet Explorer 11中丢失,在其他浏览器中仍然可以正常工作.我该如何解决它?

Moo*_*oob 3

一种解决方案是使用事件委托。这种相当简单的范例在 jQuery 中很常见(这让我想到在这里尝试一下。)

通过使用委托事件侦听器扩展d3.selection原型,我们可以侦听父元素上的事件,但仅当事件的目标也是我们所需的目标时才应用处理程序。

所以而不是:

d3.select('#targetElement').on('mouseout',function(){})
Run Code Online (Sandbox Code Playgroud)

你会使用:

d3.select('#targetElementParent').delegate('mouseout','#targetElement',function(){})
Run Code Online (Sandbox Code Playgroud)

现在,当您移动元素时,或者即使在创建侦听器后添加/编辑/删除元素时,事件是否丢失也没关系。

这是演示。在 Chrome 37、IE 11 和 Firefox 31 上进行了测试。我欢迎建设性的反馈,但请注意,我对 d3.js一点也不熟悉,所以很容易错过一些基本的东西;)

//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetid, handler) {
    return this.on(event, function() {
        var eventTarget = d3.event.target.parentNode,
            target = d3.select(targetid)[0][0];
        if (eventTarget === target) {//only perform event handler if the eventTarget and intendedTarget match
            handler.call(eventTarget, eventTarget.__data__);
        }
    });
};    
//add event listeners insead of .on() 
d3.select('#svg').delegate('mouseover','#g2',function(){
    console.log('mouseover #g2');
}).delegate('mouseout','#g2',function(){
    console.log('mouseout #g2');
})    
//initial move to front to test that the event still works
d3.select('#g2').moveToFront();
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/f8bfw4y8/

更新和改进...

根据 Makyen 的有用反馈,我进行了一些改进,以允许将委派侦听器应用于所有匹配的子项。EG“监听 svg 内每个 g 上的鼠标悬停

这是小提琴。下面的片段。

d3.select('#targetElement').on('mouseout',function(){})
Run Code Online (Sandbox Code Playgroud)
d3.select('#targetElementParent').delegate('mouseout','#targetElement',function(){})
Run Code Online (Sandbox Code Playgroud)
//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetid, handler) {
    return this.on(event, function() {
        var eventTarget = d3.event.target.parentNode,
            target = d3.select(targetid)[0][0];
        if (eventTarget === target) {//only perform event handler if the eventTarget and intendedTarget match
            handler.call(eventTarget, eventTarget.__data__);
        }
    });
};    
//add event listeners insead of .on() 
d3.select('#svg').delegate('mouseover','#g2',function(){
    console.log('mouseover #g2');
}).delegate('mouseout','#g2',function(){
    console.log('mouseout #g2');
})    
//initial move to front to test that the event still works
d3.select('#g2').moveToFront();
Run Code Online (Sandbox Code Playgroud)

与所有委托侦听器一样,如果您将目标元素移到已委托侦听的父级之外,那么该子级的事件自然会丢失。但是,没有什么可以阻止您将事件委托给标签,body因为您永远不会将孩子移出该标签。例如:

d3.select('body').delegate('mouseover','g',function(){...
Run Code Online (Sandbox Code Playgroud)