This, I think, is basically a selection question.
Here's my semi-working Plunker.
I'm trying to select a path, and change the opacity of all paths within a chart that are not selected, based on a mouseover of the respective graphic element (a circle) in the chart's legend.
I've set the id of the paths such they'll have the same id as the circles that are activated on mouseover. I've also gotten the circles that are not selected on hover to change opacity. (Currently, however, all of the non-selected circles, in all of the legends, across all of the charts, change opacity. I'm trying to limit mouseover opacity changes to only the relevant chart.)
What I'm trying to achieve:
When I mouseover a circle in the legend of a given chart, the same opacity changes should be applied to the paths of that chart, as if I had hovered over the paths themselves. If I understand my problem correctly, I'm having trouble defining the selection/non-selection of the circles and their respective paths, and limiting those selections to only one chart out of several on the page.
Here's how the paths' groups and ids are defined:
var pathGroup = main.append('g').classed('paths', true);
var paths = pathGroup.selectAll("path")
.data(dataset)
.enter()
.append("path")
.attr("id", function(d) {
return d.record
})
.attr("data-legend", function(d) {
return d.record
})
And here's the problematic code, I think:
li.selectAll("circle")
.attr("id",function (d) {return d.key})
.style("fill",function(d) { return d.value.color})
.on("mouseover", function(d) {
// need to define "circleGroup" and "circles" (as is done for "pathGroup" and "paths") so that the legend's non-selected circles are the ones that fade)
// also need to find a way of limiting "circles" to a circle group within only that state's chart
// circles
d3.selectAll('circle:not(this)')
.style('opacity', 0.4)
.style('transition', "opacity 0.1s")
d3.select(this)
.classed('hover', true)
.style('opacity', 0.9)
.style('transition', "opacity 0.1s")
d3.select('path:not(this)')
.style('opacity', 0.4)
.style('transition', "opacity 0.1s")
// d3.select('path data-legend', function(d) { return d.key})
d3.select('path id', function(d) { return d.key})
.classed('hover', true)
.style('opacity', 0.9)
.style('transition', "opacity 0.1s")
})
.on('mouseout', function(d) {
d3.selectAll('circle')
.style('opacity', 0.9)
.style('transition', "opacity 0.1s")
})
Here, again, is my semi-working Plunker: http://plnkr.co/edit/mvdqBPMymCt9VAKAPKD1?p=preview
In advance, thanks for any help you can offer in setting this right.
Issues with your code:
d3.selectAll('circle')
selects all the circles in the body and as far as the paths are concerned:
d3.select('path id') wouldn't work as the selector itself is messed up here. Try console logging the selection here.
Option 1:
Try replacing the legend mouse events with the following code:
.on("mouseover", function(d) {
// look for all the circles within the parent node
d3.select(this.parentNode).selectAll('circle').style('opacity', 0.4);
// change opacity of current circle
d3.select(this).style('opacity', 0.9);
// use parentNode to go until SVG and select all paths
d3.select(this.parentNode.parentNode.parentNode).select('g.paths').selectAll('path').style('opacity', 0.4);
// change opacity of path with data-legend = key
d3.select(this.parentNode.parentNode.parentNode).select('g.paths').selectAll('path[data-legend="'+d.key+'"]').style('opacity', 0.9);
})
.on('mouseout', function(d) {
// change all circles' and paths' opacity back to original values
d3.select(this.parentNode).selectAll('circle').style('opacity', 0.9);
d3.select(this.parentNode.parentNode.parentNode).select('g.paths').selectAll('path').style('opacity', 0.9);
});
I hope the comments are clear enough to understand the code. Just parsing through the parentNodes.
Option 2:
Add a class/id to the legend group representing the "state" i.e. Alabama, California etc.
And search for the SVG with selectedState on every mouseover and change the paths' opacity.
Hope this helps. :)
Related
I have a donut chart that I want to use, which is based on this.
I'm trying to create a function, when a user hovers over a certain path of the donut chart, the stroke color appears.
Nevertheless, I tried to edit a portion of the code but somehow the "mouseover" and "mouseout" handlers are ignored (not working)? I tried researching the Internet, but I couldn't find a solution.
Below is a portion of the code:
var path =
svg.select('.slices')
.datum(data)
.selectAll('path')
.data(pie)
.enter().append('path')
.attr('fill', function(d) {
return colour(d.data[category]);
})
.attr('d', arc)
.on('mouseover', function() {
console.log("mouseOver");
})
.on('mouseout', function(d) {
console.log("mouseOver");
});
I have sets of circles on a map like 10 red circles, 10 blue circles, 10 green circles. How can i select only red circles using d3 selectAll or select?
Or is there any other methods than that?
the color has been given like this(as the value of "fill" in "style" attribute,
feature = g.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("id", function (d) {
return d.ArtistID + d.FollowerID;
})
.style("stroke", "black")
.style("opacity", .6)
.style("fill", function (d) {
if (d.ArtistID == 1) {
return "red"
} else if (d.ArtistID == 2) {
return "blue"
} else {
return "green"
};
})
.attr("r", 10);
so, the circles will be drawn like this,
<circle id="10" r="10" transform="translate(695,236)" style="stroke: rgb(0, 0, 0); opacity: 0.6; fill: rgb(255, 255, 0);"></circle>
I want to select the circles of red color. Cam somebody help?
Thanks in Advance.
You're asking to select based on the value of the style attribute. The fill property is nested within the style attribute; it is not a direct attribute of the DOM node. So you won't be able to select it using an attribute selector (ie the method #LarsKotthoff linked to).
You can switch to setting the fill using the SVG fill attributes, .attr('fill', ...) instead of using style('fill'), which will allow you to later select it using an attribute selector.
That aside, selecting via the fill attribute doesn't seem too elegant. If your design evolves and you start using a different fill color, you'll have to change the attribute selector as well. Assigning it a class and selecting based on the class is more appropriate.
Perhaps the best is to select based on data. Eg:
g.selectAll('circle')
.filter(function(d) { return d.ArtistID == 1; })
.style('fill', function(d, i) {
// here you can affect just the red circles (bc their ArtistID is 1)
})
I'm testing a d3js treemap from a blog. Please see the live jsbin here.
I want to control the filling color of each rect for each small area. I don't know where can I control the color of rect. I found the following part is setting the color.
childEnterTransition.append("rect")
.classed("background", true)
.style("fill", function(d) {
return color(d.parent.name);
});
I try to remove the fill or change the color, the filling color is not working. For example I want to change all rect fill color to FFF, it is not working at all.
childEnterTransition.append("rect")
.classed("background", true)
.style("fill", function(d) {
return '#FFF';
});
You're setting the fill colour twice three times -- once in the "enter" chain for new elements, and then again in the "update" chain for all elements, and then a third time during the zoom transition. If you're only changing one of those pieces of code, the others may be replacing your setting.
Enter code (from your bl.ocks page):
childEnterTransition.append("rect")
.classed("background", true)
.style("fill", function(d) {
return color(d.parent.name); //change this
});
Update code: You can probably delete the entire update chain and just use the zoom function to update the values to the current zoom.
childUpdateTransition.select("rect")
.attr("width", function(d) {
return Math.max(0.01, d.dx);
})
.attr("height", function(d) {
return d.dy;
})
.style("fill", function(d) {
return color(d.parent.name); //change this
});
Zoom code:
zoomTransition.select("rect")
.attr("width", function(d) {
return Math.max(0.01, (kx * d.dx));
})
.attr("height", function(d) {
return d.children ? headerHeight : Math.max(0.01, (ky * d.dy));
})
.style("fill", function(d) {
return d.children ? headerColor : color(d.parent.name); //change this
});
Also, just to nitpick: Your "enter" selection (childEnterTransition) isn't actually a transition. If it was, there would be no point to setting the colour there and then re-setting it in update, because the update transition would just cancel the earlier transition. But because it isn't a transition, setting the colour there creates a starting value for the entering elements before you transition all the elements to the current value.
In Mike Bostocks example http://bost.ocks.org/mike/nations/ there is so much data that putting the names of the countries there would make it chaotic, but for a smaller project I would like to display it.
I found this in the source:
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(position)
.sort(order);
dot.append("title")
.text(function(d) { return d.name; });
But somehow a title never shows up. Does anybody have an idea, how to display the name, next to the bubble?
As the other answer suggests, you need to group your elements together. In addition, you need to append a text element -- the title element only displays as a tooltip in SVG. The code you're looking for would look something like this.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter()
.append("g")
.attr("class", "dot")
.call(position)
.sort(order);
dot.append("circle")
.style("fill", function(d) { return color(d); });
dot.append("text")
.attr("y", 10)
.text(function(d) { return d.name; });
In the call to position, you would need to set the transform attribute. You may have to adjust the coordinates of the text element.
Unfortunately grouping the text and circles together will not help in this case. The bubbles are moved by changing their position attributes (cx and cy), but elements do not have x and y positions to move. They can only be moved with a transform-translate. See: https://www.dashingd3js.com/svg-group-element-and-d3js
Your options here are:
1) rewrite the position function to calculate the position difference (change in x and change in y) between the elements current position and its new position and apply that to the . THIS WOULD BE VERY DIFFICULT.
or 2) Write a parallel set of instructions to setup and move the tags. Something like:
var tag = svg.append("g")
.attr("class", "tag")
.selectAll(".tag")
.data(interpolateData(2004))
.enter().append("text")
.attr("class", "tag")
.attr("text-anchor", "left")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(tagposition)
.sort(order);
You will need a separate tagposition function since text needs 'x' and 'y' instead of 'cx', 'cy', and 'r' attributes. Don't forget to update the "displayYear" function to change the tag positions as well. You will probably want to offset the text from the bubbles, but making sure the text does not overlap is a much more complicated problem: http://bl.ocks.org/thudfactor/6688739
PS- I called them tags since 'label' already means something in that example.
you have to wrap the circle element and text together , it should look like
<country>
<circle ></circle>
<text></text>
</country>
Here is an example of the pack layout in d3js:
http://bl.ocks.org/4063530
Is it possible to control the colors of the individual cirles?
Here is another example of the pack layout with colors:
http://bl.ocks.org/4063269
Can you help me understand how the colors are assigned to the bubbles in the second chart?
You can just add the attribute fill to change the color:
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d){ return d.color; });
In the example above, suppose your data contains a color field.