Always Show Tooltip Regardless of Mouseover - javascript

Im new to JS and HTML so I am having a hard time implementing this change:
I want the values of the stacked bar graph to always be displayed rather than being dependent of the mouseover.
How would I go about changing the below code to achieve this?
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
/* Data in strings like it would be if imported from a csv */
var data = [
{ year: "2006", redDelicious: "10", mcintosh: "15", oranges: "9", pears: "6" },
{ year: "2007", redDelicious: "12", mcintosh: "18", oranges: "9", pears: "4" },
{ year: "2008", redDelicious: "05", mcintosh: "20", oranges: "8", pears: "2" },
{ year: "2009", redDelicious: "01", mcintosh: "15", oranges: "5", pears: "4" },
{ year: "2010", redDelicious: "02", mcintosh: "10", oranges: "4", pears: "2" },
{ year: "2011", redDelicious: "03", mcintosh: "12", oranges: "6", pears: "3" },
{ year: "2012", redDelicious: "04", mcintosh: "15", oranges: "8", pears: "1" },
{ year: "2013", redDelicious: "06", mcintosh: "11", oranges: "9", pears: "4" },
{ year: "2014", redDelicious: "10", mcintosh: "13", oranges: "9", pears: "5" },
{ year: "2015", redDelicious: "16", mcintosh: "19", oranges: "6", pears: "9" },
{ year: "2016", redDelicious: "19", mcintosh: "17", oranges: "5", pears: "7" },
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
return data.map(function(d) {
return {x: parse(d.year), y: +d[fruit]};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) { return d.x; }))
.rangeRoundBands([10, width-10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) { return colors[i]; });
var rect = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.attr("width", x.rangeBand())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Anjou pears";
case 1: return "Naval oranges";
case 2: return "McIntosh apples";
case 3: return "Red Delicious apples";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

I guess you need to add a label (tooltip) for each rect result. With little calculations I've got this:
http://jsfiddle.net/5o4jefap/87/
Here is the modified part without mouse events attached:
var rect = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("g")
.attr('class', 'tooltip')
.append('rect')
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.attr("width", x.rangeBand())
groups.selectAll('.tooltip')
.append("rect")
.attr('width', 15)
.attr('height', 15)
.attr('fill', 'white')
.style("opacity", 0.5)
.attr('y', function(d) {
var rectY = y(d.y0 + d.y),
rectHalfHeight = ((y(d.y0) - y(d.y0 + d.y)) / 2),
tooltipHalfHeight = (this.getBBox().height / 2);
return rectY + rectHalfHeight - tooltipHalfHeight;
})
.attr("x", function(d) {
var rectX = x(d.x),
rectHalfWidth = (x.rangeBand() / 2),
tooltipHalfWidth = (this.getBBox().width / 2);
return rectX + rectHalfWidth - tooltipHalfWidth;
})
groups.selectAll('.tooltip')
.append('text')
.attr("x", function(d) {
var rectX = x(d.x),
rectHalfWidth = (x.rangeBand() / 2),
tooltipHalfWidth = (this.getBBox().width / 2);
return rectX + rectHalfWidth - tooltipHalfWidth;
})
.attr("y", function(d) {
var rectY = y(d.y0 + d.y),
rectHalfHeight = ((y(d.y0) - y(d.y0 + d.y)) / 2),
tooltipHalfHeight = (this.getBBox().height / 2);
return rectY + rectHalfHeight - tooltipHalfHeight;
})
.attr("dy", '5px')
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.text(function (data) {
return data.y;
} );

Related

D3 line chart line selected on hoover even if opacity of the line is zero

