D3 - Select element in this svg? - d3.js

I have a codepen here - https://codepen.io/anon/pen/yvgJKB
I have two d3 charts with different data.
I want to have tooltips on the bars so when you rollover them it shows the value.
I want the tooltip to be separate html in the svg cos the actual charts are responsive and I dont want the tooltips to scale with the svg.
The tooltip works as expected in the top chart.
In the bottom chart the tooltip appears in the top chart.
I think this is becasue the let tooltip = d3.select('.tooltip').append("div") is selecting the first tooltip which is in the first chart.
The charts and tooltip is dynamically created.
This there a way in the D3 to say d3.select('.tooltip') in this svg something like d3.select(this)('.tooltip')
let tooltip = d3.select('.tooltip').append("div")
.attr("class", "tip")
.style("display", "none")
.attr("fill", "grey");

There is a lot of unnecessary repetition in your code.
Create the tooltip div only once. Also, since you are creating the div in your D3 code, you don't need any div with the tooltip class in your HTML. Remove both.
Besides that, use d3.event.pageX and d3.event.pageY to position your tooltip div anywhere in the page. A single tooltip div can be used to show the tooltip to any number of SVGs you have on that page.
Here is your updated CodePen: https://codepen.io/anon/pen/KQaBvE?editors=0010

Tooltips should be created separately by selecting different charts (or some other unique marker) and given unique variable names within the same scope:
let tooltip1 = d3.select('.chart1').append("div")
.attr("class", "tip")
.style("display", "none")
.attr("fill", "grey");
let tooltip2 = d3.select('.chart2').append("div")
.attr("class", "tip")
.style("display", "none")
.attr("fill", "grey");
https://codepen.io/mortonanalytics/pen/BYpwaz

Related

Display text w/ brushed values in d3.js

I have some svg circles along a single axis and d3.brushX activated. In this small Observable example, I want displayText to respond to the brush, displaying the number of circles selected by the brush.
The values currently respond to the brush, but it just writes the text on top of the existing value. How do I clear it every time the brush moves?
Give the text a class, select all DOM elements of the class and remove them right before you append the text within your brushed function.
svg.selectAll(".label").remove()
svg.append("text")
.attr("class","label")
.attr("x", width/2)
.attr("y", 50)
.text(displayText)
OR create the text element outside your brushed function
svg.append("text")
.attr("class","label")
.attr("x", width/2)
.attr("y", 50)
...then, just change the text within the brushed function
svg.select(".label").text(displayText)
The second approach is probably better...

dc.js Can a Pie Legend be recentered after filtering

I would like to be able to recenter a Pie charts legend after it has been filtered. Slices/Legends will removed when filtering because we remove empty bins. I added a pretransition listener to chart2, but that seems to be to late because the legend y value is the previous value and not current.
.on('pretransition', chart => buildLegend (chart))
If Male is selected on the Gender Pie chart I want the 4 legend items on the Job Pie chart to be re-centered. Any suggestions?
You can see a jsFiddle example.
A little more digging around showed me how to reference and update SVG elements.
function recenterLegend(chart) {
chart.selectAll('g.dc-legend')
.attr('transform', function(d) {
let legendY = (300 - (chart.group().all().length * 16)) / 2;
let translate = 'translate(220,' + legendY + ')';
return translate ;
});
}
Here is the updated jsfiddle.

Issue with useInteractiveGuideline() in nvd3.js

I have a chart object which uses interactive guideline:
var chart = nv.models.lineChart();
chart.useInteractiveGuideline(true);
[...]
If I later (say during an event) clear the chart via something like this:
d3.select("#{{id}} > *").remove();
Then the problem for me is that whilst the chart area is made blank (as desired), the mouse tooltip / interactive guideline remains.
My question is: How can I remove the interactive guideline at the same time that the chart is removed?
Before running d3.select("#{{id}} > *").remove(); you can run
d3.select("#{{id}}")
.on("mousemove", null)
.on("mouseout", null)
.on("dblclick", null)
.on("click", null);
and d3 will remove the event listeners.
Look at the documentation https://github.com/mbostock/d3/wiki/Selections#on
d3.select only finds the first element matching the selector. In this situation that would be the svg of the plot but not the tooltip which is a div sibling of the svg.
Instead use selectAll, which would find the svg and div:
d3.selectAll("#{{id}} > *").remove();

