I'm using this jsfiddle. All of the tooltips are initially correct when I hover over the bars. But when I click the weekview button to change the graph the tooltips don't get updated.
I believe the problem is in this section:
layer.enter()
.append("g")
.attr("class", "layer");
layer.attr("fill", function (d, i) {
return color(i);
})
.append("svg:title")
.text(function(d){
return d[0].s;
});
layer.exit()
.remove();
The append text is where I add the tooltips. I thought the enter and exit would refresh the bars and therefore refresh the tooltips but it isn't doing it correctly.
How do I update the tooltips when my graph changes?
It is because you are appending a new <title> element every time the bars change. The append should be done once on the enter selection and then simply update the value of the title in the update selection.
Here's a modified version of your code with some comments inline (I've removed the parts that aren't relevant to the tool tip):
layer.enter()
.append("g")
.attr("class", "layer")
.append("title"); // add new element under new layer
// add or update the value of the title element
layer.select("title").text(function(d) {
return d[0].s;
});
Related
All:
I am pretty new to CSS. One Data visualization user case I try to implement(I use D3.js) is :
Say there are several DIVs overlap each other, the one clicked will show on the top layer,I wonder how can I set its z-index when all DIVs z-index are "auto"?(for certain reason like data binding, I can not adjust its location in the DOM tree)?
The main difficulties that I have is:
How to determine the starting minimum z-index for those DIVs( because there are some other elements, even I can give a higher z-index to the one clicked and lower for all other DIVs, but the number still can possibly be smaller than other elements) So, if I can get that z-index, I do not need to know what it is, but as long as I can increase it to make sure the clicked one has higher number than that, then it is done.
Or is there other ways to do this?
var container = d3.select("body")
.append("div")
.classed("random", true)
.style("position", "fixed")
.style({
"top":"50px",
"width":"200px",
"height":"200px",
"background-color":"rgba(100,100,100, 0.5)"
})
var color = d3.scale.category20();
container.selectAll("span")
.data([1,2,3,4])
.enter()
.append("span")
.style({
"position":"absolute",
"width":"50px",
"height":"50px"
})
.style("margin", function(d,i){
return i*10+10+"px";
})
.style("background-color", function(d, i){
return color(i);
})
.text(function(d, i){
return d;
})
container.selectAll("span")
.on("click", function(d, i){
var block = d3.select(this);
// this is the part I can not figure out ================
block.moveToFront();
});
you don't use a SVG and can apply z-index style to clicked element
example here: https://jsfiddle.net/serGlazkov/z3gykfc1/
UPD:
change your example to SVG format and make top position by click https://jsfiddle.net/serGlazkov/z3gykfc1/3/
Successfully created a heatmap using d3.
Here's the FIDDLE.
I have some basic idea on using d3's mouseover events. But now I wanted move a step ahead.
This is what I'm looking for. When I hover on a legend, I wanted the hovered legend's respective data to be highlighted in the chart.
Can someone help me to achieve it?
You're not binding the data to the legend, which makes this task a bit more difficult, but you can still do it fairly easily. The idea is to assign a class defined by the fill color to the rect elements and then select accordingly in the mouseover handler. The code looks like this.
// for the rectangles
.attr("class", function(d) {
return "hour bordered " + "color-" + colorScale(d.value).substring(1);
})
// for the legend
.on("mouseover", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "blue");
})
.on("mouseout", function(d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "white");
});
Complete example here.
I'm working with this jsfiddle. I expect that when I click the WeekView button it should change the bar colors to reflect the same colors that are in my legend. But for some reason the colors are different.
I don't think
var layer = svg.selectAll(".layer")
.data(stack);
layer.enter()
.append("g")
.attr("class", "layer")
.style("fill", function (d, i) {
return color(i);
});
layer.exit()
.remove();
is being called when I switch to weekview, therefore its not replacing the old bars with the new ones its just reusing the bars from the previous views.
How can I get d3.js to replace the bars with the proper colors?
Indeed, the problem is in that part of the code: Demo
var layer = svg.selectAll(".layer")
.data(stack);
layer.enter()
.append("g")
.attr("class", "layer");
// Set the colors in the `update` cycle, not the `enter` cycle.
layer.style("fill", function (d, i) {
return color(i);
});
layer.exit()
.remove();
There is an interesting history of why this behaves this way. In earlier versions of D3, the enter and update set of elements were kept separate, just like update and exit events are still kept separate, i.e. operations you performed on the update set would not be performed on the exit set and vice-versa.
However, in version 2.0 of D3, it was decided that any element appended in the enter phase would also become a part of the update set. This was done because often the enter set of elements and the update set of elements needed to have the exact same operation performed on them (like in your case). To avoid this effect, you'll need to write the update phase before the enter phase.
Hence, in the enter cycle, elements should be appended and their initial attributes should should be set while their final values (which they should have in static state) should be set in the update cycle.
Making a bar graph in d3. I have 30+ bars, with 30+ corresponding labels on x-axis. I would like x-axis labels to be hidden when the page loads (this is working), AND APPEAR only if user cursors over the corresponding bar (svg rect object). To do this I am assigning an id to each rect and each text element. When user cursors over rect, text will appear for ONLY the selected (mouseover'd) rect.
I can assign id to rects, but not for text. Code:
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("id", function(d){
return d.slug; // slug = label downcased, this works
}); // each rect has unique id
However, similar code for my text element on x-axis doesn't assign an id?!
svg.append("g")
.call(xAxis)
.selectAll("text")
.attr("id", function (d){ // inspect text element shows no ID.
return d.slug; // text doesn't have any id
})
.style("text-anchor", "end")
.attr("opacity", 0.2);
How can I assign a unique id to my text elements in x-axis? Thank you!
The problem is that no data is bound to the x axis ticks and therefore d is undefined -- you should actually get an error message when running your code.
In this particular case, you can use the index to get the relevant data item like so.
svg.append("g").call(xAxis)
.selectAll("text")
.attr("id", function(d, i) { return dataset[i].slug; });
Note that this will only work if the number of axis ticks is the same as the number of data items.
when i do this :
var link = svg.selectAll('.link')
.data(links)
.enter().append('path')
.attr('class', 'link')
.attr('d', diagonal)
There is no node with the .link class. So selectAll returns en empty selection. But i've found that, when you call this for the first time, you can selectAll('whaterverYouWant')
That is because D3 doesn't matter about what you select, as you provide the tag name and the classes later .append('path'), .attr(class ...).
And, if you want to select elements that already exist, i read in the doc that .enter returns a placeholder selection. But if it returns a selection of placeholders (anonymous tags with .link class ?), there is no point to append a path to a path.
When i call .append, it does what i want, i.e. append a path to svg. But i don't understand the logic behind that. (I'm glad it works though, because d3 is powerful)
So, ok i selectAll('anything') and append what i want, regardless of what i selected. But if i try this:
d3.select('#savestring-debug')
.selectAll('div')
.data(debugobjs)
.enter().append('span')
.attr('style', function(d) { return 'background:#'+d.color })
.text(function(d) { return d.aff });
This would create placeholders for divs, but i append spans. Actually spans are created but i'm still looking for my divs ;)
So, what is the principle behind selectAll >> data >> enter >> append ?
thanks
The principle behind selectAll > data > enter > append is explained pretty well by
Mike Bostock here: http://bost.ocks.org/mike/join/ where he explains the concept of the data-join. I can't speak with any authority on the right way to use selectAll, but the way I use it is to select all of the elements I am going to be modifying-appending-removing within the part of the SVG that I need to modify.
So if I'm working with "rects" in a certain area, I'll do something like this:
var svg = d3.select('#graphID')
.append("svg")
.attr("width", 300)
.attr("height", 500);
var graphGroup = self.svg.append("g");
//...Inside a render function
//just want all the "rect" elements in graphGroup
var rects = graphGroup.selectAll("rect")
.data(dataset);
//depending on dataset new rects will need to be appendend
rects.enter()
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 0)
.attr("height", 0)
//all rects are transitioned to new co-ordinates
rects.transition().duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d){
return yScale(d);
})
//rects that have no data associated with them are removed
rects.exit()
.transition()
.duration(500)
.attr("x", -xScale.rangeBand())
.remove();
With the idea that I could have other rects in the SVG that do not belong to graphGroup. I just selectAll the rects in a certain area and work on them when needed.
This is a great question and a slightly odd property of D3. If you look carefully how anything is done in D3 you'll notice that everything is added by appending to what is previously created. So the logic behind having the svg.selectAll('whatever class of stuff you're going to add') is that you are kinda making a placeholder for where whatever you are about append to go. It's like the svg is a wall and you're hanging hooks on the upper ridge for you to THEN hang your paintings from. If you don't have the selectAll, I just tried this, you will still append whatever you were gonna make to the page, but it won't be appended to the svg.
The data-->enter-->append is basically saying for each element in the larger data file that you are passing into the data function, make a new element, and append this element to my selection with such and such properties (set when you use the .attr).