How to update data in d3 chart with an onchange slider event? - javascript

Attached is my minimum working example in JS Fiddle. I am able to only show the initial data, but when I change the slider, the data does not update. Where am I going wrong in my code here? Apologies for the poor structure - I am still a beginner in D3.
https://jsfiddle.net/pv02z8em/1/
chartGroup
.selectAll('.line-series')
.data(data, d=> d.x)
.join(
enter => {
enter.append('path')
.attr('class', d => `line-series x_${d.x}`)
.attr("d", drawLine(data))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
},
update => { update.transition().duration(500) },
exit => { exit.remove() }
)
}

You have several issues in that code:
You are not selecting the slider by its ID. It should be d3.select('#slider-x-range');
In the listener, you're not calling buildLine(data);
Remove everything inside buildLine that isn't related to the path itself, otherwise you'll create different SVGs every time the user moves the slider;
Your join structure is currently appending several paths, one above the other. This is certainly not what you want. It could be just:
let path = chartGroup.selectAll('.line-series')
.data([data]);
path = path.enter()
.append('path')
.attr('class', "line-series")
.attr("d", d => drawLine(d))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
.merge(path)
path.transition().duration(500).attr("d", d => drawLine(d))
Here is your code with these and other minor changes:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/skeleton.css">
<link rel="stylesheet" href="css/skeleton_override.css">
<style>
svg {
display: inline-block;
position: relative;
vertical-align: top;
overflow: hidden;
}
.x-axis,
.y-axis {
font: 16px sans-serif;
}
.axis-label {
font: 18px sans-serif;
}
.chart-title {
font: 24px sans-serif;
}
.x-axis .tick:first-of-type text {
fill: none;
}
.body {
display: flex;
}
.chart-group-container {
margin: 10px 20px 20px 20px;
}
.controls-container {
display: flex;
flex-direction: column;
}
.controls-header {
color: black;
padding: 0.5rem 2rem;
text-align: left;
}
.controls-body {
overflow: auto;
font-size: 0.8em;
cursor: default;
}
.slidecontainer {
text-align: left;
margin: 10px;
font-family: sans-serif;
font-size: 14px;
}
#slider-x-range {
vertical-align: bottom;
}
</style>
<title>Document</title>
</head>
<body>
<div class="chart-group-container">
<div class="row">
<div class="six columns">
<div class="chart-container">
<div class="viz">
</div>
</div>
</div>
<div class="six columns"></div>
<div class="controls-container">
<div class="controls-header">UI Controls</div>
<div class="controls-body">
<div class="slider-label">Adjust x axis</div>
<div class="slidecontainer">
<span>10</span>
<input type="range" min="10" max="100" value="1" id="slider-x-range">
<span>100</span>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
//let sinWave = Math.sin(x)
let range = function(start, stop, step) {
step = step || 1;
let arr = []
for (let i = start; i < stop; i += step) {
arr.push(i);
}
return arr;
}
let generateSinWave = function(x) {
let y = []
x.forEach(function(i) {
y.push(Math.sin(i))
});
return y;
}
const generateData = (n) => {
x = range(0, n, 1)
y = generateSinWave(x)
let labels = ['x', 'y']
let data = []
for (let i = 0; i < x.length; i++) {
data.push({
x: x[i],
y: y[i]
})
}
return data;
}
</script>
<script>
let margin = {
top: 50,
right: 30,
bottom: 30,
left: 100
},
width = 800 - margin.left - margin.right
height = 400 - margin.top - margin.bottom;
let xScale = d3.scaleLinear()
.range([0, width])
let yScale = d3.scaleLinear()
.range([height, 0])
.nice()
let drawLine = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y))
.curve(d3.curveBasis);
let svg = d3.select('.viz')
.append('svg')
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr("preserveAspectRatio", "xMinYMin meet")
//.attr('width', `${width + margin.left + margin.right}px`)
//.attr('height', `${height + margin.top + margin.bottom}px`)
//.classed("svg-content", true);
.append('g')
.attr('class', 'line-chart-container')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
/* d3.select(".line-chart-container")
.attr("style", "outline: thin solid black;")
.attr("margin-right", "102px") */
const chartGroup = svg.append('g').attr('class', 'line-chart')
// Draw x axis
const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
const xAxisDraw = svg
.append('g')
.attr('class', 'x-axis')
//.style('font', '14px sans-serif')
.attr('transform', `translate(0, ${height / 2})`)
.call(xAxis);
const yAxis = d3
.axisLeft(yScale)
.ticks(10)
//.tickSizeInner(-width);
const yAxisDraw = svg
.append('g')
.attr('class', 'y-axis')
.call(yAxis);
// x axis label
svg.append('text')
.attr('class', 'axis-label')
.attr('text-anchor', 'end')
.text('X axis')
.attr('x', width)
.attr('y', height - margin.bottom + 50)
// y axis label
svg.append('text')
.attr('class', 'axis-label')
.attr('text-anchor', 'end')
.attr('transform', 'rotate(-90)')
.attr('x', margin.top + 50 - (height / 2))
.attr('y', margin.left - 160)
.text('Y axis')
// Draw Header
const header = svg
.append('g')
.attr('class', 'chart-title')
.attr('transform', `translate(${width / 2 - 75}, ${margin.top - 75})`)
.append('text')
header.append('tspan').text('Sine wave')
function buildLine(data) {
xScale.domain([d3.min(data, d => d.x), d3.max(data, d => d.x)])
yScale.domain([d3.min(data, d => d.y), d3.max(data, d => d.y)])
let path = chartGroup
.selectAll('.line-series')
.data([data]);
path = path.enter().append('path')
.attr('class', "line-series")
.attr("d", d => drawLine(d))
.style('stroke', 'dodgerblue')
.style('stroke-width', 2)
.style('fill', 'none')
.style('opacity', 1)
.merge(path)
path.transition().duration(500).attr("d", d => drawLine(d))
}
let xRangeSlider = document.getElementById('slider-x-range');
xRangeSlider.min = 10;
xRangeSlider.max = 100;
let data = generateData(xRangeSlider.value)
buildLine(data)
d3.select('#slider-x-range')
.on("change", d => {
data = generateData(xRangeSlider.value)
buildLine(data)
});
</script>
</body>
</html>
As you'll find out, the transition is not what you probably expect: that's an unrelated issue, the interpolation of the d property string (you can find more info here).