I have a D3 line chart in my application and on hovering on the line a text is shown above the line displaying the line name. After that I had implemented a legend alongside with the chart and legend allows making lines appear and disappear upon clicking on legend item. My Sample code is given below. But now it has caused a problem like this. As now I'm setting the opacity of the line which was selected from legend to zero, the line gets disappeared. But if I hover in the area where the line was earlier still it shows the on hover text. Does anyone know a solution to avoid this problem.
You can see this issue by clicking on both items in the legend, Then all lines get disappeared. But if you move the mouse over the chart, where the lines were earlier, you will see that text will show on hovering
Example : https://jsfiddle.net/yasirunilan/ymavtsbj/4/
var data = [{
name: "USA",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
},
{
name: "Canada",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
});
/* Scale */
var xScale = d3.scaleTime()
.domain(d3.extent(data[0].values, d => d.date))
.range([0, width - margin]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data[0].values, d => d.price)])
.range([height - margin, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3.select("#chart").append("svg")
.attr("width", (width + margin) + "px")
.attr("height", (height + margin) + "px")
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.price));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('id', d => d.name.replace(/\s+/g, '') + "-line")
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.append("text")
.attr("class", "title-text")
.attr("font-weight", "bold")
.style("fill", color(i))
.text(d.name)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", -30);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', d => color(d.name))
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.attr('id', d => d.name.replace(/\s+/g, '') + "-circle")
.style("fill", d => color(d.name))
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(`${d.price}`)
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.price) - 10);
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.price))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height-margin})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 15)
.attr("transform", "rotate(-90)")
.attr("fill", "#000")
.text("Total values");
var keys = []
// Add the Legend
var legend = d3.select("body").selectAll(".legend")
.data(data.map(d => d.name))
.enter().append("font")
.attr("class", "legend") // style the legend
.style("color", color)
.style("margin-left", 10 + "px")
.style("padding-left", 10 + "px")
.html(d => d)
d3.selectAll(".legend")
.on("click", function(d) {
keys.includes(d) ?
keys.splice(keys.indexOf(d), 1) :
keys.push(d)
d3.select(this).style("opacity", () => keys.includes(d) ? .5 : 1)
d3.select("#" + d.replace(/\s+/g, '') + "-line")
.transition().duration(100)
.style("opacity", () => keys.includes(d) ? 0 : 1);
d3.select("#" + d.replace(/\s+/g, '') + "-circle")
.transition().duration(100)
.style("opacity", () => keys.includes(d) ? 0 : 1);
})
You can set the CSS pointer-events to none in order to disable all mouse interactions and the set it to auto when you change the opacity back to 1 to enable them.
You can do it using this line:
.style("pointer-events", () => keys.includes(d) ? "none" : "auto");
Working demo: JsFiddle
Alternatively you can replace the lines that sets the opacity and the pointer-events by just setting the display css property like so:
.style("display", () => keys.includes(d) ? "none" : "inline");
Working demo: JsFiddle

D3 Multi Series Line Chart with Clickable Legend

