In D3.js, how do I assign HTML elements/attributes to tick labels? I'm specifically interested in making them hyperlinks, but it could be generalized to making them images, or something strange like alternating div classes.
var data = [{"count": 3125,"name": "aaa"}, {"count": 3004,"name": "bbb"}...
y = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1)
.domain(data.map(function (d) {
return d.name;
}))
yAxis = d3.svg.axis().scale(y)
vis.append("g")
.attr("class", "y axis")
.call(yAxis)
http://jsfiddle.net/SFDrv/
So in the JSfiddle, how would I link to www.example.com/aaa, www.example.com/bbb, www.example.com/ccc, etc?
For the JSfiddle you posted, you can create a selection of all text that are strings (these are the y-axis ticks), and then use .on("click", function), to link each label. Here's a working example:
d3.selectAll("text")
.filter(function(d){ return typeof(d) == "string"; })
.style("cursor", "pointer")
.on("click", function(d){
document.location.href = "http://www.example.com/" + d;
});
I forked your JSFiddle and have the whole example there: http://jsfiddle.net/mdml/Qm9U7/.
A better solution would be to have an array of y-axis values and to use those to filter the text elements in the document, instead of testing whether each text element's data is a string. The best way to do that depends on the rest of the code, however, so it may differ from application to application.
To create axis with images you need to create them yourself and not use the d3.svg.axis(). This creates floated li tags with certain width...
// generate axis
x = d3.scale.linear().range([min, max]).domain([minValue, maxScaleValue]);
xAxis = d3.scale.identity().domain([minValue, maxScaleValue]);
var ticks = xAxis.ticks(10);
if (ticks.length) {
var tickDiff = Math.abs(ticks[1] - ticks[0]);
scope.legend
.selectAll('li')
.data(ticks)
.enter()
.append('li')
.text(xAxis)
.style('width', x(tickDiff));
}
scope.legend
.selectAll("li")
.data(ticks)
.exit()
.remove();
Also there is useful article about d3 axis that helped me a lot.
Related
with a few questions from here, I was able to add an event listener to my y-axis label. However, I would like to add a different link for each y-axis label so that when you click on a label(in my example the numbers), a new window opens. The link is stored in my CSV, but I have not been able to add it with function(d) {return d.url}. It might be a problem that the y-axis label is created with an array? Does anyone have a solution for this? Here is my code https://codepen.io/Lea12/pen/dyzKEOV
var y_axis = d3.scaleBand()
.range([ height, 0 ])
.domain(data.map(function(d) { return d.activity; }))
.padding(0.01);
svg2.append("g")
.call(d3.axisLeft(y_axis))
.attr("class","y_axis")
.selectAll("text")
.on("click", function(d){window.open(d.url,"_blank")})
My csv looks like this:
group,activity,value,card,url
a,1,50,"card","www.google.de"
Only the array of activities is bound to your axis, but you can use the index number to get the url from your data array.
svg2.append("g")
.call(d3.axisLeft(y_axis).tickFormat(function(d,i) { return data[i].url }))
.attr("class","y_axis")
.selectAll("text")
.on("click", function(d,i){ window.open(data[i].url,"_blank")});
I have a d3.js line chart which may have negative values. My Y-span starts at 0 and goes up to the maximum value in the dataset. Thus, the line goes underneath the X-axis when there is a negative value.
This is fine, but what I need is to just hide the part of the line that is underneath the X-axis. I want to keep the values as they are, just use some CSS or JS to make the part of the line underneath the X-axis invisible.
I have tried with various overflow settings, but it doesn't seem to help. It is possible to make everything below the X-axis invisible by placing an element over that part, but then the X-axis labels are hidden too.
This is the code drawing the line:
x.domain([d3.min(data, function(d) { return d.date}), d3.max(data, function(d) { return d.date})]);
y.domain([0, 1.05 * d3.max(data, function (d) { return d.value; })]);
area.y0(y(0));
g.append("path")
.datum(data)
.attr("fill", "#f6f6f6")
.attr("d", area);
//create line
var valueline = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
g.append("path")
.data([data])
.attr('fill', 'none')
.attr('stroke', '#068d46')
.attr("class", "line")
.attr("d", valueline);
Fiddle: https://jsfiddle.net/c1bvrd50/1/
One way to solve this problem is to clip the line inside a rectangle covering the positive values area. In SVG, this is done with clip-path.
This happens in two steps:
Define a rect inside clipPath, covering the chart area only:
g.append('clipPath')
.attr('id', 'clipRect')
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height)
Clip the line path, using clip-path attribute:
g.append("path")
.data([data])
/* ... */
.attr("d", valueline)
.attr('clip-path', 'url("#clipRect")')
Updated jsFiddle implementing the solution: here.
What is left to do is update the tooltip's mousemove event listener in order not to display the tooltip whenever the value is lower than 0.
Recommendation as P.S.: Whatever the use case of the chart is, it is most likely desirable to show the portions of the chart with negative values. Therefore, the chart's y axis should cover values lower than 0, rather than omitting these.
I am trying to draw barchart with each bar next to its corresponding checkbox. I have several <div className="geneCountBar">s and I try to select all of them, append the data and then draw bars using the following function:
createBarChart = () => {
let datum = this.props.datum.map((geneObj) => {
return geneObj["values"].length;
});
d3.selectAll("div.geneCountBar")
.data(datum)
.enter()
.append("div")
.attr("class", "bar")
.attr('transform', 'rotate(-90)')
.style("height", function(d) {
var barHeight = d * 5;
return barHeight + "px";
});
}
I just took it from the most basic tutorial on barchart creation with d3:
http://alignedleft.com/tutorials/d3/making-a-bar-chart
Somehow after the function runs - I trigger it with the button when the DOM has already been rendered - no DOM manipulation happens at all. I checked datum and it is correct: just an array of several values. The number of elements in the datum corresponds to the number of the selected divs. I checked whether d3.selectAll(div.geneCountBar) actually selects the right elements and it is. The DOM looks like that:
To further clarify what I actually want to achieve. Here you can see checkboxes:
Next to each one of them I want to draw a bar which would represent the amount of each item present in the dataset.
What am I doing wrong here? Any suggestions will be greatly appreciated.
On this case you are not using svg. Need to Style your divs. Try this:
Remove Transform, style width intead heigh
Style background-color for div,
add & nbsp; to prevent colapse empty div
change
createBarChart = () => {
let datum = this.props.datum.map((geneObj) => {
return geneObj["values"].length;
});
d3.selectAll("div.geneCountBar")
.data(datum)
.enter()
.append("div")
.attr("class", "geneCountBar") // <----CHANGE class
.style("background-color","red")
.style("width", function(d) { return (d*5)+"px";})
.html(" ")
}
Every bar must look like this:
<div class="geneCountBar" style="backgroud-color:red;width:50px;"> </div>
Or, on CSS define .geneCountBar{background-color:red;}
<div class="geneCountBar" style="width:50px;"> </div>
I've not access to your rig. Here a working code.
var myData=[15, 30, 20, 10]
var graph = d3.select("#graph")
graph.selectAll("div#graph")
.data(myData) // maybe this's data(myData), not data(datum)
.enter()
.append("div")
.attr("class", "geneCountBar") // <----CHANGE class
.style("width", function(d) {return (d*5)+"px";})
.html(" ")
.geneCountBar {
background-color: red;
margin:3px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="graph">
</div>
Hope this help
I ended up drawing bars without the use of d3s entering the data.
I put the following divs on the page:
<div className="geneCountBar"
style={{width: this.getHeightForGeneBar(idx, innerIdx), height:"10px",
backgroundColor: getColorForGene(this.getGeneIdx(idx, innerIdx)),
borderRadius: "2px"}}> </div>
And set up individual bars height and color in code with the two functions: getHeightForGeneBar(idx, innerIdx) and getColorForGene(this.getGeneIdx(idx, innerIdx)).
I have two questions.
I want to add data labels to a stacked bar chart.
I try to change the Code of this Example StackedBarChart, but I am not able to add data labels.
if the labels are added I need to prevent overlapping. I hope I can do this like John Williams in his blog for pie charts www.safaribooksonline.com/blog/2014/03/11/solving-d3-label-placement-constraint-relaxing/ or with the d3 extension D3-Labeler //tinker10.github.io/D3-Labeler/.
I found this jsfiddle http://jsfiddle.net/3a5Wk/1/ code and I am very grateful if you can also put the answers in a jsfiddle or explain your code in more detail as I am just starting on d3-Charts.
I have solved the issue of the first question by adding the Label code. I have added:
var dataText = svg.selectAll(".dtext")
.data(layers)
.enter().append("g")
.attr("class", "g")
dataText.selectAll("rect")
.data(function(d) { return d; })
.enter().append("text")
.attr("x", function(d) { return x(d.x) +18; })
.attr("y", function(d) { return y(d.y + d.y0) +10; })
.style("text-anchor", "middle")
.style("font-size", "10px")
.style("color", "white")
.text(function (d) {return (d.y);});
Now I still got the Problem that the labels overlap.
How can I change the position of the labels in a way that they don't overlap?
In Mike Bostocks example http://bost.ocks.org/mike/nations/ there is so much data that putting the names of the countries there would make it chaotic, but for a smaller project I would like to display it.
I found this in the source:
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(position)
.sort(order);
dot.append("title")
.text(function(d) { return d.name; });
But somehow a title never shows up. Does anybody have an idea, how to display the name, next to the bubble?
As the other answer suggests, you need to group your elements together. In addition, you need to append a text element -- the title element only displays as a tooltip in SVG. The code you're looking for would look something like this.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2004))
.enter()
.append("g")
.attr("class", "dot")
.call(position)
.sort(order);
dot.append("circle")
.style("fill", function(d) { return color(d); });
dot.append("text")
.attr("y", 10)
.text(function(d) { return d.name; });
In the call to position, you would need to set the transform attribute. You may have to adjust the coordinates of the text element.
Unfortunately grouping the text and circles together will not help in this case. The bubbles are moved by changing their position attributes (cx and cy), but elements do not have x and y positions to move. They can only be moved with a transform-translate. See: https://www.dashingd3js.com/svg-group-element-and-d3js
Your options here are:
1) rewrite the position function to calculate the position difference (change in x and change in y) between the elements current position and its new position and apply that to the . THIS WOULD BE VERY DIFFICULT.
or 2) Write a parallel set of instructions to setup and move the tags. Something like:
var tag = svg.append("g")
.attr("class", "tag")
.selectAll(".tag")
.data(interpolateData(2004))
.enter().append("text")
.attr("class", "tag")
.attr("text-anchor", "left")
.style("fill", function(d) { return color(d); })
.text(function(d) { return d.name; })
.call(tagposition)
.sort(order);
You will need a separate tagposition function since text needs 'x' and 'y' instead of 'cx', 'cy', and 'r' attributes. Don't forget to update the "displayYear" function to change the tag positions as well. You will probably want to offset the text from the bubbles, but making sure the text does not overlap is a much more complicated problem: http://bl.ocks.org/thudfactor/6688739
PS- I called them tags since 'label' already means something in that example.
you have to wrap the circle element and text together , it should look like
<country>
<circle ></circle>
<text></text>
</country>