I'm looking for some advice on how to get two elements in a visualization, which are linked by a common data value, to respond simultaneously.
Here is the visualization as it stands now.
http://bl.ocks.org/natemiller/2686e5c0d9a1a4bb0895
Note that the different colored points are for the 50 US states in 2005 (green) and 2013 (blue), so there is a blue point and a green point for each state. I have two things I would like to get working here.
I would like to be able to mouseover either a blue point or a green point and have the corresponding point (for the same state) highlighted.
I would like a tooltip with some basic data to appear next to both points, providing point specific data.
Regarding the first point above. Right now when you mouseover a blue point the corresponding green point is highlighted, however, when you mouseover a green point only that point is highlighted and not its corresponding blue point. I imagine this is a simple fix, but for the life of me I can't figure out to reverse the reference so I get green to blue references as well.
Regarding the second point. Right now a tooltip with relevant information appears near the moused-over point, but I would like to have a similar tooltip appear next to the corresponding point from the alternate year of data, so that direct comparisons across years are easier. I am quite new to adding HTML tooltips so I'm not clear on how to do this and suspect it may require a new method for adding tooltips. Can any help to steer me in the correct direction for how to have a tooltip appear near the moused-over element and a corresponding linked element?
1) Remember that ids are unique and you're creating multiple circles with the same id, use a class instead
circles.attr("class", function(d) { return d.state })
2) You're creating a single tooltip, if you want to show one for each pair of states create multiple tooltips
Assuming that you make these changes you can easily create multiple tooltips for each pair of states
circles.on('mouseover', function (d) {
// selection for 2 states
var states = d3.selectAll('circle.' + d.state)
// code to style those nodes goes here ...
// tooltips for the states
var tooltips = d3.select('svg').selectAll('text.tooltip')
.data(states.data())
// initial styling of the tooltips goes here...
tooltips
.enter()
.append('text')
.attr('class', 'tooltip')
// update
tooltips
.html(function (d) {
// text of the tooltip
return 'something'
})
// positioning, it requires some margin fixes I guess
.attr('x', function (d) { return xScale(d.child_pov) })
.attr('y', function (d) { return yScale(d.non_math_prof) })
})
Finally remove the tooltips created on mouseover when the mouseout event is triggered
circles.on('mouseout', function (d) {
d3.select('svg').selectAll('text.tooltip').remove()
})
You cannot have multiple elements with the same id. Use a class .circleHawaii instead of an id #circleHawaii.
Related
I currently have a chart with 2 viewing options (regular data and percentages).
I also have a tooltip that shows the results for each line that I'm showing
When I switch options I want my tooltip to keep showing the info (the lines are getting drawn correctly) but instead what I'm seeing is that I'm creating several tooltip boxes for each time I switch options.
My tooltip is an append('g') to my main chart and I'm appending my tooltip box to this tooltip which is appended to my chart which is chartContent
Here's the tooltip box
const tooltipContainer = d3.select(".stackedChart").append("div")
.attr("class", css.divBox)
.style("opacity", 0);
tooltip.append("tooltipContainer")
On update (aka when switching options) I'm already doing this
chartContent.remove()
chartContent = chart.append('g') (to get the new info)
Now for my .divBox I was trying d3.selectAll(".divBox").remove(); but I can't seem to get it to work.
Every time I switch options I get one more .divBoxand I can see the bottom one updating with the info but the one following the tooltip is the top one (I think, I might be wrong).
What are my steps from here? I'm extremely confused
I am trying to make just the axis label of the selected item bold, so that it is more apparent to users what they have selected. I am using a composite bar chart to compare two values and have turned the labels -90 so that they are inside the bars. I have been able to make the labels clickable, with help from Gordon here: dc.js barChart - click on x-axis ticks and labels
I am counting clicks so that users can select and deselect from clicking on the label, however with some of the smaller values, users cannot tell which items they have filtered on in any specific chart.
I am able to select, de-select on clicks, I have tried select('tick-text').attr('style', 'font-weight: bold;');
This bolds the first item in the x-axis, no matter which selection is made.
When I use selectAll('.x text) it changes all.
Relevant portion of the code:
ByTopLvl.on('pretransition',function() {
ByTopLvl.selectAll('g.x text')
.style('text-anchor', 'start')
.attr('transform', 'rotate(-90),translate(10, -10)');
ByTopLvl.select('.axis.x')
.selectAll('.tick text')
.on('click.custom', function (d) {
var clicks = $(this).data('clicks');
if (!clicks) {
ByTopLvl.replaceFilter(d)
.select('.tick text')
.attr('style', 'font-weight: bold;');
ByTopLvl.redrawGroup();
} else {
ByTopLvl.select('.tick text')
.attr('style', 'font-weight: normal;');
ByTopLvl.filterAll();
dc.redrawAll();
}
$(this).data("clicks", !clicks);
});
I would expect that when I click on the label, only the label clicked would be bold.
First off, whenever possible, I would suggest using the built-in selection/filter state rather than trying to keep track of clicks yourself. Otherwise they are bound to get out of sync at some point.
If you have the boldness of the ticks driven by the active filters, then you'll get the same behavior whether the bar or the tick was clicked, and you can be certain that exactly the ticks in the filters are the bolded ones:
CSS
.dc-chart g.axis.x text.selected {
font-weight: bold;
}
JS
chart.on('filtered', function(chart) {
var filters = chart.filters();
chart.selectAll('.axis.x .tick text').classed('selected', function(d) {
return filters.includes(d);
})
})
[Side note since I'm not answering your exact question: if you want to make your code work, you could do something like filter the selection based on d:
ByTopLvl.select('.tick text').filter(function(d2) { return d2 === d; })
or in your case, this is the clicked tick, so d3.select(this) should also work. But I think you'll run into a lot of bugs that way.]
Similarly, you can simplify your click behavior by tying into the built-in filter behavior, which already toggles:
chart.on('pretransition', function(chart) {
chart.select('.axis.x')
.selectAll('.tick text')
.on('click.select', function(d) {
chart.filter(d);
chart.redrawGroup();
});
});
Yeah, it's weird that the built-in filter function toggles, but that's just the way that dc.js evolved.
Here's a demo fiddle.
In a composite
Composite charts in dc.js are a little bit of a confusing mess.
The filter selection is shared between the parent and child, except they sort also handle it separately.
Unfortunately when I have to troubleshoot composite charts, I just try different things until it works, and never fully understand what's going on. I think it's just too complicated for my brain. :-O
Anyway, this worked...
Keep a reference to the inner bar chart:
var chart = dc.compositeChart('#test'), bar;
chart
.width(768)
.height(380)
.x(d3.scaleBand())
.xUnits(dc.units.ordinal)
.brushOn(false)
.xAxisLabel('Fruit')
.yAxisLabel('Quantity Sold')
.dimension(fruitDimension)
.group(sumGroup)
._rangeBandPadding(0.05)
.compose([
bar = dc.barChart(chart)
.barPadding(0.1)
.outerPadding(0.05)
])
When responding to the click, filter the child bar chart instead of the parent:
chart.on('pretransition', function(chart) {
chart.select('.axis.x')
.selectAll('.tick text')
.on('click.select', function(d) {
bar.filter(d);
chart.redrawGroup();
});
});
Listen to the filtered event of the child bar chart and apply axis bolding to the parent composite chart:
bar.on('filtered', function(chart) {
var filters = chart.filters();
chart.selectAll('.axis.x .tick text').classed('selected', function(d) {
return filters.includes(d);
})
})
Whoo. I guess it's sorta.. consistent? The child bar chart is the source of truth for the filters. Maybe I'm rationalizing.
New fiddle version.
Basically I have a graph where the user can click different SVG shapes and by doing so a .on('click') function will change the color fills. However, at some point it's going to be important to give the user the option to revert the graph to its initial state. I think the intuitive way to go about this for my case is by clicking anywhere outside the graph, i.e somewhere on the document body that is not in the graph coordinate plane (margins, padding, ect).
I tried this:
d3.select('body').on('click', function() {
d3.selectAll('circle').style('fill', function(d) {
return d.color;
})
});
It did not have any effect. I'm guessing that my existing on click effects of the shapes are overriding my d3.select('body').on('click') that I tried above. Either that or my approach is just flat out wrong.
Any suggestions here would be great.
Also I am aware that .attr('fill') and .style('fill') should almost always be consistent throughout, I do need .style('fill') here. I tried .attr('fill') just to be safe as well.
You might be able to use something like
window.onclick = function(event){
if(!(event.target.className.baseVal=="circleClass")){
d3.selectAll('circle').style('fill', function(d) {
return d.color;
})
}
}
I am working with a zoomable sunburst in D3 that also has some breadcrumbs. First time working with D3 so I don't know all of the intricacies yet, but I am having trouble getting the colors of the arcs in the sunburst and the breadcrumbs in the sequence to match up. It only ever happens on the leaf nodes too which is weird. I can click on the inner circle and the breadcrumb shows up with the same color, and so on until I click on a leaf node.
Originally I set the color like this var colors = d3.scale.category10(); and the in the chart options like color: colors, When trying to set the colors for the polygon for the bread crumb I thought it'd be as simple as this which I had seen from a few examples,
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", function(d) {
return colors(d.name);
});
But this results in the explanation above. So to clarify in the picture below, either the outer arc on the sunburst should be pink, or the lowest bread crumb should be red. (I'm not sure which is correct, probably the former):
I have a working plunker I am almost done with, but can't get this part. Also part two kind of, but is it possible to set the color of individual arcs based on a certain value?
EDIT Okay well after looking at some more examples, it appears the red on red is okay in the picture for example. So I guess the solution I am looking for is to correct the behavior of the breadcrumbs.
I'm working on fading out certain lines from a chord diagram when a separate chart is hovered over. I'm looking at the code for the "fade" function,
`function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}`
And I was just wondering if someone could explain to me what d.source.index means and d.target.index mean. I generally understand that it's the source of the chord and the target of the chord, but I wanted to gain some more specific insight into the values/meaning of "index" so that I could better manipulate the selection.
My ultimate goal is to over over a box in a separate legend rectangle, and have the chord diagram fade so that only the color hovered over in the legend box retains full opacity.
Every chord has data associated with it. This data has, among other things, source and target attributes, which point to the nodes that the chord connects. In the code above, the index attribute of the source and target nodes is referenced to identify which ones to filter. This can be anything you want though.
In your application, depending on what the legend is for, you would need to check the value displayed in the legend for the source/target nodes or the chord itself.