I'm having a D3 v3 Multi Series line chart in my application and it works properly. But in a scenario where the data points for all series are equal, we can't identify the multiple lines because they are getting overlapped with each other. If all the data points are equal for three series, we can only see one single line instead of three lines. After getting some suggestions from developers I thought of going with a legend for the chart so that I can click the legend and see lines of the chart. Following is my sample code. Can someone help me to implement a legend with clickable behavior for it? Or else if there is any other solution for the problem they are also welcome.
Sample Code: https://jsfiddle.net/yasirunilan/rvoft1h8/
var data = [
{
name: "USA",
values: [
{date: "2000", price: "100"},
{date: "2001", price: "110"},
{date: "2002", price: "145"},
{date: "2003", price: "241"},
{date: "2004", price: "101"},
{date: "2005", price: "90"},
{date: "2006", price: "10"},
{date: "2007", price: "35"},
{date: "2008", price: "21"},
{date: "2009", price: "201"}
]
},
{
name: "Canada",
values: [
{date: "2000", price: "100"},
{date: "2001", price: "110"},
{date: "2002", price: "145"},
{date: "2003", price: "241"},
{date: "2004", price: "101"},
{date: "2005", price: "90"},
{date: "2006", price: "10"},
{date: "2007", price: "35"},
{date: "2008", price: "21"},
{date: "2009", price: "201"}
]
},
{
name: "Maxico",
values: [
{date: "2000", price: "100"},
{date: "2001", price: "110"},
{date: "2002", price: "145"},
{date: "2003", price: "241"},
{date: "2004", price: "101"},
{date: "2005", price: "90"},
{date: "2006", price: "10"},
{date: "2007", price: "35"},
{date: "2008", price: "21"},
{date: "2009", price: "201"}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
});
/* Scale */
var xScale = d3.scaleTime()
.domain(d3.extent(data[0].values, d => d.date))
.range([0, width-margin]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data[0].values, d => d.price)])
.range([height-margin, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3.select("#chart").append("svg")
.attr("width", (width+margin)+"px")
.attr("height", (height+margin)+"px")
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.price));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.append("text")
.attr("class", "title-text")
.style("fill", color(i))
.text(d.name)
.attr("text-anchor", "middle")
.attr("x", (width-margin)/2)
.attr("y", 5);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', (d, i) => color(i))
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.style("fill", (d, i) => color(i))
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(`${d.price}`)
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.price) - 10);
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.price))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height-margin})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 15)
.attr("transform", "rotate(-90)")
.attr("fill", "#000")
.text("Total values");
I'm posting the solution that I used.
https://jsfiddle.net/yasirunilan/rvoft1h8/7/
var data = [{
name: "USA",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
},
{
name: "Canada",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
},
{
name: "Maxico",
values: [{
date: "2000",
price: "100"
},
{
date: "2001",
price: "110"
},
{
date: "2002",
price: "145"
},
{
date: "2003",
price: "241"
},
{
date: "2004",
price: "101"
},
{
date: "2005",
price: "90"
},
{
date: "2006",
price: "10"
},
{
date: "2007",
price: "35"
},
{
date: "2008",
price: "21"
},
{
date: "2009",
price: "201"
}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
});
/* Scale */
var xScale = d3.scaleTime()
.domain(d3.extent(data[0].values, d => d.date))
.range([0, width - margin]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data[0].values, d => d.price)])
.range([height - margin, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3.select("#chart").append("svg")
.attr("width", (width + margin) + "px")
.attr("height", (height + margin) + "px")
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
/* Add line into SVG */
var line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.price));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('id',function(d){ return d.name.replace(/\s+/g, '')+"-line"; })
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.append("text")
.attr("class", "title-text")
.style("fill", color(i))
.text(d.name)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", 5);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', (d, i) => color(i))
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.attr('id',function(d){ return d.name.replace(/\s+/g, '')+"-circle"; })
.style("fill", (d, i) => color(i))
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(`${d.price}`)
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.price) - 10);
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.price))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height-margin})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 15)
.attr("transform", "rotate(-90)")
.attr("fill", "#000")
.text("Total values");
var dataNest = d3.nest()
.key(function(d) {
return d.name;
})
.entries(data);
var legendSpace = width / dataNest.length;
// Loop through each symbol / key
dataNest.forEach(function(d, i) {
// Add the Legend
svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // space legend
.attr("y", height)
.attr("class", "legend") // style the legend
.style("fill", color(i))
.on("click", function() {
// Determine if current line is visible
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements based on the ID
d3.select("#" + d.key.replace(/\s+/g, '') + "-line")
.transition().duration(100)
.style("opacity", newOpacity);
d3.select("#" + d.key.replace(/\s+/g, '') + "-circle")
.transition().duration(100)
.style("opacity", newOpacity);
// Update whether or not the elements are active
d.active = active;
})
.text(d.key);
});

Set custom tick value on d3v4 bat charts from JSON data

