如果在DOM周围移动,SVG元素会丢失事件处理程序 [英] SVG element loses event handlers if moved around the DOM

查看:587
本文介绍了如果在DOM周围移动,SVG元素会丢失事件处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用这个D3代码片段将SVG g 移动到其余元素的顶部作为SVG呈现顺序取决于 DOM中元素的顺序,并且没有z索引:

  d3.selection.prototype.moveToFront = function(){
return this.each(function(){
this.parentNode.appendChild(this);
});
};

我运行它像:

  d3.select(el).moveToFront()

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

解决方案

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



通过扩展 d3.selection 原型与委托的事件监听器,我们可以侦听父元素上的事件,但如果事件的目标也是我们想要的目标,只应用处理程序。



而不是:

  d3.select('#targetElement')。on('mouseout',function(){})

您将使用:

  d3.select('#targetElementParent')。delegate('mouseout','#targetElement',function(){})

现在,当你移动元素或者在创建侦听器之后添加/编辑/删除元素时,事件是否会丢失并不重要。 / p>

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

  //原型。委托事件
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){//只有当eventTarget和intendedTarget匹配
handler.call(eventTarget,eventTarget .__ data__);
}
});
};
//添加事件监听器insead的.on()
d3.select('#svg')。delegate('mouseover','#g2',function(){
console。 log('mouseover#g2');
})delegate('mouseout','#g2',function(){
console.log('mouseout#g2');
})
//初始移动到前面以测试事件是否仍然有效
d3.select('#g2')。moveToFront();

http://jsfiddle.net/f8bfw4y8/



更新和改进...



以下Makyen的有用的反馈,我做了一些改进,以允许委派的监听器应用于所有匹配的孩子。 EG



这里是小提琴。下面的代码段。



  // prototype。移动到frontd3.selection.prototype.moveToFront = function(){return this.each(function(){this.parentNode.appendChild(this);});}; // prototype。 delegate eventsd3.selection.prototype.delegate = function(event,targetselector,handler){var self = this;返回this.on(event,function(){var eventTarget = d3.event.target,target = self.selectAll(targetselector); target.each(function(){//如果eventTarget和intendedTarget匹配, (eventTarget.parentNode,eventTarget.parentNode .__ data__);}});}}}};} ;});}; var testmessage = document.getElementById(testmessage); //添加事件监听器insead .on()// EG:onmouseover / out of ANY< g> #svg:d3.select('#svg')。delegate('mouseover','g',function(){console.log('mouseover',this); testmessage.innerHTML =mouseover#+ this。 id;})。delegate('mouseout','g',function(){console.log('mouseout',this); testmessage.innerHTML =mouseout#+ this.id;}); / *注意:添加另一个.delegate侦听器替换此节点上此事件的任何现有侦听器。取消注释以查看。 // EG2 onmouseover of the#g3d3.select('#svg')。delegate('mouseover','#g3',function(){console.log('mouseover of just#g3',this); testmessage。 innerHTML =mouseover#+ this.id;}); //解析这个代理监听另一个父节点例如://d3.select('body').delegate('mouseover','#g3' ,function(){... * ///初始移动到前面测试。OP声明监听器在DOM.d3.select('#g2')中移动后丢失。moveToFront();  

  svg {height:300px; width:300px;} rect { fill:pink;}#g2 rect {fill:green;}#testmessage {position:absolute; top:50px; right:50px;}   < script src =https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js >< / script>< svg id =svg>< g id =g1>< rect x =0pxy =0pxwidth =100pxheight =100px />< / g>< g id =g2>< rect x =50pxy =50pxwidth =100pxheight =100px/>< / g> < g id =g3>< rect x =100pxy =100pxwidth =100pxheight =100px/>< / g>< / svg>< div id =testmessage>< / div>  



