Selection in svg / d3 - javascript

I don't really know svg and I want to select the text of this element (and just this element).
var text = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("class", "label")
.style("fill-opacity", function(d) { return d.parent === rootZC ? 1 : 0; })
.style("display", function(d) { return d.parent === rootZC ? "inline" : "none"; })
.text(function(d) { return d.name; });
To be clearer, I have some other text in my code and I want to apply a transition on this part and not on the text above.
I apply my transition like this
transition.selectAll("text")
but don't know. (work but remove the other text elements)
Then, how to select well my text? (I tried svg.text but that doesn't work).
Many thanks in advance for your help.

If you want to select this text you create here :
var text = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("class", "label")
.style("fill-opacity", function(d) { return d.parent === rootZC ? 1 : 0; })
.style("display", function(d) { return d.parent === rootZC ? "inline" : "none"; })
.text(function(d) { return d.name; });
I would just add a unique class for this text (for example sampleClass) :
.attr('class','label sampleClass'); //adding to the class you already have
Then you can select all elements like so :
var sampleClass = d3.selectAll('.sampleClass');
Then apply the translation like so :
sampleClass.attr('translate', 'transform('+sampleX+','+samplyY')';
So your code would look like :
var text = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("class", "label sampleClass")
.style("fill-opacity", function(d) { return d.parent === rootZC ? 1 : 0; })
.style("display", function(d) { return d.parent === rootZC ? "inline" : "none"; })
.text(function(d) { return d.name; });
var sampleClass = d3.selectAll('.sampleClass');
sampleClass.attr('translate', 'transform('+sampleX+','+samplyY')';

Related

Circle Pack Layout D3 - remove add nodes on new data

I have a situation where I want users to browse 3 different data sets using circle pack layout. I am using Bostock's Zoomable circle packing.
I have added 3 options additionally which loads data and creates new nodes. Here chartid is passed from these elements.
function changeDataSet(chartid)
{
console.log(chartid);
//console.log(nodes);
//add new chart data depending on the selected option
if(chartid === "plevels")
{
root = JSON.parse(newKmap_slevels);
focus = root;
nodes = pack.nodes(root);
}
else if (chartid === "pduration")
{
root = JSON.parse(newKmap_sduration);
focus = root;
nodes = pack.nodes(root);
}
else
{
root = JSON.parse(newKmap_stype);
focus = root;
nodes = pack.nodes(root);
}
refresh();
}
Then I have the refresh function, but I am not understnading how to remove existting nodes, and add new ones based on the new data set. Showing some transition animations would be nice also.
Currently I am trying to remove and recreate the initial elements, but the chart goes blank when I do that.
var refresh = function() {
//var nodes = pack.nodes(root);
var duration = 10;
console.log(nodes);
d3.select("#conf_knowledge_map").selectAll("g")
.remove();
svg
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function(d) { return d.children ? color(d.depth) : null; })
.on("click", function(d) {
if (focus !== d) zoom(d), d3.event.stopPropagation();
});
text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; })
.style("display", function(d) { return d.parent === root ? "inline" : "none"; })
.text(function(d) {
//console.log(d);
if( d.size )
{
return d.name + ":" + d.size;
}
else
return d.name;
});
}
So my question is how can I remove and then create new nodes on click?
UPDATE
I was able to remove all nodes and add the new nodes based on the new data, but now on click to zoom the layout is all messed up. The transform function is not applied to the new nodes somehow.
var refresh = function() {
svg.selectAll(".node").remove();
svg.selectAll(".label").remove();
var nodes = pack.nodes(root);
focus = root;
circle = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.attr("r", function(d) { return d.r;})
.attr("cx", function(d) {
console.log(d);
// if(d.depth === 0)
// return 0;
// else
// return d.x - (diameter/2);
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
// .attr("transform", "translate(" + "x" + "," + "y" + ")")
.style("fill", function(d) { return d.children ? color(d.depth) : null; })
.on("click", function(d) {
// d.x = d.x - (diameter/2);
// d.y = d.y - (diameter/2);
if (focus !== d) zoom(d), d3.event.stopPropagation();
});
text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.attr("x", function(d) {return d.x - (diameter/2);})
.attr("y", function(d) {return d.y - (diameter/2);})
.style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; })
.style("display", function(d) { return d.parent === root ? "inline" : "none"; })
// .attr("transform", "translate(" + "x" + "," + "y" + ")")
.text(function(d) {
//console.log(d);
if( d.size )
{
return d.name + ":" + d.size;
}
else
return d.name;
});
}
How can I transform the new nodes to be in place and for the zoom to work properly?
UPDATE 2
Now the transform is working properly after attaching 'g' element to the circles, and showing all the nodes and text correctly. The only problem now is that the zoom does not work when I click on the circles!!
circle = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.attr("r", function(d) { return d.r;})
.attr("cx", function(d) {
if(d.depth === 0)
return 0;
else
return d.x - (diameter/2);
})
.attr("cy", function(d) {
if(d.depth === 0)
return 0;
else
return d.y - (diameter/2);
})
.style("fill", function(d) { return d.children ? color(d.depth) : null;})
.on("click", function(d) {
if (focus !== d) zoom(d); d3.event.stopPropagation();
})
.append("g")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
;
How to make the zoom work??
It so happens that I was making the mistake of simultaneously creating three independent circle pack layouts, with mixed statements one after the other. This was problematic as when you have to select svg sections, different elements were all getting selected and wrongly attached with different events.
So I decided to separate the three implementations and create the three layouts one after the other, only after each one finished. This way I kept the section selection and attaching events etc, separate and then load them as and when required.