I am trying to get values from a JSON File and use them as the X and Y axis on a d3 v4 bar chart. But my axis is not showing up as I like. WHat am I doing wrong?
My HTML Code:
<div class="col" id="main-chart" style="padding-top:75px;">
<svg width="675" height="415"></svg>
</div>
My D3 Code:
var svg = d3.select("#main-chart svg"),
margin = {top: 20, right: 20, bottom: 30, left: 75},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var colours = d3.scaleOrdinal().range(["#6F257F", "#CA0D59"]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json(data.json, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.values; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
// .attr("transform", "rotate(-90deg)")
.call(d3.axisBottom(x).ticks(5));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(5).tickFormat(function(d) {return parseInt(d); }).tickSizeInner([-width]))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.attr("fill", "#5D6971");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) {return x(d.date); })
.attr("y", function(d) {return y(d.values); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.values); })
.attr("fill", function(d) { return colours(d.date); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.date) + "<br>" + (d.values));
})
.on("mouseout", function(d){ tooltip.style("display", "none");});
});
My JSON CODE:
[
{
"date": "2018-10-19",
"values": 6574406
},
{
"date": "2018-10-20",
"values": 6575406
},
{
"date": "2018-10-21",
"values": 6575696
},
{
"date": "2018-10-22",
"values": 6576656
},
{
"date": "2018-10-23",
"values": 6577222
},
{
"date": "2018-10-24",
"values": 6578908
},
{
"date": "2018-10-25",
"values": 6579386
},
{
"date": "2018-10-26",
"values": 6580020
},
{
"date": "2018-10-27",
"values": 6580214
},
{
"date": "2018-10-28",
"values": 6580440
},
{
"date": "2018-10-29",
"values": 6581334
},
{
"date": "2018-10-30",
"values": 6583556
},
{
"date": "2018-10-31",
"values": 6584098
},
{
"date": "2018-11-01",
"values": 6584660
}
]
My chart looks smushed and I want my Y-Axis to start with the lowest value. Which in this case is 6574406 and want to increment by 25 with 10 ticks. I have tried many variations but am not able to get the x-axis un-overlapped. I tried to skew the values but that did not work either.
My chart:

Donut bubble chart in D3.js version 3