Related

Draw scatter plot graph with d3

I have this and I need to see a graph of the genre scatter plot. I know I'm a bit dumb, but I do not know how to play it well yet, no. One of the axes still appears if executed. This code is given by the teacher to correct us, I already got some things, but I do not understand any of this.
var margin = {top: 20, right: 10, bottom: 100, left: 40};
var width = 700 - margin.right - margin.left;
var height = 500 - margin.top - margin.bottom;
// o g para agrupar objetos juntos, agrupar as barras
var svg = d3.select('body')
.append('svg')
.attr({"width" : width + margin.right + margin.left,
"height" : height + margin.top + margin.bottom
});
//.append("g")
//.attr("transform", "translate(" + margin.left +"," + margin.right + ")");
//definição da posição do eixo xx e yy
var x_scale = d3.scaleLinear()
.range([0, width])
.domain([0, width]);
var y_scale = d3.scaleLinear()
.range([height, 0])
.domain([height, 0]);
//axis, definição do conteudo dos eixos
var x_axis = d3.axisBottom(x_scale);
d3.select("svg")
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin) + ")")
.call(x_axis);
var y_axis = d3.axisLeft(y_scale);
d3.select("svg")
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin + ", 0)")
.call(y_axis);
var years = [];
var suicides = [];
var taxaAno = new Object();
var taxaAno = {};
d3.csv("master.csv", function(error,data){
if(error) console.log("Erro no ficheiro csv");
data.forEach(function(d){
years = +d.year;
suicides = +d.suicides_no;
if (taxaAno[years]==null){
taxaAno[years] = suicides;
}
else{
taxaAno[years] += suicides;
}
});
console.log(taxaAno);
var circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) {
return x_scale(taxaAno[years]);
})
.attr("cy", function (d) {
return y_scale(taxaAno[suicides]);
})
.attr("r", function (d) {
return 10;
});
var line = d3.line()
.x(function (){ return x_scale(taxaAno[years])})
.y(function (){ return y_scale(taxaAno[suicides])
});
d3.select("svg")
.append("path")
.attr("d", line(data))
.attr("class", "line_death");
d3.select("svg")
.append("text")
.text("Anos")
.attr("x", (width / 2) - margin)
.attr("y", height - margin / 3);
d3.select("svg")
.append("text")
.text("N. de suicidios")
.attr("x", 0)
.attr("y", 0)
.attr("transform", "rotate (90, 0, 0) translate(100, -10)");
});
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
.topnav {
overflow: hidden;
background-color: #333;
}
.topnav a {
float: left;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
}
.topnav a:hover {
background-color: #ddd;
color: black;
}
.topnav a.active {
background-color: #4CAF50;
color: white;
}
IMG.displayed {
display: block;
margin-left: auto;
margin-right: auto
}
h1 {
color: #069;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<body>
<div class="topnav">
<a class="active" href="index.html">Home</a>
Mapa
Evolucaoo por ano
<!-- About -->
</div>
<body><br><br>
<!-- <IMG class="displayed" src="home.png" alt="some text" width=600 height=400> -->
<h1 style="text-align: center">Taxa de suicidio por ano</h1>
<script src="http://d3js.org/d3.v4.js"></script>
<script>
</script>
</body>
</html>
With this code I want to see a graph of the genre scatter plot with a line joining the points.
Error in console is this:
your data is only defined inside this block:
d3.csv("master.csv", function(error,data){
if(error) console.log("Erro no ficheiro csv");
data.forEach(function(d){
years = +d.year;
suicides = +d.suicides_no;
if (taxaAno[years]==null){
taxaAno[years] = suicides;
}
else{
taxaAno[years] += suicides;
}
});
console.log(taxaAno);
});
so you need to move everything that depends on data to within that block:
d3.csv("master.csv", function(error,data){
if(error) console.log("Erro no ficheiro csv");
data.forEach(function(d){
years = +d.year;
suicides = +d.suicides_no;
if (taxaAno[years]==null){
taxaAno[years] = suicides;
}
else{
taxaAno[years] += suicides;
}
});
console.log(taxaAno);
var circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) {
return x_scale(taxaAno[years]);
})
.attr("cy", function (d) {
return y_scale(taxaAno[suicides]);
})
.attr("r", function (d) {
return 10;
});
var line = d3.line()
.x(function (){ return x_scale(taxaAno[years])})
.y(function (){ return y_scale(taxaAno[suicides])
});
d3.select("svg")
.append("path")
.attr("d", line(data))
.attr("class", "line_death");
d3.select("svg")
.append("text")
.text("Anos")
.attr("x", (width / 2) - margin)
.attr("y", height - margin / 3);
d3.select("svg")
.append("text")
.text("N. de suicidios")
.attr("x", 0)
.attr("y", 0)
.attr("transform", "rotate (90, 0, 0) translate(100, -10)");
});