D3.js Interactive Legend Toggling

I have a line graph I've made from nested data. The user can pick which keys will be graphed. Each key value has 4 child values associated with it. Every key has the same child value names. Each child value has it's own line. So, if the user picks 2 keys to be graphed, 8 lines will be on the graph. 4 for one key and 4 for the other.
I've made a legend that will display the child name and the pattern of it's line. I would like to be able to click the name of the child value in the legend and all child values with that name will be hidden, even if they have a different key.
Here is how I nested:
var servers = d3.nest()
.key(function (d) { return d.serverName; })
.entries(data)
How I defined lines:
var lineGen = d3.svg.line()
.x(function (d) {
return x(timeFormat.parse(d.metricDate));
})
.y(function (d) {
return y(d.ServerLogon);
})
.interpolate("linear")
var lineGen2 = d3.svg.line()
.x(function (d) {
return x(timeFormat.parse(d.metricDate));
})
.y(function (d) {
return y1(d.Processor);
})
.interpolate("linear");
var lineGen3 = d3.svg.line()
.x(function (d) {
return x(timeFormat.parse(d.metricDate));
})
.y(function (d) {
return y2(d.AdminLogon);
})
.interpolate("linear");
var lineGen4 = d3.svg.line()
.x(function (d) {
return x(timeFormat.parse(d.metricDate));
})
.y(function (d) {
return y3(d.ServerExport);
})
.interpolate("linear");
How I append lines, text and line pattern for legend:
servers.forEach(function (d, i) {
visObjectLoc.append("svg:path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke-dasharray", ("15, 3"))
.attr("d", lineGen2(d.values))
.attr("id", "Processor")
.style("stroke", function () {
return d.color = color(d.key);
});
visObjectLoc.append("text")
.attr("x", WIDTH / 4)
.attr("y", 34)
.style("text-anchor", "middle")
.style("font-size", "18px")
.text("Processor")
.on("click", function () {
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
d3.select("#Processor")
.transition().duration(100)
.style("opacity", newOpacity);
d.active = active;
});
visObjectLoc.append("line")
.attr("x1", 455)
.attr("x2", 370)
.attr("y1", 44)
.attr("y2", 44)
.attr("stroke-width", 2)
.attr("stroke", "black")
.attr("stroke-dasharray", ("15, 3"))
});
So far, the onClicks are only hiding the first key's child line. I've tried giving each child line for each server the same ID. I'm thinking I'm not looping through the keys correctly or maybe each line isn't getting the ID it needs. Any suggestions are welcome and appreciated.
IDs are unique. If you want to select a group of things, give them a class. If you could put the project in a JSFiddle I could test it, but it might be as simple as changing the line in the servers.forEach(... in the following way:
.attr("id", "Processor")
to
.attr("class", "Processor")
and then in the onClick callback:
d3.selectAll(".Processor")
.transition().duration(100)
.style("opacity", newOpacity);
...

display text in each leaf when parent is focused d3

I would like to display text in leaf when parent is focused like this: http://i60.tinypic.com/25qxdzt.jpg. Here is my code which display text only when last parent is focused:
var text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; })
.style("display", function(d) { return d.children ? null : "none"; })
.text(function(d) { return d.name; });