所有委托侦听器,如果你移动目标元素 之外的你委托监听的父亲,那么该子系统的事件自然会丢失。然而,没有什么能阻止你委托侦听 body 标签的事件,因为你永远不会移动一个孩子。 EG:

  d3.select('body')。delegate('mouseover','g',function(){。 .. 


I use this D3 snippet to move SVG g elements to top of the rest element as SVG render order depends on the order of elements in DOM, and there is no z index:

d3.selection.prototype.moveToFront = function () {
  return this.each(function () {
    this.parentNode.appendChild(this);
  });
};

I run it like:

d3.select(el).moveToFront()

My issue is that if I add a D3 event listener, like d3.select(el).on('mouseleave',function(){}), then move the element to front of DOM tree using the code above, all event listeners are lost in Internet Explorer 11, still working fine in other browsers. How can I workaround it?

解决方案

One solution could be to use event delegation. This fairly simple paradigm is commonplace in jQuery (which gave me the idea to try it here.)

By extending the d3.selection prototype with a delegated event listener we can listen for events on a parent element but only apply the handler if the event's target is also our desired target.

So instead of:

d3.select('#targetElement').on('mouseout',function(){})

You would use:

d3.select('#targetElementParent').delegate('mouseout','#targetElement',function(){})

Now it doesn't matter if the events are lost when you move elements or even if you add/edit/delete elements after creating the listeners.

Here's the demo. Tested on Chrome 37, IE 11 and Firefox 31. I welcome constructive feedback but please note that I am not at all familiar with d3.js so could easily have missed something fundamental ;)

//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();

http://jsfiddle.net/f8bfw4y8/

Updated and Improved...

Following Makyen's useful feedback I have made a few improvements to allow the delegated listener to be applied to ALL matched children. EG "listen for mouseover on each g within svg"

Here's the fiddle. Snippet below.

//prototype. move to front
d3.selection.prototype.moveToFront = function () {
  return this.each(function () {
    this.parentNode.appendChild(this);
  });
};

//prototype. delegated events
d3.selection.prototype.delegate = function(event, targetselector, handler) {
    var self = this;
    return this.on(event, function() {
        var eventTarget = d3.event.target,
            target = self.selectAll(targetselector);
        target.each(function(){ 
            //only perform event handler if the eventTarget and intendedTarget match
            if (eventTarget === this) {
                handler.call(eventTarget, eventTarget.__data__);
            } else if (eventTarget.parentNode === this) {
                handler.call(eventTarget.parentNode, eventTarget.parentNode.__data__);
            }
        });
    });
};


var testmessage = document.getElementById("testmessage");
//add event listeners insead of .on() 
//EG: onmouseover/out of ANY <g> within #svg:
d3.select('#svg').delegate('mouseover','g',function(){
    console.log('mouseover',this);
    testmessage.innerHTML = "mouseover #"+this.id;
}).delegate('mouseout','g',function(){
    console.log('mouseout',this);
    testmessage.innerHTML = "mouseout #"+this.id;
});

/* Note: Adding another .delegate listener REPLACES any existing listeners of this event on this node. Uncomment this to see. 
//EG2 onmouseover of just the #g3
d3.select('#svg').delegate('mouseover','#g3',function(){
    console.log('mouseover of just #g3',this);
    testmessage.innerHTML = "mouseover #"+this.id;
});
//to resolve this just delegate the listening to another parent node eg:
//d3.select('body').delegate('mouseover','#g3',function(){...
*/


//initial move to front for testing. OP states that the listener is lost after the element is moved in the DOM.
d3.select('#g2').moveToFront();

svg {height:300px; width:300px;}
rect {fill: pink;}
#g2 rect {fill: green;}
#testmessage {position:absolute; top:50px; right:50px;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="svg">
    <g id="g1"><rect x="0px" y="0px" width="100px" height="100px" /></g>
    <g id="g2"><rect x="50px" y="50px" width="100px" height="100px" /></g>
    <g id="g3"><rect x="100px" y="100px" width="100px" height="100px" /></g>
</svg>
<div id="testmessage"></div>

As with all delegated listeners if you move the target element outside of the parent you have delegated the listening to then naturally the events for that child are lost. However, there is nothing to stop you delegating the event listening to the body tag as you'll never move a child outside of that. EG:

d3.select('body').delegate('mouseover','g',function(){...

这篇关于如果在DOM周围移动,SVG元素会丢失事件处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