I found no direct answer for this, please forgive me if this has been covered differently in another topic.
I draw a bar chart which appears with a transition. I also want to add a tooltip which displays the value of data on mousehover.
Using the code below I have managed to obtain either the tooltip or the transition, but never the 2 together, which is my objective.
chart.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("fill", function(d) {return colorscale(colorize(d.age));})
.attr("x", function(d) {return xscale(d.name);})
.attr("y", height - 3)
.attr("height", 3)
.attr("width", xscale.rangeBand())
.append("title")
.text(function(d){return d.age;})
.transition()
.duration(1600)
.attr("y", function (d) {return yscale(d.age);})
.attr("height", function (d) {return height - yscale(d.age);}) ;
If I remove
.append("title")
.text(function(d){return d.age;})
Then my transition works fine. If I but those 2 lines back I can see my tooltip but I lose my transition.
Any suggestion would be appreciated!
You can see the result here
Thank you
You need to add the transition to the rect and not the title element:
var sel = chart.selectAll(".bar")
.data(data)
.enter()
.append("rect");
sel.append("title")
.text(function(d){return d.age;});
sel.transition()
.duration(1600)
.attr("y", function (d) {return yscale(d.age);})
.attr("height", function (d) {return height - yscale(d.age);}) ;
Related
I am trying to replicate this example of a multiline chart with dots. My data is basically the same, where I have an object with name and values in the first level, and then a couple of values in the second level inside values. For the most part, my code works, but for some reason, the j index in the anonymous function for the fill returns an array of repeated circle instead of returning the parent of the current element. I believe this may have something to do with the way I created the svg and selected the elements, but I can't figure it out. Below is an excerpt of my code that shows how I created the svg, the line path and the circles.
var svgb = d3.select("body")
.append("svg")
.attr("id","svg-b")
.attr("width", width)
.attr("height", height)
var gameb = svgb.selectAll(".gameb")
.data(games)
.enter()
.append("g")
.attr("class", "gameb");
gameb.append("path")
.attr("class", "line")
.attr("d", function(d) {return line_count(d.values); })
.style("stroke", function(d) { return color(d.name); })
.style("fill", "none");
gameb.selectAll("circle")
.data(function(d) {return d.values;})
.enter()
.append("circle")
.attr("cx", function(d) {return x(d.date);})
.attr("cy", function(d) {return y_count(d.count);})
.attr("r", 3)
.style("fill", function(d,i,j) {console.log(j)
return color(games[j].name);});
j (or more accurately, the third parameter) will always be the nodes in the selection (the array of circles here), not the parent. If you want the parent datum you can use:
.attr("fill", function() {
let parent = this.parentNode;
let datum = d3.select(parent).datum();
return color(datum.name);
})
Note that using ()=> instead of function() will change the this context and the above will not work.
However, rather than coloring each circle independently, you could use a or the parent g to color the circles too:
gameb.append("g")
.style("fill", function(d) { return color(d.name); })
.selectAll("circle")
.data(function(d) {return d.values;})
.enter()
.append("circle")
.attr("cx", function(d) {return x(d.date);})
.attr("cy", function(d) {return y_count(d.count);})
.attr("r", 3);
Here we add an intermediate g (though we could use the original parent with a few additional modifications), apply a fill color to it, and then the parent g will color the children circles for us. The datum is passed on to this new g behind the scenes.
I am trying to do the following:
Click a node
Enlarge the image of that node according to its current width and height
The idea is to determine x,y position based on its own width and height so it does not run wild.
My code for force tick
function render(){
force.on("tick", function() {
node.attr("x", function(d) {return d.x-d.width/2})
.attr("y", function(d) {return d.y-d.height/2});
Declaration of node
var node = container.append("g").selectAll(".node")
.data(graph.nodes)
.enter()
.append("image")
.attr("xlink:href",function(d){return d.type+ ".svg" ;})
.attr("class", "node")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", "24px")
.attr("height", "24px")
.attr("isChosen", "no")
.on("click",click);
Sample of data
"nodes":[
{"id":"0", "name":"ETCO I","type":"Company","trust":50},
{"id": "1","name":"PINKERTON Eidel ","type":"human","trust":50}
],
"links":[
{"source":1,"target":0,"linktype":"Secretary"},
{"source":1,"target":0,"linktype":"Director"},
Click handle
function click(node)
d3.select(this)
.attr("isChosen", "yes")
.transition()
.duration(750)
.attr("x", function(d) {return d.x-24})
.attr("y", function(d) {return d.y-24})
.attr("width", "35px")
.attr("height", "35px")
but it does not work, the image is all on left conner. Where does my logic go wrong?
I have a simple D3 donut diagram with a .mouseover() event that updates an SVG:text element at the center of the donut hole. It works great...
Until I encounter users with IE 9, 10 and 11. These browsers won't render the center label. Is there a way to accommodate IE and show the center label in both browsers?
The HTML page is based on HTML5BoilerPlate with the various shims to detect old browsers.
The D3 script seems pretty straight forward.
d3.json("data/census.php", function(error, dataset) {
var h = 220, w = 295;
var outerRadius = h / 2, innerRadius = w / 4;
var color = d3.scale.category20b();
var svg= d3.select("#dailycensus")
.append("svg")
.data([dataset])
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.value(function(d,i) { return +dataset[i].Census; });
var arcs = svg.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice");
var syssum = d3.sum(dataset, function(d,i) { return +dataset[i].Census; });
var tip = d3.tip()
.attr("class", "d3-tip")
.html(String);
var formatter = d3.format(".1%");
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html("Health System Census"); // Default label text
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(syssum); // Default label value
arcs.append("svg:path")
.call(tip) // Initialize the tooltip in the arc context
.attr("fill", function(d,i) { return color(i); }) // Color the arc
.attr("d", arc)
.on("mouseover", function(d,i) {
tip.show( formatter(dataset[i].Census/syssum) );
// Update the doughnut hole label with slice meta data
svg.select("#hospital").remove();
svg.select("#census").remove();
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html(dataset[i].Facility);
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(+dataset[i].Census);
})
.on("mouseout", function(d) {
tip.hide();
// Return the doughnut hole label to the default label
svg.select("#hospital").remove();
svg.select("#census").remove();
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.html("Health System Census");
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.html(syssum);
})
Replace all the .html calls with .text calls. Generally innerHTML is for HTML things although browsers are giving it SVG support as everybody keeps expecting it to work.
It's not immediately clear what is causing the issue, however setting the .text property instead resolves the issue after testing with Fiddler:
svg.append("text")
.attr("id", "hospital")
.attr("class", "label")
.attr("y", -10)
.attr("x", 0)
.text(dataset[i].Facility);
svg.append("text")
.attr("id", "census")
.attr("class", "census")
.attr("y", 40)
.attr("x", 0)
.text(+dataset[i].Census);
})
After investigating the <text /> elements directly in the Developer Tools you can see that setting the .innerHTML property doesn't render the results you'd expect, however .textContent does.
If this is working as expected in both Chrome and Firefox, I'll gladly open up an interop bug for the IE team to look into. We've been doing some SVG work lately, so I may find that this has already been discussed.
I had the same issue and innerSvg polyfill helps me. Now html() in SVG works in IE.
Here's the code I am running http://jsfiddle.net/a7as6/14/
I know that I can use this code to change node to image:
node.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", "-8px")
.attr("y", "-8px")
.attr("width", "16px")
.attr("height", "16px");
But when I use it and my nodes are still not images. Any idea why?
And I am wondering how to change each nodes with different images?
Thx.
You've got the right idea for appending the images, but you need to operate on node.enter() as in:
node.enter().append("image")
.attr("class", function (d) {
return "node " + d.id;
})
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("width", "16px")
.attr("height", "16px");
You then need to get your tick function to place the images, as in:
function tick() {
node.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
And here's the working fiddle. Not that you'll need to move the images around so they look right, you can use the dx and dy properties to bump them.
I've brought some of my code to match D3 standards. But now I'm having issues with my update function working. I'm trying to follow the General Update Pattern.
When I run the function, I get an error in the console: "TypeError: gbars.enter(...).attr is not a function"
function updateBars()
{
xScale.domain(d3.range(dataset.length)).rangeRoundBands([0, w], 0.05);
yScale.domain([0, d3.max(dataset, function(d) { return (d.local > d.global) ? d.local : d.global;})]);
var gbars = svg.selectAll("rect.global")
.data(dataset);
gbars.enter()
.attr("x", w)
.attr("y", function(d) {return h - yScale(d.global); })
.attr("width", xScale.rangeBand())
.attr("height", function(d) {return yScale(d.global);});
gbars.transition()
.duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.global);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d.global);
});
gbars.exit()
.transition()
.duration(500)
.attr("x", -xScale.rangeBand())
.remove();
}
I understand, I probably have a lot of errors with the update function in general, but it's hard to troubleshoot when hung-up at the beginning.
My goal for the update will be to remove/add series from the chart. They can choose to display Global, Local, or both (default). I'd actually prefer that when they hover over the legend it shows only that series. On mouseout, it would go back to default.
Working Fiddle here.
You need to say what you want to do for new nodes. Yes it's almost always append but you still have to tell it:
gbars.enter()
.append("rect")
.attr("class", "global")
.attr("x", w)
Other than that error, your structure for general update pattern looks correct on the surface.