D3 treemap - How to insert div into first level nodes?

I am new to D3.js and want to make a treemap like this: http://bl.ocks.org/mbostock/4063582#index.html
What I am going to do is to insert new div into parent nodes.
...
d3.json("flare.json", function(error, root) {
var node = div.datum(root).selectAll(".node")
.data(treemap.nodes)
.enter().append("div")
// parent nodes have class name "first"
.attr("class", function(d) { return d.children ? "node first" : "node"; })
.call(position)
.style("background", function(d) { return d.children ? color(d.name) : null; })
.text(function(d) { return d.children ? null : d.name; });
// my code
var parents = d3.selectAll(".first")
.data(treemap.value(function(d) { return d.size; }).nodes) // ISSUE
.enter().append("div")
.text(function(d) { return d.name; });
d3.selectAll("input").on("change", function change() {
var value = this.value === "count"
? function() { return 1; }
: function(d) { return d.size; };
node
.data(treemap.value(value).nodes)
.transition()
.duration(1500)
.call(position);
});
});
...
But this doesn't work correctly.
HOW CAN I UPDATE ONLY THE PAREENT NODES BY ADDING NEW DIV TO THEM?
Could anybody help me please. Thanks a lot...

D3js filter selection

I am creating an interactive bubble chart of fortune 500 data. I am trying to reduce a selection but can't figure out why it's not working as I expect.
var bubble = d3.layout.pack()
...
d3.json("js/data/fortune2009.json", function(json) {
var node = svg.data([json]).selectAll("g.node")
.data(bubble.nodes); // filter here?
node.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("circle")
.attr("r", function(d) { return d.r; })
.attr("class", function(d) { return "q" + color(d.Profit) + "-9"; });
node.filter(function(d) { return !d.children; })
.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.Company.substring(0, d.r / 3); });
});
The JSON data looks like this:
{
"children":[
{
"Year" : "2009",
"Rank" : "1",
"Revenue" : "442",
"Profit" : "851.00",
"Company" : "Exxon Mobil"
},
....
}
I want to remove the parent node. I am trying to use a filter function like described here.
.filter(function(d) { return !d.children; })
But if i add my filter function where I initialize the node selection, either nothing happens or I get an error.
(The filter works when I apply it before appending text)
How do I solve this?
UPDATE: Here is a JSFIDDLE http://jsfiddle.net/B9w4N/8/ showing my problem.
You had the right idea. You just needed to apply the filter where the circles are created (in addition to where the text is created):
node
.filter(function(d) { return !d.children; })
.append("circle")
.attr("r", function(d) { return d.r; });
Forked version on JSFiddle
You can filter the array that the layout outputs:
var node = svg.data([fortune2009]).selectAll("g.node")
.data(function(d) {
return bubble.nodes(d).filter(function(v) {
return v.depth > 0;
});
});
See updated fiddle: http://jsfiddle.net/B9w4N/10/

Categories