I'm having an issue with mouseover and mouseout events in d3.
I have built an area graph and at each "data point" I have appended a circle. These circles are hidden (by setting opacity to 0) on load. Then, when you mouse over an area, it shows the circles relating to that layer.
I now need to make it so that when you hover over a circle, it grows a bit larger. However, when mousing over the circle, it triggers the mouseout event for the area (hiding the circles).
Is there any way that the events can be set so that the mouseout event doesn't fire until the mouse enters another layer or leaves the svg entirely?
Here is the current transition code that I have:
var svg = d3.select('svg');
svg.selectAll('.data-circles')
.attr('opacity', 0);
svg.selectAll('.layer')
.attr('opacity', 1)
.on('mouseover', function (d, i) {
svg.selectAll('.data-circles')
.transition()
.duration(250)
.attr('opacity', function (d, j) {
return j == i ? 1 : 0;
});
}).on('mouseout', function (d, i) {
console.log(d, i);
svg.selectAll('.data-circles')
.transition()
.duration(250)
.attr('opacity', 0);
});
var dataCircle = svg.selectAll('.data-circle');
dataCircle.on('mouseover', function (d, i) {
d3.select(this)
.transition()
.duration(500)
.attr('r', 8)
.attr('stroke-width', 4);
}).on('mouseout', function () {
d3.select(this)
.transition()
.duration(500)
.attr('r', 4)
.attr('stroke-width', 2);
});
And here is a link to the code on Jsfiddle
Thanks,
You can simply remove the mouseout handler (and rename mouseover to mouseenter for efficiency):
.on('mouseenter', function (d, i) {
svg.selectAll('.data-circles')
.transition()
.duration(250)
.attr('opacity', function (d, j) {
return j == i ? 1 : 0;
});
});
This sets the correct opacity for the correct circles and doesn't interfere with the highlighting of individual circles. The difference to your previous interaction model is that the circles remain there even if the cursor leaves the plot area -- you could fix that by attaching a mouseout handler to the plot area/SVG.
Complete demo here.
Related
I have build a scatter plot in d3 v4 using following link: scatter plot
I have also used tipsy plugin for tooltip.
Now i wanted to add guidelines in this chart that is show guideline when user hovers over a circle and hide guidelines when out of focus. For this i stumbled upon a code which i tried to use but it is not showing anything.
Following is the code which i used:
var circles = svg.selectAll("circle").data(dataset).enter().append("circle");
circles.attr("cx",function(d){
return xScale(d[indicator1]);//i*(width/dataset.length);
})
.attr("cy",function(d){
return yScale(d[indicator2]);
})//for bottom to top
.attr("r", 10);
circles.attr("fill",function(d){
return "#469DDA";
});
circles.attr("stroke",function(d){
return "white";
});
circles.attr("class", "circles");
svg.style('pointer-events', 'all')
// what to do when we mouse over a bubble
var mouseOn = function() {
var circle = d3.select(this);
// transition to increase size/opacity of bubble
circle.transition()
.duration(800).style("opacity", 1)
.attr("r", 16).ease("elastic");
// append lines to bubbles that will be used to show the precise data points.
// translate their location based on margins
svg.append("g")
.attr("class", "guide")
.append("line")
.attr("x1", circle.attr("cx"))
.attr("x2", circle.attr("cx"))
.attr("y1", +circle.attr("cy") + 26)
.attr("y2", h - margin.t - margin.b)
.attr("transform", "translate(40,20)")
.style("stroke", "#ccc")
.transition().delay(200).duration(400).styleTween("opacity",
function() { return d3.interpolate(0, .5); })
svg.append("g")
.attr("class", "guide")
.append("line")
.attr("x1", +circle.attr("cx") - 16)
.attr("x2", 0)
.attr("y1", circle.attr("cy"))
.attr("y2", circle.attr("cy"))
.attr("transform", "translate(40,30)")
.style("stroke", "#ccc")
.transition().delay(200).duration(400).styleTween("opacity",
function() { return d3.interpolate(0, .5); });
// function to move mouseover item to front of SVG stage, in case
// another bubble overlaps it
/* d3.selection.prototype.moveToFront = function() {
return this.each(function() {
this.parentNode.appendChild(this);
});
};
// skip this functionality for IE9, which doesn't like it
if (!$.browser.msie) {
circle.moveToFront();
}*/
};
// what happens when we leave a bubble?
var mouseOff = function() {
var circle = d3.select(this);
// go back to original size and opacity
circle.transition()
.duration(800).style("opacity", 1)
.attr("r", 10).ease("elastic");
// fade out guide lines, then remove them
d3.selectAll(".guide").transition().duration(100).styleTween("opacity",
function() { return d3.interpolate(.5, 0); })
.remove()
};
// run the mouseon/out functions
circles.on("mouseover", mouseOn);
circles.on("mouseout", mouseOff);
$('.circles').tipsy({
gravity: 'w',
html: true,
title: function() {
var d = this.__data__;
return "State: "+d.States+"<br>"+indicator1+" "+d[indicator1]+"<br>"+indicator2+" "+d[indicator2];
}
});
I am getting following result now:
When i checked the browser console i am getting following error:
If you are using d3.v4 , I think problem lays with transition's ease method
You should provide easing constant, instead of plain string
So, instead of using
circle.transition()
.duration(800).style("opacity", 1)
.attr("r", 16).ease("elastic");
You should write
circle.transition()
.duration(800).style("opacity", 1)
.attr("r", 16).ease(d3.easeElastic) // change
I'd like to be able to both zoom and mouseover an element. Included is an example followed by more details about the scenario.
https://jsfiddle.net/pkerpedjiev/ny5ob3h2/4/
var svg = d3.select('svg')
var zoom = d3.behavior.zoom()
.on('zoom', draw)
svg.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', 400)
.attr('height', 400)
.attr('fill', 'transparent')
.call(zoom)
var xScale = d3.scale.linear()
.domain([0, 10])
.range([0,10])
zoom.x(xScale)
svg.append('text')
.attr('x', 50)
.attr('y', 100)
.text('Hi there')
.attr('visibility', 'hidden')
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 10)
//.attr('pointer-events', 'none')
.on('mouseover', function(d) {
svg.selectAll('text')
.attr('visibility', 'visible');
})
.on('mouseout', function(d) {
svg.selectAll('text')
.attr('visibility', 'hidden')
});
function draw() {
d3.selectAll('circle')
.attr('r', xScale(10));
}
The example just contains a circle and some text. The text is invisible unless the mouse is over the circle. If I scroll using the mouse wheel, the circle changes in size in response to the zoom behavior. If, however, the mouse is over the circle, zooming doesn't work.
Is there a way to fix this? Setting pointer-events to none on the circle fixes the zooming, but then the mouseover event doesn't get called.
Is there way to have both the circle's mouseover get called and be able to zoom while the mouse is over the circle?
Yes this is possible by giving the zoom on the circle also.
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 10)
.call(zoom)//giving on circle also
//.attr('pointer-events', 'none')
.on('mouseover', function(d) {
svg.selectAll('text')
.attr('visibility', 'visible');
})
.on('mouseout', function(d) {
svg.selectAll('text')
.attr('visibility', 'hidden')
});
working example here
Hope this helps!
EDIT
If you have lot of elements and you don't like to attach the zoom listener to all the elements then you can attach the zoom to the main group which holds everything.
Like this:
var svg = d3.select('svg').attr("x",500).attr("y",500).append("g")
Attach zoom listener to the group.
svg.call(zoom);
Working code here
var data1 = [150,350,550]
var data2 = [100,300,500]
var sampleSVG = d3.select("body")
.append("svg")
.attr("width", 800)
.attr("height", 800);
var circles1 = sampleSVG
.append("g")
.attr("class", "circles1")
.selectAll(".circle1")
.data(data1)
.enter()
.append("circle")
.attr("class", "circle1")
.on("mousedown", animateFirstStep);
var circleAttributes1 = circles1
.attr("cx", function (d) { return d;})
.attr("cy", 200)
//.attr("class", function (d) { return "circle" + d;})
.attr("r", function(d) { return d/10;})
.style("fill", function(d){
var color;
if (d === 150){ color = "yellow";
} else if (d === 350) { color = "orange";
} else if (d === 550) { color = "red";
} return color;
})
function animateFirstStep(){
d3.selectAll(...??...)
.data(data1,function(d, i) { return d; })
.transition()
.delay(0)
.duration(2000)
.attr("r", 400)
.style("opacity", 0)
.each("end", animateSecondStep);
};
I have 3 circles and i want to click on one of them. When I click on one I want that one to grow bigger and disappear. the other 2 circles should also disappear but should NOT grow any bigger. Right now I name the class of each circle simply "circle1". But is also made a option(which are commented out) that gives each circle its own class based on the data. I have a function which animate the circles. But I don't know how to select a specific circle with a mouseclick and let that one grow bigger and disappear while the others don't grow but simply disappear. Can anyone help me out please??
You're on the right track, but instead of selecting elements by their class in the transition, I'd just bind the onclick event to each circle using the .on("click", ...) operator. You will then have access to each individual circle using the d3.select(this). Here's an example of what you can do with the circles1.on("click", ...) function (here I'm choosing how to animate the circles by their index i in the original data, but you can also filter by the value of d):
.on("click", function(d, i){
if (i == 0){
d3.select(this).transition()
.delay(0)
.duration(2000)
.attr("r", d)
.style("opacity", 0);
}
else{
d3.select(this)
.transition()
.delay(0)
.duration(2000)
.style("opacity", 0);
}
});
Complete working JSfiddle here.
Late to the party, but I think this is what you want: Fiddle
To "remember" the selected circle and the unselected circles, you need something like the following:
var grow;
var disappear;
Then modifying #mdml's answer a bit:
.on("click", function (d, i) {
// This is an assumption, I thought you wanted to remember
// so that you can toggle those states.
if (grow && disappear) {
disappear.transition()
.delay(0)
.duration(2000)
.style("opacity", 1);
grow.transition()
.delay(0)
.duration(2000)
.style("opacity", 1)
.attr("r", d / 10);
grow = null;
disappear = null;
} else {
var g = d3.selectAll("circle");
disappear = g.filter(function (v, j, a) {
return i !== j;
});
grow = g.filter(function (v, j, a) {
return i === j;
});
disappear.transition()
.delay(0)
.duration(2000)
.style("opacity", 0);
grow.transition()
.delay(0)
.duration(2000)
.attr("r", d)
.style("opacity", 0);
}
});
As you explained in the comments in the other answer, you wanted to select a circle and have that circle grow AND disappear. The other two circles will fade away. You also wanted to remember which was selected and which were not.
The Fiddle demo enables you to click on a circle, it will grow AND disappear, the others will fade. Click on it again and it will return to normal size, while the others will reappear.
I'm using d3 to create a map and add some data on it in. So far, I managed to draw circles based on the data that I pull from database. What I want to do is, when I mouseover one of those circle, create a new bigger circle with some text on it. I was able to draw the bigger circle but couldn't figure out how to add the label or text on it.
This is how I add circles to the map.
for (var i = 0; i < data.length; i++) {
var coordinates = projection([data[i]["Longitude"], data[i]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 5)
.attr('fill', 'black')
.attr('class', 'pClass')
.attr('id', data[i]["Id"])
.attr('dataId', data[i]["DataId"])
.on('mouseover', dataMouseover);
}
Here is the mouseover event
function dataMouseover() {
var id = $(this).attr('id');
var d= $.grep(data, function (e) { return e.Id == id; });
var coordinates = projection([d[0]["Longitude"], d[0]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 120)
.attr('fill', 'darkblue')
.attr('class', 'pClass')
.attr('id', data[0]["Id"] + "popUp")
.attr('dataId', plaques[0]["DataId"])
.attr("stroke", "white")
.attr("stroke-width", "5")
.on('mouseout', function () {
d3.select(this).remove();
});
}
So, I'm also removing the bigger circle when mouse is out. What I want is to put a text in that circle from the data while drawing it in there.
UPDATE: I updated my code to change current circle's radius instead of drawing new one.
for (var i = 0; i < data.length; i++) {
var coordinates = projection([data[i]["Longitude"], data[i]["Latitude"]]);
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 5)
.attr('fill', 'black')
.attr('class', 'pClass')
.attr('id', data[i]["Id"])
.attr('dataId', data[i]["DataId"])
.on('click', function () {
$("#dialog").dialog('open');
})
.on('mouseover', function (data) {
var sel = d3.select(this);
sel.moveToFront();
d3.select(this)
.transition()
.duration(200)
.attr('fill', 'darkblue')
.attr('r', 120)
.attr('stroke', 'white')
.attr('stroke-width', '5')
})
.on('mouseout', function () {
d3.select(this)
.transition()
.duration(200)
.attr('fill', 'black')
.attr('r', 5)
.attr('stroke', 'none')
.attr('stroke-width', '0')
});
}
Still could use some guidance on how to use g element to cover both circle and text in my case.
You can't actually add text inside an svg circle. I discovered this to my own chagrin as well a few weeks ago. :\
Instead, encapsulate both the circle and the text inside a g element. Here's a link to a SO post explaining exactly how to do that: d3 add text to circle
Steps to going about doing this:
On hover: Instead of appending a circle, first append a g element.
Then append a circle element to that g element
Then append a text element to that g element
??? (stylize the elements as you will...)
Profit from having it look like the text is inside the circle!
Also: instead of redrawing the entire circle on mouseover, see if you can just change the r attr of the circle on mouseover. Might save you some code & make your app update a bit faster.
Hi I am using this code for making a bar/stacked_bar graph
bars.append('rect')
.attr('height', axes.ver.scale.rangeBand())
.attr('width', 0)
.attr('x', function (d, i) { var value = axes.hor.scale(csum[i]); csum[i] += d.value; return value; })
.attr('y', function (d) {return axes.ver.scale(d.name); })
.classed('cr_' + uv.util.formatClassName(self.categories[idx]), true)
.style('stroke', 'none')
.style('fill', color)
.on('mouseover', uv.effects.bar.mouseover(self, idx))
.on('mouseout', uv.effects.bar.mouseout(self, idx))
.transition()
.duration(uv.config.effects.duration)
.delay(idx * uv.config.effects.duration)
.attr('width', function (d,i) { return axes.hor.scale(csum[i]) - axes.hor.scale(csum[i]-d.value); });
I have an mouseover event attached which changes the color on hover.
My problem is that when my chart is rendering and at the same time I hover over a currently rendering bar then it does not render properly.
I have linked the screenshot and the error is that a gap comes.
http://db.tt/ng1akZDy
Please Tell me what am I doing wrong.
Don't know if the problem is exactly the same.
Try to add this to your CSS or in line styles:
pointer-events: all;
https://stackoverflow.com/a/9210392/731179