|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<title>Graduate Incomes</title> |
|
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet"> |
|
<style> |
|
@font-face { |
|
font-family: "Metropolis", |
|
src: url("https://github.com/chrismsimpson/Metropolis/blob/master/Webfonts/WOFF/Metropolis-ExtraBold.woff?raw=true") format("woff") |
|
} |
|
|
|
html, |
|
body { |
|
height: 100%; |
|
width: 100%; |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
align-items: center; |
|
} |
|
|
|
#mount { |
|
height: 320px; |
|
width: 320px; |
|
} |
|
|
|
#mount h1 { |
|
text-align: center; |
|
font-family: "Metropolis", "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
font-size: 24px; |
|
color: #4242ea; |
|
word-wrap: break-word; |
|
line-height: 1.2em; |
|
font-weight: 700; |
|
text-rendering: optimizeLegibility; |
|
} |
|
|
|
.y-axis > g line, |
|
.y-axis path { |
|
stroke: #d1d1d1; |
|
} |
|
|
|
.y-axis > g text { |
|
fill: #d1d1d1; |
|
} |
|
|
|
.x-axis > g line, |
|
.x-axis path { |
|
display: none; |
|
} |
|
|
|
.y-axis text, |
|
.x-axis text { |
|
font-family: "Inconsolata", monospace; |
|
font-size: 12.5px; |
|
} |
|
|
|
.x-axis text { |
|
font-size: 11px; |
|
} |
|
|
|
svg text { |
|
font-family: "Inconsolata", monospace; |
|
} |
|
</style> |
|
<div id="mount"></div> |
|
<script src="//d3js.org/d3.v5.min.js" charset="utf-8"></script> |
|
<script> |
|
const data = [ { income: 18000, status: 'Pre-program' }, { income: 85000, status: 'Post-program' }]; |
|
|
|
const margin = {top: 20, right: 0, bottom: 20, left: 65 }, |
|
width = 320 - margin.right - margin.left, |
|
height = 250 - margin.top - margin.bottom; |
|
|
|
d3.select('#mount').append('h1') |
|
.text('Average Graduate Annual Income'); |
|
|
|
const graph = d3.select('#mount').append('svg') |
|
.attr('width', width + margin.right + margin.left) |
|
.attr('height', height + margin.top + margin.bottom) |
|
.append('g') |
|
.attr('transform', `translate(${margin.left}, ${margin.top})`); |
|
|
|
const x = d3.scalePoint() |
|
.domain(data.map(d => d.status)) |
|
.range([60, width - 60]); |
|
|
|
const y = d3.scaleLinear() |
|
.domain([0, 100000]) |
|
.range([height, 0]); |
|
|
|
const xAxis = d3.axisBottom(x); |
|
const yAxis = d3.axisLeft(y).ticks(10) |
|
.tickSizeInner(-width) |
|
.tickPadding(10) |
|
.tickFormat(d3.format('$,')); |
|
|
|
graph.append('g') |
|
.attr('class', 'y-axis') |
|
.call(yAxis); |
|
|
|
d3.selectAll('.y-axis > g text').attr('dy', '0em'); |
|
|
|
d3.selectAll('.y-axis > g line').attr('x1', '-5'); |
|
|
|
graph.append('g') |
|
.attr('class', 'x-axis') |
|
.attr('transform', `translate(0, ${height})`) |
|
.call(xAxis); |
|
|
|
const line = d3.line() |
|
.x(d => x(d.status)) |
|
.y(d => y(d.income)); |
|
|
|
graph.append('path') |
|
.datum(data) |
|
.attr('fill', 'none') |
|
.attr('stroke', '#4242ea') |
|
.attr('stroke-width', 2) |
|
.attr('d', line); |
|
|
|
graph.selectAll('.point') |
|
.data(data) |
|
.enter().append('circle') |
|
.attr('class', 'point') |
|
.attr('r', 6) |
|
.attr('cx', d => x(d.status)) |
|
.attr('cy', d => y(d.income)) |
|
.attr('fill', '#fff') |
|
.attr('stroke', '#4242ea') |
|
.attr('stroke-width', 2); |
|
|
|
graph.append('text') |
|
.attr('dy', '1.3em') |
|
.attr('x', d => x(data[0].status) - 30) |
|
.attr('y', d => y(data[0].income)) |
|
.attr('fill', '#000') |
|
.text(`${d3.format('$,')(data[0].income)}`); |
|
|
|
graph.append('text') |
|
.attr('dy', '-0.9em') |
|
.attr('x', d => x(data[1].status) - 30) |
|
.attr('y', d => y(data[1].income)) |
|
.attr('fill', '#000') |
|
.text(`${d3.format('$,')(data[1].income)}`); |
|
</script> |