Tooltip Stays Hidden In D3 Treemap

I'm building a D3 treemap diagram that requires a tooltip. The tooltip is a div element that I'm toggling on and off with the 'mouseover' and 'mouseleave' events attached to each leaf of the treemap. The developer console confirms that the element is being added to the DOM, that it has the correct text to display within the div, that 'display' is set to 'block' when hovering, 'z-index' is 10, and that the 'position' is 'absolute', however, I can't get the tooltip to actually appear as expected.
Here's a link to the Codepen.
Here's my actual code:
HTML:
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
<div>
<h1 id='title'>JSON Data Treemaps</h1>
<h2 id='description'>Top 100 Most Pledged Kickstarter Campaigns</h2>
<svg id="canvas"></svg>
<svg id="legend"></svg>
</div>
CSS:
#import url('https://fonts.googleapis.com/css?family=Noto+Sans');
body {
background-color: ghostwhite;
display: flex;
flex-direction: column;
font-size: 16px;
font-family: 'Noto Sans', sans-serif;
div#tooltip {
position: absolute;
}
div {
display: flex;
flex-direction: column;
h1 {
margin: 4.6rem auto 0;
font-size: 3.2rem;
}
h2 {
margin: 1rem auto 2rem;
font-size: 1.4rem;
}
svg {
margin: auto;
}
svg#legend {
margin: 2rem auto;
}
}
}
JS:
const margin = {
top: 30,
bottom: 100,
left: 30,
right: 30
},
width = 960 - margin.left - margin.right,
height = 570 - margin.top - margin.bottom;
const KICK_STARTER_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/kickstarter-funding-data.json';
const MOVIE_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/movie-data.json';
const VIDEO_GAME_DATA_URL = 'https://cdn.rawgit.com/freeCodeCamp/testable-projects-fcc/a80ce8f9/src/data/tree_map/video-game-sales-data.json';
// Load all data and assign to variables.
d3.queue()
.defer(d3.json, KICK_STARTER_DATA_URL)
.defer(d3.json, MOVIE_DATA_URL)
.defer(d3.json, VIDEO_GAME_DATA_URL)
.await(storeDataCategories);
let KICK_STARTER_DATA;
let MOVIE_DATA;
let VIDEO_GAME_DATA;
let currentDataSet;
function storeDataCategories(error, kickStarter, movies, videoGames) {
// console.log(...arguments);
if(error) throw error;
KICK_STARTER_DATA = kickStarter;
MOVIE_DATA = movies;
VIDEO_GAME_DATA = videoGames;
currentDataSet = kickStarter;
buildTreemap();
buildLegend();
}
var svg = d3.select('svg#canvas')
.attr('width', width)
.attr('height', height)
.style('background-color', 'ghostwhite')
.style('box-shadow', '0 6px 26px darkgray');
var fader = (color) => {
return d3.interpolateRgb(color, 'ghostwhite')(0.2);
},
test = d3.schemeCategory20.map(fader),
colorScale = d3.scaleOrdinal(d3.schemeCategory20.map(fader)),
format = d3.format(',d');
var treemap = d3.treemap()
.tile(d3.treemapResquarify)
.size([width, height]);
// Adding tooltip for info on hover
const tooltip = d3.select('svg#canvas')
.append('div')
.attr('id', 'tooltip')
.attr('width', 60 + 'px')
.attr('height', 40 + 'px')
.style('z-index', 10)
.style('display', 'none')
.style('position', 'absolute');
function buildTreemap() {
// console.log(KICK_STARTER_DATA);
// console.log(MOVIE_DATA);
// console.log(VIDEO_GAME_DATA);
// console.log(currentDataSet);
var root = d3.hierarchy(currentDataSet)
.sum(sumValue)
.sort((a, b) => b.height - a.height || b.value - a.value);
treemap(root);
var cell = svg.selectAll('g')
.data(root.leaves())
.enter().append('g')
.attr('transform', d => 'translate(' + [d.x0, d.y0] + ')')
.on('mouseover', d => {
// console.log('mouseover - d:\n', d);
const tooltipText = formatTooltip(d);
tooltip.transition().duration(200)
.style('position', 'absolute')
.style('display', 'block');
tooltip.html(tooltipText)
.attr('data-value', d.value)
.style('top', (d3.event.pageY - 50) + 'px')
.style('left', (d3.event.pageX + 20) + 'px');
})
.on('mouseout', d => {
// console.log('mouseout!');
tooltip.transition().duration(500)
.style('display', 'none');
});
cell.append('rect')
.attr('id', d => d.data.id)
.attr('class', 'tile')
.attr('width', d => d.x1 - d.x0)
.attr('height', d => d.y1 - d.y0)
.attr('fill', d => colorScale(d.parent.data.name))
.attr('data-name', d => d.data.name)
.attr('data-category', d => d.parent.data.name)
.attr('data-value', d => d.data.value);
cell.append('clipPath')
.attr('id', d => `clip-${d.data.name}`)
.append('use')
.attr('xlink:href', d => `#${d.data.name}`);
cell.append('text')
.attr('clip-path', d => `url(#clip-${d.data.name})`)
.selectAll('tspan')
.data(d => d.data.name.split(/(?=[A-Z][^A-Z])/g))
.enter().append('tspan')
.attr('x', 4)
.attr('y', (d, i) => 13 + i * 10)
.text(d => d)
.style('font-size', '10');
}
function buildLegend() {
console.log(`inside 'buildLegend'`);
// console.log(currentDataSet);
const rectWidth = width / 8,
rectHeight = 40;
const legend = d3.select('svg#legend')
.attr('width', width)
.attr('height', rectHeight);
legend.append('g')
.selectAll('g')
.data(colorScale.domain())
.enter()
.append('g')
.attr('class', 'legend')
.append('rect')
.attr('class', 'legend-item')
.attr('width', '40px')
.attr('height', '40px')
.attr('transform', (d, i) => {
return 'translate(' + i * 40 + ',' + 0 + ')';
})
.style('fill', d => colorScale(d))
.style('stroke', d => colorScale(d));
}
function sumValue(d) {
return d.value;
}
function formatTooltip(d) {
const name = d.data.name,
category = d.data.category,
value = d.data.value,
tooltipText = `
<span>Name:</span> ${name} <br>
<span>Category: </span> ${category} <br>
<span>Value:</span> ${value} <br>`;;
console.log('formatted tooltip:\n', tooltipText);
return tooltipText;
}
Minor mistake: You're appending a <div> to a SVG which wouldn't work. Changing the code and appending it to the body, here's a fork:
https://codepen.io/anon/pen/KbVvwR
Code changes:
const tooltip = d3.select('body')
and a couple of minor CSS additions:
div#tooltip {
background: #FFF;
pointer-events: none; // important
padding: 4px;
border: 1px solid #CCC;
border-radius: 3px;
}
Hope this helps.