DimpleJS barchart styling columns

I'm basically using a modified version of : http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels .
I'd like to be able to add for each value a border on the left as high as the value (with a specific color for that border).
I'm not really sure where to start for adding that.
Any ideas?
Thanks.
More details : This is what I'd like to obtain : https://dl.dropboxusercontent.com/u/2227188/Image%202.png - the border on the left is the issue. (jsfiddle.net/mkzTk/5/ this what I currently have which is pretty much what's in the example - I don't know where to start really for adding a border)
You could append a rectangle after drawing for each element of the series as follows:
mySeries.afterDraw = function (s, d) {
var shape = d3.select(s);
svg.append("rect")
.attr("x", shape.attr("x"))
.attr("y", shape.attr("y"))
.attr("height", shape.attr("height"))
.attr("width", "10px")
.style("fill", shape.style("stroke"))
.style("pointer-events", "none");
};
The example you mention already uses the afterDraw function so just add the contents above to the existing method for labelling.
It looks nice, here's an example:
http://jsbin.com/lorin/9/edit?js,output#J:L20
I would set up each bar + edge pair as its own group based on a certain data point, and then append two rect elements to that group. Differences in color can be used to give them their distinctive colors.
Your code would look something like this:
var monthBars = d3.selectAll('.monthBar') //These will be for each chart
.data(allMyData, idFunction) //Assign and key your data
.enter()
.append('g')
.classed('monthBar', true);
.each(function(d){
var taskGroups = d3.select(this).selectAll('.taskGroup')
.data(d.dataForThisMonth, taskIdFn)
.enter()
.append('g')
.classed('.taskGroup', true);
.attr('transform', ...) //Define the x and y positioning for the group
taskGroups.append('rect')
//Make this the 'body' rect with the text in it
taskGroups.append('rect')
//Make this the edge rect
})

dc.js accessing a chart other that it's own in postRender

I have 2 charts on a page (A and B) and wish for some custom behavior to be added to chart B when a brush filter on chart A is performed.
I thought I could achieve this by doing something like;
charta.on('postRender', function(){
...
d3.selectAll('#chartb svg')
.data(nested, function(d){ return d.key})
.enter()
.append("g")
.attr("class", "aclass")... more code....
But this #chartb selector doesn't seem to work - when I inspect the DOM it has appended the <g> attributes to the <html> element and not the svg element I wanted to append to.
Is what I am trying to achieve possible?
If you are just adding stuff to the other chart, something like this should be possible. I don't think you will be able to select the generated items of the other chart and then apply a d3 join to it, because it is already joined.
I believe the problem with the code above is that d3.select is what you use to choose the context for a join, and d3.selectAll is what you use to actually make the data join. See
http://bost.ocks.org/mike/join/
So your code is trying to join to the chart and svg elements, which would have the effect you are describing. Instead you'll want to d3.select the svg and then d3.selectAll the elements you want to add - even though they don't exist yet! Yes, it's kind of a mind-bender; take a look at the above and the linked articles to get a better idea of it.
Note: there are dc convenience methods on the chart object which will execute the selects in the right context.
I got this working in the end by replacing the .enter() with repeated calls to datum() instead. A bit of a hack, but it works; If anyone can suggest a more d3ish way of acheiving this, I would be very grateful.
var svg = chart.svg();
nested.forEach(function(withValues) {
_(withValues.values).filter(function(d){return d.value < threshold}).forEach(function(timesMatchingThreshold){
svg.datum(timesMatchingThreshold)
.append("rect")
.style("opacity", 0.6)
.attr("transform", "translate(" + 30 + ", " + (-30) + ")")
.attr("class", "belowThreshold")
.attr("x", function(d) {return x(d.date)})
.attr("y", function(d) {return 200 - y(d.value)})
.attr("width", 3)
.attr("height", function(d) {return y(d.value)});
});

Resources