My code works and it shows me the elements for my data, but d3 doesn't update the text of my SVG Element after changing my data and running the same code again. I have to refresh the whole site for it to change.
var blackbox= d3.select("#content")
.selectAll(".silencer")
.data([0]);
blackbox.enter()
.append("div")
.attr("class", "silencer")
.attr("id", "silencer");
blackbox.exit().remove();
var box = blackbox.selectAll(".ursa")
.data(fraung);
box.enter()
.append("div")
.attr("class", "ursa")
.each(function(d) {
d3.select(this)
.append("svg")
.attr("class", "invoker")
.each(function(d) {
d3.select(this).append("svg:image")
.attr("xlink:href", "images/qwer.png")
.attr("x", "0")
.attr("y", "0")
.attr("width", "100")
.attr("height", "100")
.append("title")
.text(function(d) {return (d.name)});
});
d3.select(this).append("div")
.attr("class", "chen")
.each(function(d) {
d3.select(this).append("table")
.attr("class", "tab")
.each(function(d) {
d3.select(this).append("tbody")
.each(function(d) {
d3.select(this).append("tr")
.text(function(d) {return("Name: ")})
.attr("class", "key")
.each(function(d) {
d3.select(this).append("td")
.text(function(d) {return (d.name)});
});
});
});
});
});
box.exit().remove();
It sounds like your SVG element isn't being cleared before you load the second data set and so the second data set is being drawn behind the first. If the only thing that is changing is the text, it would look like nothing is happening at all. A browser refresh would clear any dynamically drawn content. Before you invoke "blackbox", you can do something like this to clear everything in the "#content" element, which I'm assuming is your SVG container.
if(!d3.select("#content").empty()) d3.select("#content").remove();
This will remove the SVG container entirely. So you'll have to create it again before you can load in new data. Alternatively if you just want to remove the child elements from the SVG container, you could do this:
if(!d3.select("#content").selectAll("*").empty()) d3.select("#content").selectAll("*").remove();
Related
I'm working on a project where I'm making multiple D3 stacked bar charts. The idea is that when a button is pressed, the plot will reload with a different dataset, similar to the code that is shown here.
My issue is with modifying this code to make the bar charts stacked. I'm not too familiar with the update functionality in D3 (I've never learned about it), so I've been trying to just append more "rect" objects to the "u" variable. It will load in correctly the first time (with all the "rect" objects where I'd expect), but whenever the update method is recalled on a button click all that gets drawn is the second iteration of the append "rect" calls. If anyone knows how to work this code into stacked bar chart functionality, I'd greatly appreciate it.
For reference, this is what I've been trying
u
.enter()
.append("rect") // Add a new rect for each new elements
.merge(u) // get the already existing elements as well
.transition() // and apply changes to all of them
.duration(1000)
.attr("x", function(d) { return x(d.group); })
.attr("y", function(d) { return y(d.value1); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.value1); })
.attr("fill", "#69b3a2")
u
.enter()
.append("rect") // Add a new rect for each new elements
.merge(u) // get the already existing elements as well
.transition() // and apply changes to all of them
.duration(1000)
.attr("x", function(d) { return x(d.group); })
.attr("y", function(d) { return y(d.value2 + d.value1); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.value1); })
.attr("fill", "#69b3a2")
I'm binding data to a bunch of nodes using d3, and I would like to arrange it so that all of the nodes change dynamically when one of them is clicked on (or some other event). Based on my understanding of d3, I think it should work like this:
var nodes = svg.selectAll(".node")
.data(someData)
.enter()
.append("circle")
.attr("r", 5)
.attr("class", ".node")
.style("fill", "blue")
.on("click", function(d, i) {
svg.selectAll(".node").style("fill", function(e, j) {
if(someCondition(i, j))
return "red";
else
return "green";
});
});
But nothing happens when I click. Even the simpler code:
var nodes = svg.selectAll(".node")
.data(someData)
.enter()
.append("circle")
.attr("r", 5)
.attr("class", ".node")
.style("fill", "blue")
.on("click", function(d, i) {
svg.selectAll(".node").style("fill", "red");
});
(which I expect would turn all of the nodes red when one of them is clicked on) does not work.
There is an error in the way you are setting the class names for your circles by calling
.attr("class", ".node")
Doing it this way would set the attribute to class=".node" which is certainly not what you want. Moreover, this would not be a valid class name. See this answer for an explanation of what characters are allowed to form a class name. To select this class name you would have to do a svg.selectAll("..node") having two dots in your selector string.
Having said that, change you code to leave out the dot to make it work:
.attr("class", "node")
Lessons learned:
.attr() takes the attribute's value literally.
When applying a CSS selector, you prefix it with a dot to select a class name.
You need specify the "cx" and "cy" properties of a circle, otherwise you wont see anything.
var nodes = svg.selectAll(".node")
.data(someData)
.enter()
.append("circle")
.attr("r", 5)
//add cx and cy here:
.attr("cx", function(d) {return d+10;/*just an example*/})
.attr("cy", function(d) {return 2*d+10;/*just an example*/})
.attr("class", "node")
.style("fill", "blue")
.on("click", function(d, i) {
svg.selectAll(".node").style("fill", "red");
});
I am new to D3.js and am trying to build rectangles that represent all nodes from an XML file. So far so good but I want interactivity with each of the rectangles I draw and to be able to capture the nodes that have been touched for further processing. So let's say I click on a rectangle, I can make it react by doing an onclick event (like increasing the font size) but I can't seem to retrieve some of the info. I'd like to create an array with the text of each item that was clicked on.
Here's the code for one instance of the rectangle.
d3.select("#chart")
.append("svg")
.attr("width", 600)
.attr("height", 2000)
.style("background", "#93A1A1")
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 25)
.attr("height", 20)
.attr("width", 200)
.attr("title", "resourceDef")
.style("fill", "#CB4B19")
d3.select("svg")
.append("text")
.attr("x", 55)
.attr("y", 37)
.attr("font-size", 11)
.attr("font-family", "sans-serif")
.text("resourceDef")
.on("mouseover", function(d) {
tempText = this.text;
alert(tempText);
d3.select(this)
.attr("font-size", 15)})
.on("mouseout", function(d) {
d3.select(this)
.attr("font-size", 11)})
I can grab style info by using but not the title and I can't find that info anywhere. Thanks for your help, I know it's a long question with probably a simple answer.
You can attach a mouse over event on the rectangle DOM by doing something like this:
d3.select("svg")
.append("rect").attr("x", 50)
.attr("y", 25)
.attr("height", 20)
.attr("width", 200)
.attr("title", "resourceDef")
.style("fill", "#CB4B19")
.on("click", function (d) {
var t = d3.select(this).attr("title");
//pushing the title into the array.
clickedTitles.push(t);
console.log(t);
});
You can get the attribute of a DOM(in your case tite) by doing something like this:
.on("click", function (d) {
var t = d3.select(this).attr("title");
clickedTitles.push(t);
console.log(t)
})
You can store the clicked rectangles title in an array like this:
//create an array
var clickedTitles = [];
//in your click function push the title into the array
clickedTitles.push(t);
//use the clickedTitles where ever you need in the code
Full code is here.
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);}) ;
I'v just started playing with d3js and find it strange that I have to create multiple selectors for each element I want to link to the background data structure for example separate selectors such as one for overlay text and one for rectangles to make an annotated bar graph.
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr('y',function(d,i){return i*10;})
.attr('height',10)
.attr('width',function(d){return d.interestingValue})
.fill('#00ff00');
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr('y',function(d,i){return i*10;})
.fill('#0000ff')
.text(function(d){return d.interestingValue});
Is there a more convenient way of combining these into a single selection and enter() chain that creates both the rects and the text elements?
Use a G (group) element. Use a single data-join to create the G elements, and then append your rect and text element. For example:
var g = svg.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(0," + i * 10 + ")"; });
g.append("rect")
.attr("height", 10)
.attr("width", function(d) { return d.interestingValue; });
g.append("text")
.text(function(d) { return d.interestingValue; });