Here is an svg with a circle drawn on it:
svg = d3.select("body").append("svg")
.on("mouseover", function() { console.log("callback");} );
svg.append("circle")
.attr("cx", 50)
.attr("cy",50)
.attr("r",20)
.attr("fill","red");
Why does mouseover fire when I mouse over the circle? I assume because its a child element of its parent svg?
But I'd like to inhibit this action. How can I do this?
The arguments on the mouseover function don't seem to pass in the event, so I came up with this solution instead.
svg = d3.select("body").append("svg")
.on("mouseover", function() {
var event = window.event;
if (event.target.nodeName === "svg") {
console.log("callback");
}
});
svg.append("circle")
.attr("cx", 50)
.attr("cy",50)
.attr("r",20)
.attr("fill","red");
If you want to inhibit pointer interactions set pointer-events="none" on that element.
.attr("pointer-events","none")
Container elements don't trigger mouse events in SVG. The event will only get triggered when you mouse over any of the child graphical elements it contains.
Related
Here is my problem, I want to use transition() method of d3.js but for a set of rectangles that I don't know the size.
For example: At first I have 2 rectangles then 3 then 4 and then 2.
Can I use transition() in this case? If so, what's the best way to do it?
Thanks in advance
I use invisible rectangles for providing good hover effect in charts. So while creating the rectangles I simply assign them a class. While updating I remove elements of that class first and just repeat the process.
tmpsvg = svg.transition();
g = tmpsvg.select('g');//prefer to refer by classname
g.selectAll(".bar-rect").remove();
var rect = g.selectAll(".bar-rect")
.data(data)
.enter().append("svg:rect")
.attr("class", "bar-rect")
.attr("x", function(d, i) { return x(d.key)-10; })
.attr("y", 0)
.attr("width", "20px")
.attr("height", h)
.on("mouseenter", function(d, i) {
//TOOLTIP EFFECTS ON MOUSE-ENTER
$('#myls'+i).animate( {opacity:1 },100);
$('.chart-tooltip[data-index='+i+']').addClass('hover');
// Add hover class to the targeted point
}).on("mouseleave", function(d, i) {
//REMOVE TOOLTIP EFFECT ON MOUSE-LEAVE
$('#myls'+i).animate( {opacity:0 },100);
$('.chart-tooltip').removeClass('hover');
// Remove hover class from the targeted point
});
This may not be the most efficient way but hope it helps.
Typing d3.behavior.drag() has me confused. I'd like to specify drag types so that different objects trigger different drag events. I'm used to programming with D3 dispatches/namespaces doing the following:
registering them with the D3 dispatch object
then using something like .on('click.typeHere') to listen for the dispatch event
and using dispatch.typeHere(<<data>>) to trigger the event.
I can't figure out how the normal dispatch procedure applies to drag behavior. For example:
var svg = d3.select('svg').append('g')
.attr('transform','translate(0,20)');
svg.append('rect')
.attr('height', 50)
.attr('width', 20)
.style('fill', 'steelblue');
var drag = d3.behavior.drag();
drag.on('drag', function() {
console.log('drag');
}).on('drag.type', function() {
console.log('namespace active');
}).on('drag.type2', function() {
console.log('namespace2 active');
});
svg.append('circle')
.attr('cx',20)
.attr('cy',0)
.attr('r',10)
.style('fill', 'orange')
.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<svg></svg>
I want the circle to trigger just drag.type when it is dragged and not the other drags that are registered in the drag behavior namespace. But .call(drag.type) isn't supported. What is the right way to go about this?
I would use different drag behaviours for the different types of objects with the respective drag handlers doing the appropriate thing. This will save you having to mess around with event namespaces:
var drag1 = d3.behavior.drag()
.on('drag', function() {
console.log('drag rect');
});
svg.append('rect').call(drag1);
var drag = d3.behavior.drag()
.on('drag', function() {
console.log('drag circle');
});
svg.append('circle').call(drag);
Complete demo here.
I can access d3.event.x from inside on("drag"...) but x doesn't exist inside on("dragstart"...); why? And how can I get it another way, preferably not very hackish?
Check console output for this example:
d3.select("body").append("svg").append("rect")
.attr("width", 100)
.attr("height", 100)
.style("color", "black")
.call(
d3.behavior.drag()
.on("dragstart", function(){
console.log(d3.event);
})
.on("drag", function(){
console.log(d3.event);
})
.on("dragend", function(){
console.log(d3.event);
})
);
JSFiddle
#Lars explains why, so how do you do it? d3.mouse is pretty slick:
d3.behavior.drag()
.on("dragstart", function(){
console.log('relative to rect: ' + d3.mouse(this));
console.log('relative to body: ' + d3.mouse(d3.select("body").node()));
})
Updated example.
This is the intended behaviour (see the documentation):
Drag events (but not dragstart and dragend events) expose "x" and "y" properties representing the current position of the drag gesture in local coordinates.
The start and end events are fired before and after the actual drag takes place, i.e. there's no coordinate change compared to the first and last drag events.
I am learning d3 and I came across a previous topic discussing stopPropagation(). jasondavies posted a reply to the question and an example here https://gist.github.com/jasondavies/3186840.
In this example he uses:
.on("mousedown", function() { d3.event.stopPropagation(); })
.on("mousedown.log", log("mousedown circle"))
I don't understand the event "mousedown.log" and how it triggered in this example.
Here is the full code from jasondavies's example:
<!DOCTYPE html>
<style>
circle { fill: lightgreen; stroke: #000; }
</style>
<body>
<script src="http://d3js.org/d3.v2.min.js"></script>
<script>
var svg = d3.select("body").append("svg")
.style("float", "left")
.attr("width", 480)
.attr("height", 480)
.on("mousedown", log("mousedown svg"))
.on("mouseup", log("mouseup svg"))
.on("click", log("click svg"));
svg.append("circle")
.attr("cx", 240)
.attr("cy", 240)
.attr("r", 200)
.on("mousedown", function() { d3.event.stopPropagation(); })
.on("mousedown.log", log("mousedown circle"))
.on("mouseup", log("mouseup circle"))
.on("click", log("click circle"))
var div = d3.select("body").append("div")
.style("float", "left")
function log(message) {
return function() {
div.append("p")
.text(message)
.style("background", "#ff0")
.transition()
.duration(2500)
.style("opacity", 1e-6)
.remove();
};
}
</script>
It's namespacing for events. See the documentation:
If an event listener was already registered for the same type on the selected element, the existing listener is removed before the new listener is added. To register multiple listeners for the same event type, the type may be followed by an optional namespace, such as "click.foo" and "click.bar". The first part of the type ("click" for example) is used to register the event listener (using element.addEventListener()) and methods are added on the selected elements as __onclick.foo and __onclick.bar. To remove a listener, pass null as the listener. To remove all listeners for a particular event type, pass null as the listener, and .type as the type, e.g. selection.on(".foo", null).
In this particular example, it means that both handlers are called when a mousedown event occurs. Without the namespacing, the second handler would overwrite the first.
Ok so I have the following code example where I have circles in an svg element. Each circle has a click event and I'm trying to animate the circle that was clicked. Currently all circles animate because I'm referring to the bubble object. What I want is to refer to the clicked object its self and not the other ones:
var data_items=[100,200,300];
var svg = d3.select("#chart")
.append("svg").attr("width", 800).attr("height", 600);
var g = svg.selectAll(".bubbleContainer")
.data(data_items)
.enter().append("g")
.attr("class","bubbleContainer");
var bubble = g.append("circle")
.attr("class","bubble")
.attr("cx", function(d) {
return d;
})
.attr("cy", function(d) {
return d
})
.attr("r", function(d) {
return d/2
})
.on("click",function(d){
bubble
.transition()
.duration(1000)
.attr("r",1000)
})
Any help is much appreciated
Thanks!
What Lars Kotthoff wrote would work. Alternatively – and I'm not sure which is more idiomatic:
Inside the click handler, the this context refers to the clicked DOM element.
So the following would do it too:
.on("click",function(d){
d3.select(this)
.transition()
.duration(1000)
.attr("r",1000)
});
You can use d3.event.target to access the element that is being clicked in the event handler. See for example this jsfiddle.