I need to plot bubble chart, where each bubble is a donut chart like in below image in d3 version 3. I am able to achieve something, but don't understand how to distribute the circles horizontally, as my widget will be rectangular.
Also, how to make the donut bubble like in the image below. Any help would be appreciated. Thanks.
Code:
let colorCircles = {
'a': '#59bcf9',
'b': '#faabab',
'd': '#ffde85'
};
let tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip-inner")
.style("position", "absolute")
.style("min-width", "12rem")
.style("visibility", "hidden")
.style("color", "#627386")
.style("padding", "15px")
.style("stroke", '#b8bfca')
.style("fill", "none")
.style("stroke-width", 1)
.style("background-color", "#fff")
.style("border-radius", "6px")
.style("text-align", "center")
.text("");
let bubble = d3.layout.pack()
.sort(null)
.size([width, diameter])
.padding(15)
.value(function(d) {
return d[columnForRadius];
});
let svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", diameter)
.attr("class", "bubble");
let nodes = bubble.nodes({
children: dataset
}).filter(function(d) {
return !d.children;
});
let circles = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r;
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y - 20;
})
.style("fill", function(d) {
return colorCircles[d[columnForColors]]
})
.on("mouseover", function(d) {
tooltip.style("visibility", "visible");
tooltip.html('<p>' + d[columnForColors] + ": " + d[columnForText] + "</p><div class='font-bold displayInlineBlock'> $" + d[columnForRadius] + '</div>');
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.offsetY - 10) + "px").style("left", (d3.event.offsetX + 10) + "px");
})
// .on("mouseout", function() {
// return tooltip.style("visibility", "hidden");
// })
.attr("class", "node");
circles.transition()
.duration(1000)
.attr("r", function(d) {
return d.r;
})
.each('end', function() {
display_text();
});
function display_text() {
let text = svg
.selectAll(".text")
.data(nodes, function(d) {
return d[columnForText];
});
text.enter().append("text")
.attr("class", "graphText")
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y - 20;
})
.attr("dy", ".2em")
.attr("fill", "white")
.attr("font-size", function(d) {
return d.r / 5;
})
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d)
return d[columnForText].substring(0, d.r / 3);
});
text.enter().append("text")
.attr("class", "graphText")
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y - 20;
})
.attr("dy", "1.3em")
.style("text-anchor", "middle")
.text(function(d) {
return '$' + d[columnForRadius];
})
.attr("font-size", function(d) {
return d.r / 5;
})
.attr("fill", "white");
}
function hide_text() {
let text = svg.selectAll(".text").remove();
}
d3.select(self.frameElement)
.style("height", diameter + "px");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script type="text/javascript">
var dataset = [
{ "Name": "Olives", "Count": 4319, "Category": "d" },
{ "Name": "Tea", "Count": 4159, "Category": "d" },
{ "Name": "Boiled Potatoes", "Count": 2074, "Category": "a" },
{ "Name": "Milk", "Count": 1894, "Category": "a" },
{ "Name": "Chicken Salad", "Count": 1809, "Category": "a" },
{ "Name": "Lettuce Salad", "Count": 1566, "Category": "a" },
{ "Name": "Lobster Salad", "Count": 1511, "Category": "a" },
{ "Name": "Chocolate", "Count": 1489, "Category": "b" }
];
var width = 300, diameter = 300;
var columnForText = 'Name',
columnForColors = 'Category',
columnForRadius = "Count";
</script>
Here's my fiddle: http://jsfiddle.net/71s86zL7/
I created a compound bubble pie chart and specified the inner radius in the pie chart.
var arc = d3.svg.arc()
.innerRadius(radius)
.outerRadius(radius);
.attr("d", function(d) {
arc.innerRadius(d.r+5);
arc.outerRadius(d.r);
return arc(d);
})
please let me know if there's any alternative solution to this problem.
I have a sorta hacky solution for this. What I did was:
to use the d3.layout.pie to get the startAngles and endAngles for arcs and create the arcs on top of the circles.
Give the circles a stroke line creating an effect of a donut chart.
And then I just had to adjust the startAngles and the endAngles so that all the arcs start from the same position.
Here's the fiddle:
let colorCircles = {
'a': '#59bcf9',
'b': '#faabab',
'd': '#ffde85'
};
let tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip-inner")
.style("position", "absolute")
.style("min-width", "12rem")
.style("visibility", "hidden")
.style("color", "#627386")
.style("padding", "15px")
.style("stroke", '#b8bfca')
.style("fill", "none")
.style("stroke-width", 1)
.style("background-color", "#fff")
.style("border-radius", "6px")
.style("text-align", "center")
.text("");
let bubble = d3.layout.pack()
.sort(null)
.size([width, diameter])
.padding(15)
.value(function(d) {
return d[columnForRadius];
});
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.Count;
});
var arc = d3.svg.arc()
let svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", diameter)
.attr("class", "bubble");
let nodes = bubble.nodes({
children: dataset
}).filter(function(d) {
return !d.children;
});
let g = svg.append('g')
let circles = g.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r;
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y - 20;
})
.style("fill", function(d) {
return colorCircles[d[columnForColors]]
})
.attr("class", "node")
.on("mouseover", function(d) {
tooltip.style("visibility", "visible");
tooltip.html('<p>' + d[columnForColors] + ": " + d[columnForText] + "</p><div class='font-bold displayInlineBlock'> $" + d[columnForRadius] + '</div>');
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.offsetY - 10) + "px").style("left", (d3.event.offsetX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
arcs = g.selectAll(".arc")
.data(pie(dataset))
.enter().append("g")
.attr("class", "arc");
arcs.append("path")
.attr('transform', function(d) {
return 'translate(' + d['data']['x'] + ',' + (d['data']['y'] - 20) + ')';
})
.attr("d", function(d) {
return arc({
startAngle: 0,
endAngle: d.startAngle - d.endAngle,
innerRadius: d['data']['r'] - 2,
outerRadius: d['data']['r'] + 2,
})
}).on("mouseover", function(d) {
tooltip.style("visibility", "visible");
tooltip.html('<p>' + d['data'][columnForColors] + ": " + d['data'][columnForText] + "</p><div class='font-bold displayInlineBlock'> $" + d['data'][columnForRadius] + '</div>');
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.offsetY - 10) + "px").style("left", (d3.event.offsetX + 10) + "px");
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
circles.transition()
.duration(1000)
.attr("r", function(d) {
return d.r;
})
.each('end', function() {
display_text();
});
function display_text() {
let text = svg
.selectAll(".text")
.data(nodes, function(d) {
return d[columnForText];
});
text.enter().append("text")
.attr("class", "graphText")
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y - 20;
})
.attr("dy", ".2em")
.attr("fill", "white")
.attr("font-size", function(d) {
return d.r / 3;
})
.attr("text-anchor", "middle")
.text(function(d) {
return d[columnForText].substring(0, d.r / 3);
});
text.enter().append("text")
.attr("class", "graphText")
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y - 20;
})
.attr("dy", "1.3em")
.style("text-anchor", "middle")
.text(function(d) {
return '$' + d[columnForRadius];
})
.attr("font-size", function(d) {
return d.r / 5;
})
.attr("fill", "white");
}
function hide_text() {
let text = svg.selectAll(".text").remove();
}
d3.select(self.frameElement)
.style("height", diameter + "px");
path {
fill: orange;
stroke-width: 1px;
stroke: crimson;
}
path:hover {
fill: yellow;
}
circle {
fill: white;
stroke: slategray;
stroke-width: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<script type="text/javascript">
var dataset = [{
"Name": "Olives",
"Count": 4319,
"Category": "d"
},
{
"Name": "Tea",
"Count": 4159,
"Category": "d"
},
{
"Name": "Boiled Potatoes",
"Count": 2074,
"Category": "a"
},
{
"Name": "Milk",
"Count": 1894,
"Category": "a"
},
{
"Name": "Chicken Salad",
"Count": 1809,
"Category": "a"
},
{
"Name": "Lettuce Salad",
"Count": 1566,
"Category": "a"
},
{
"Name": "Lobster Salad",
"Count": 1511,
"Category": "a"
},
{
"Name": "Chocolate",
"Count": 1489,
"Category": "b"
}
];
var width = 300,
diameter = 300;
var columnForText = 'Name',
columnForColors = 'Category',
columnForRadius = "Count";
</script>

D3 V4 multi-line chart with scatterplot in angular-cli

I am using D3 charting library to create charts with Angular-cli. D3 version is 4.2.2. Following is what I am trying to create multi-line chart.
import {Directive, ElementRef} from '#angular/core';
import * as D3 from 'd3';
#Directive({
selector: 'bar-graph'
})
export class BarGraphDirective {
private htmlElement:HTMLElement;
constructor(elementRef:ElementRef) {
this.htmlElement = elementRef.nativeElement; // reference to <bar-graph> element from the main template
console.log(this.htmlElement);
console.log(D3);
let d3:any = D3;
var data = [{
"date": "2016-10-01",
"sales": 110,
"searches": 67
}, {
"date": "2016-10-02",
"sales": 120,
"searches": 67
}, {
"date": "2016-10-03",
"sales": 125,
"searches": 69.4
}, {
"date": "2016-10-04",
"sales": 100,
"searches": 67
},{
"date": "2016-10-05",
"sales": 99,
"searches": 66
},{
"date": "2016-10-06",
"sales": 131,
"searches": 67
},{
"date": "2016-10-07",
"sales": 111,
"searches": 47
},{
"date": "2016-10-08",
"sales": 110,
"searches": 67
},{
"date": "2016-10-09",
"sales": 130,
"searches": 67
},{
"date": "2016-10-10",
"sales": 110,
"searches": 67
},{
"date": "2016-10-11",
"sales": 110,
"searches": 67
}];
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var line = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.sales);
});
var svg = d3.select(this.htmlElement).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// format the data
data.forEach(function (d) {
d.date = parseDate(d.date);
});
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.sales > d.searches ? d.sales : d.searches;
})]);
// Add the line path.
svg.append("path")
.attr("class", "line")
.style("fill", "none")
.attr("d", line(data))
.style("stroke", "orange");
// change line to look at searches
line.y(function (d) {
return y(d.searches);
});
// Add the second line path.
svg.append("path")
.attr("class", "line")
.style("fill", "none")
.attr("d", line(data))
.style("stroke", "steelblue");
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.sales); });
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
}
}
Then my chart looks as below.
How to add scatterplots to both lines and how to change color of scatterplots as same as of the line ?
Any suggestions are highly appreciated.
Thank You
// Add sales to the scatterplot
svg.selectAll(".sales-circle")
.data(data)
.enter().append("circle")
.attr('class', 'sales-circle')
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.sales); })
.style("fill", "orange");
// Add searches to the scatterplot
svg.selectAll(".searches-circle")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr('class', 'searches-circle')
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d. searches); })
.style("fill", "steelblue");

Categories