Axis alignment - TypeError: Cannot read property 'length' of null

I'm making a scatterplot graph but despite the points visually aligning with the y-axis I'm failing a test that checks this and I get:
Cannot read property 'length' of null
TypeError: Cannot read property 'length' of null
at https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:622:120652
at NodeList.forEach ()
at Y (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:622:119636)
at r. (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:622:200083)
at c (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:31654)
at o.f.run (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:31590)
at m.runTest (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:37160)
at https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:38022
at s (https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:36472)
at https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js:570:36541
The test is the following:
var e=document.querySelector("#y-axis"),t=document.querySelectorAll(".dot"),r=e.querySelectorAll(".tick");s.assert.isTrue(Y(t,r,"cy","data-yvalue","minute","center"),"y values don't line up with y locations ")
Here's my code:
document.addEventListener("DOMContentLoaded", function() {
function convertToMMSS(originalSeconds) {
let minutes = Math.floor(originalSeconds / 60);
let seconds = originalSeconds % 60;
let date = new Date();
date.setMinutes(minutes);
date.setSeconds(seconds);
let formatTime = d3.timeFormat("%M:%S");
let mmss = formatTime(date);
let parseTime = d3.timeParse("%M:%S");
return parseTime(mmss);
}
function formatDate(originalSeconds) {
let minutes = Math.floor(originalSeconds / 60);
let seconds = originalSeconds % 60;
seconds = seconds >= 10 ? seconds : "0" + seconds;
let time = minutes + ":" + seconds;
return time;
}
$.getJSON(
"https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json",
function(dataset) {
console.log(dataset);
const w = 800;
const h = 600;
const padding = 60;
const xScale = d3
.scaleLinear()
.domain([
d3.min(dataset, (d, i) => dataset[i].Year) - 1,
d3.max(dataset, (d, i) => dataset[i].Year)
])
.range([padding, w]);
const yScale = d3
.scaleTime() //linear d3.extent
.domain([
d3.min(dataset, (d, i) => dataset[i].Seconds),
d3.max(dataset, (d, i) => dataset[i].Seconds)
])
.range([h - padding, 0]);
const yAxisScale = d3
.scaleTime()
.domain([
d3.min(dataset, (d, i) => convertToMMSS(dataset[i].Seconds)),
d3.max(dataset, (d, i) => convertToMMSS(dataset[i].Seconds))////
])
.range([0, h - padding]);
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.attr("id", "tooltip");
const container = d3
.select("body")
.append("svg")
.attr("height", h + padding)
.attr("width", w + padding);
container
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("class", "dot")
.attr("data-xvalue", d => d.Year)
.attr("data-yvalue", d => convertToMMSS(d.Seconds))
.attr("cx", d => xScale(d.Year))
.attr("cy", d => h - yScale(d.Seconds))
.attr("r", 5)
.attr("fill", (d, i) => {
return dataset[i].Doping != "" ? "red" : "green";
})
.on("mouseover", (d, i) => {
tooltip
.text(d.Year + " " + formatDate(d.Seconds))
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px")
.style("opacity", 1)
.attr("data-year", () => dataset[i].Year);
})
.on("mouseout", () => {
tooltip.style("opacity", 0);
});
const xAxis = d3.axisBottom(xScale).tickFormat(d3.format("d"));
container
.append("g")
.attr("id", "x-axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
const yAxis = d3.axisLeft(yAxisScale).tickFormat(d3.timeFormat("%M:%S"));
container
.append("g")
.attr("id", "y-axis")
.attr("transform", "translate(" + padding + "," + padding + ")")
.call(yAxis);
}
);
});
body {
margin: 10vh;
display: flex;
flex-direction: column;
align-items: center;
font-family: Monospace;
}
h1 {
text-align: center;
font-size: 35px;
}
.tooltip {
position: absolute;
text-align: center;
width: 100px;
padding: 2px;
font: 12px sans-serif;
background-color: white;
border: 1px solid black;
border-radius: 2px;
pointer-events: none;
color: black;
opacity: 0;
}
#legend {
position: absolute;
z-index: 9999;
margin-top: 30%;
margin-left: 18%;
}
.bar {
display: flex;
flex-direction: row;
align-items: center;
}
.green-box {
background-color: green;
height: 20px;
width: 20px;
}
.red-box {
background-color: red;
height: 20px;
width: 20px;
}
h4 {
margin: 1em;
font-size: 15px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-scale/1.0.7/d3-scale.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="title">Doping in Professional Bicycle Racing</h1>
<div id="legend">
<div class="bar">
<div class="green-box"></div>
<h4>No doping allegations</h4>
</div>
<div class="bar">
<div class="red-box"></div>
<h4>Riders with doping allegations</h4>
</div>
</div>
And here's my Pen
Any suggestions would be appreciated!

Fixed position D3 tip rather than at mouse/s position

I am finishing off a simple scatterplot graph project using D3. I am using D3 tip to show more information about the subject on the graph upon mouseover.
Currently, the tip shows up position to wherever the subject is on the graph.
However, I want to position my tip in a fixed position in the bottom right corner of the graph whenever a tip is triggered.
I have tried to amend the d3-tip class in CSS but no matter what I try, it seems to get overridden, and the tip's top and left positions are always wherever the mouse happens to be.
Here is a Codepen of my project, where you will see the tip appear wherever the subject is positioned: http://codepen.io/alanbuchanan/pen/RryBVQ
How can I ensure my tip shows up in a fixed position at the bottom right of the graph?
Javascript:
var w = 600;
var h = 700;
var margin = {top: 70, bottom: 90, left: 110, right: 70};
var nameMargin = 2;
var chartWidth = w - margin.left - margin.right;
var chartHeight = h - margin.top - margin.bottom;
var url = 'https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json';
var svg = d3.select('body')
.append('svg')
.attr('width', w + margin.right)
.attr('height', h)
.style('background-color', '#eeeeee')
.style('border-radius', '10px')
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
var formatMinutes = d => {
d -= 2210
var mins = Math.floor(d / 60)
var secs = d - mins * 60
return mins + ':' + secs
};
// Scales
var x = d3.scale.linear()
.domain([
2390 + 5,
2210
])
.range([0, chartWidth]);
var y = d3.scale.linear()
.domain([1, 35 + 1])
.range([0, chartHeight]);
// Axes
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom')
.ticks(7)
.tickFormat(formatMinutes);
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(10);
var getFlag = nat => {
switch (nat) {
// Deal with odd cases
case 'GER': nat = "DE"; break;
case 'SUI': nat = "CH"; break;
case 'UKR': nat = "UA"; break;
case 'POR': nat = "PT"; break;
default: break;
}
return nat.substr(0, 2).toLowerCase();
};
$.getJSON(url, data => {
console.log(data)
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(d => {
return `<h4><span class="flag-icon flag-icon-` + getFlag(d.Nationality) + `"></span> ${d.Name}</h4>
<h5>Time: ${d.Time} in ${d.Year}</h5>
<p>${d.Doping || 'No allegations'}</p>`
});
// Add data from JSON
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', d => x(d.Seconds))
.attr('cy', d => y(d.Place))
.attr('r', '2')
.style('width', '4px')
.style('height', '10px');
svg.call(tip);
var createLink = d => {
return `window.location='${d.URL}`
};
var text = svg.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('x', d => x(d.Seconds - nameMargin))
.attr('y', d => y(d.Place))
.attr("dy", ".35em")
.text(d => d.Name)
.attr('class', 'rider-name')
.attr('class', d => d.Doping.length === 0 ? 'noAllegations' : 'hasAllegations')
.on('click', (d) => location.href = d.URL)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${chartHeight})`)
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
// Axis labels
svg.append('text')
.attr('class', 'label')
.attr('text-anchor', 'middle')
.attr('x', chartWidth / 2)
.attr('y', chartHeight + 50)
.text('Mins and secs behind best time');
svg.append('text')
.attr('class', 'label')
.attr('text-anchor', 'middle')
.attr('y', 0 - margin.left)
.attr('x', 0 - (chartHeight / 2))
.attr('dy', '3.9em')
.attr('transform', 'rotate(-90)')
.text('Place');
// Title
var title = svg.append('text')
.attr('class', 'title')
.attr('x', chartWidth / 2)
.attr('y', 0 - (margin.top / 2))
.attr('text-anchor', 'middle')
.text('Doping in Professional Bicycle Racing: 35 Fastest times up Alpe d\'Huez')
.style('font-size', '16px')
.style('font-weight', 'bold');
});
CSS:
#import url(https://fonts.googleapis.com/css?family=Raleway:400,100);
$font: 'Raleway', sans-serif;
$font-light: 100;
$font-med: 400;
$green: #416600;
$red: #b40000;
body {
background-color: #DADADA;
position: relative;
font-family: $font;
}
.axis path,
.axis line {
fill: none;
stroke: gray;
shape-rendering: crispEdges;
}
.d3-tip {
width: 200px;
}
svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, 10%);
border: 1px solid $red;
box-shadow: 3px 3px 15px #888888;
}
h4, h5 {
margin: 0;
}
text {
cursor: pointer;
}
.noAllegations {
fill: $green;
}
.hasAllegations {
fill: $red;
}
.title {
cursor: initial;
fill: $red;
}
.noAllegations, .hasAllegations {
text-transform: uppercase;
font-weight: $font-med;
font-size: 12px;
}
circle {
fill: black;
}
.label {
cursor: initial;
}

Enable scroll on the axis of D3 chart

I have a simple stacked bar chart :
The code is here.
I would like to have scroll-bar on the axis but as you can see in the link the scroll appears for the div container with the help of CSS.
But i need something like this chart with scroll!
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="https://ajax.goquery.min.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: #BDBDBD;
}
.axis text {
font-family: 'Open Sans regular', 'Open Sans';
font-size: 13px;
}
.y.axis{
direction: ltr;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.rect {
stroke: lightgrey;
fill-opacity: 0.6;
}
.wrapperDiv {
Width: 984px;
height: 35px;
border: thin solid black;
#margin-top: 36px;
#margin-bottom: 48px;
#margin-right: 20px;
#margin-left: 0px;
}
.divChart {
float:left;
font-size:13px;
color : #424242;
font-family: 'Open Sans regular', 'Open Sans';
#border: thin solid white;
margin-top: -0px;
#margin-bottom: 48px;
#margin-right: 150px;
margin-left: 50px;
#background-color: lightgrey;
width: 984px;
height: 500px;
#padding: 25px;
border: thin solid navy;
#margin: 25px;
#max-height:500px;
overflow-y:scroll;
direction: rtl;
}
</style>
</head>
<body>
<div class="divChart" id="wrapper-chart">
<div id ="chartID" ></div>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script><script>
<script>
var dataset = [{"key":"Completion","values":[{"name":"Module 1","value":0},{"name":"Module 2","value":0},{"name":"Module 3","value":0},{"name":"Module 4","value":0},{"name":"Module 5","value":0},{"name":"Module 6","value":0},{"name":"Module 7","value":0},{"name":"Module 8","value":0.56},{"name":"Module 9","value":13.24},{"name":"Module 10","value":12.66}]},{"key":"NonCompletion","values":[{"name":"Module 1","value":100},{"name":"Module 2","value":100},{"name":"Module 3","value":100},{"name":"Module 4","value":100},{"name":"Module 5","value":100},{"name":"Module 6","value":100},{"name":"Module 7","value":100},{"name":"Module 8","value":99.44},{"name":"Module 9","value":86.76},{"name":"Module 10","value":87.34}]}];
function intChart(chartID, dataset) {
var margins = {top: 20, right: 20, bottom: 30, left: 120};
var width = 880 - margins.left -margins.right;
var height = 5250- margins.top - margins.bottom;
var old_width = width,old_height= height;
var module_fixed = 80;
height = Math.floor((dataset[0].values.length * height)/module_fixed)
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1,.1)
var y = d3.scale.linear().rangeRound([height, 0], .1);
var series = dataset.map(function(d) {
return d.key;
});
dataset = dataset.map(function(d) {
return d.values.map(function(o, i) {
// Structure it so that your numeric
// axis (the stacked amount) is y
return {
y: o.value,
x: o.name
};
});
});
var stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function(
group) {
return group.map(function(d) {
// Invert the x and y values, and y0 becomes x0
return {
x: d.y,
y: d.x,
x0: d.y0
};
});
});
var xMax = d3.max(dataset, function(
group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
});
var xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]);
var moduleName = dataset[0]
.map(function(d) {
return d.y;
});
var yScale = d3.scale.ordinal()
.domain(moduleName)
.rangeRoundBands([height,0]);
var svg = d3.select('#chartID')
.append('svg')
.attr("width", width + margins.left +
margins.right)
.attr("height", height + margins.top +
margins.bottom)
.append('g')
.attr('transform', 'translate(60,' + margins.top +
')');
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(2)
.tickSize(0)
.tickPadding(20)
.tickFormat(function(d) {
return d + "%";
});
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(0);
var colours = d3.scale.ordinal().range(
["#8bc34a", "#ff8a65"]);
var groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g').attr('class', 'stacked')
.style('fill', function(d, i) {
return colours(i);
});
var rects = groups.selectAll(
'stackedBar')
.data(function(d, i) {
return d;
})
.enter()
.append('rect')
.attr('class', 'stackedBar')
.attr('x', function(d) {
return xScale(d.x0);
})
.attr('y', function(d, i) {
return yScale(d.y);
})
.attr('height', 48)
.attr('width', 0)
rects.transition()
.delay(function(d, i) {
return i * 50;
})
.attr("x", function(d) {
return xScale(d.x0);
})
.attr("width", function(d) {
return xScale(d.x);
})
.duration(3000);
//Added
x.domain(dataset.map(function(d) {
return d.value;
}));
y.domain([0, d3.max(dataset, function(
d) {
return d.name;
})]);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' +height + ')')
.call(xAxis)
.append("text")
.attr("transform", "rotate(360)")
.attr("y",10)
.attr("x", 140)
.attr("dy", ".30em")
.text("Percentage of Students");
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.5em")
.attr("dy", ".15em")
.attr("y", "-")
.attr("opacity", 1)
.attr("transform", function(d) {
return "rotate(-40)"
})
// Draw Y-axis grid lines
svg.selectAll("line.y")
.data(y.ticks(2))
.enter().append("line")
.attr("class", "y")
.attr("x1", 0)
.attr("x2", 450)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
}
$(document).ready(function(){
intChart("chartID", dataset);
});
</script>
Would appreciate any help.
Thanks in advance.
I don't think this can be done using D3 only. If you want to use CSS, you need to fix the position of the x-axis. You can add a separate DIV and SVG container for the X axis (these are not scrollable), and the rest of the chart in another.
I modified you code to do this see here. Please note that you code needs a lot of cleaning, as there are several non-functional parts that makes it really confusing.
The modifications are as follows:
HTML
Added a new DIV (xaxis)
<div id="wrapper-chart">
<div class="divChart" id="chartID"></div>
<div id="xaxis"></div>
</div>
CSS
Added styling for the new div (same as divChart but without the scrolling)
#xaxis {
float: left;
font-size: 13px;
color: #424242;
font-family: 'Open Sans regular', 'Open Sans';
width: 984px;
direction: rtl;
}
JS
A new SVG container for the x-axis. Notice the height attribute.
var xaxis_svg = d3.select('#xaxis')
.append('svg')
.attr("width", width + margins.left + margins.right)
.attr("height", margins.bottom)
.append('g')
.attr('transform', 'translate(60,0)');
Append the x-axis to the container.
xaxis_svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + 0 + ')')
.call(xAxis)
.append("text")
.attr("y", 10)
.attr("x", 140)
.attr("dy", ".30em")
.text("Percentage of Students");
Hope this helps.

Categories