用到这个插件d3-beeswarm
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MakeOver Monday - White House Salaries</title>
<meta name="description" content="MakeOver Monday, Obama's administration VS Trump's administration">
<script src="//d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script src="https://raw.githack.com/kcnarf/d3-beeswarm/master/build/d3-beeswarm.js"></script>
<style>
svg {
background-color: rgb(250,250,250);
}
a {
color: #ccc;
}
text {
fill: grey;
}
text.tiny {
font-size: 10pt;
}
text.light {
fill: lightgrey
}
.text-background {
fill: rgb(250,250,250);
stroke: rgb(250,250,250);
stroke-width: 3px;
}
line {
stroke: lightgrey;
}
#header #title {
letter-spacing: 15pt;
font-weight: 200;
}
#header .name{
font-size: 30pt;
}
#header .label{
fill: lightgrey;
font-size: 10pt;
}
#axis-container text {
font-size: 8pt;
}
.Obama {
stroke: blue;
}
.Trump {
stroke: red;
}
circle {
fill: transparent;
stroke: transparent;
}
circle.Obama {
fill: lightBlue;
}
circle.Trump {
fill: pink;
}
.slope {
stroke: grey;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 14px;
padding: 2px;
font: 12px sans-serif;
background: rgb(250,250,250);
color: grey;
border: 1px solid grey;
border-radius: 3px;
pointer-events: none;
}
</style>
</head>
<body>
<svg></svg>
<script>
// d3-beeswarm.js 这个是用来画 蜜蜂群图
// 示例 https://bl.ocks.org/Kcnarf/5c989173d0e0c74ab4b62161b33bb0a8
// api https://github.com/Kcnarf/d3-beeswarm
// 大致了解下,这个数据主要是用来比较,奥巴马阵营的人员工资 特朗普阵营的人员工资
// 这俩参数估计想控制动画的,但是他木有用
const WITH_TRANSITION = true;
const WITHOUT_TRANSITION = false;
// 动画时长
const duration = 250;
//begin: raw data global def
// obama trump 旗下的人信息的list
var obamaSalaries = [],
trumpSalaries = [];
// obama,trump 旗下的人数
var obamaStaffCount = 0,
trumpStaffCount = 0;
// obama,trump 旗下的人员的工资总和
var obamaTotalSalaries = 0,
trumpTotalSalaries = 0;
// 第一个参数没用到,obamaSalariesExtent obama 人员的工资范围 trumpSalariesExtent 人员的工资范围
var salaryExtent, obamaSalariesExtent, trumpSalariesExtent;
// obama 人员的平均工资 trump 人员的平均工资
var obamaMeanSalary, trumpMeanSalary;
// 工资的中位数
// 对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数。
var obamaMedianSalary, trumpMedianSalary;
//end: raw data global def
//begin: data-related utils
// 遍历数组 的访问函数
function salaryAccessor (d){ return d.salary;};
//end: data-related utils
//begin: layout conf.
var svgWidth = 960,
svgHeight = 500,
margin = {top: 10, right: 10, bottom: 10, left: 10},
// 左右布局,中间区域的距离
halfSpaceBetween = {header: 15, circle: 15, slope: 5},
// titleY 的y轴距离
titleY = 10,
nameY = 50,
staffY = nameY+30,
totalY = staffY+15,
// header 距离底下图标的距离
tillHeader = 15,
// 整个header的高度
headerHeight = totalY + tillHeader + 5,
// 整个footer的距离
footerHeight = 30,
// 内容区域的宽度 高度
width = svgWidth - margin.left - margin.right,
height = svgHeight - headerHeight - footerHeight - margin.top - margin.bottom,
halfWidth = width/2,
halfHeight = height/2,
quarterWidth = width/4,
quarterHeight = height/4;
//end: layout conf.
//begin: beeswarm conf.
// 小点的配置 弧度为3
var beeRadius = 3;
// https://github.com/d3/d3-scale/blob/master/README.md#scaleLinear
// y轴距离的线性比例尺
var yPosScale = d3.scaleLinear();
// https://github.com/d3/d3-scale/blob/master/README.md#scalePow
// 定义一个权重比例尺 根据工资的多少,产生不同的圆,增长减缓
var radiusScale = d3.scalePow().exponent(0.5);
//
var salariesArrangement, obamaArrangement, trumpArrangement;
//end: beeswarm conf.
//begin: reusable d3Selection
var svg, drawingArea, header, footer, axisContainer, avgContainer, medianContainer, circleContainer, tooltip;
//end: reusable d3Selection
// d3.csv https://github.com/d3/d3-request/blob/master/README.md#csv
d3.csv("whiteHouseSalaries.csv", csvParser, function(error, data) {
// 这个数据里,有人员的所属阵营,工资,名字,职称
if (error) throw error;
// https://github.com/d3/d3-array/blob/master/README.md#extent
// obama 阵容下所有人的最小工资和最大工资
obamaSalariesExtent = d3.extent(obamaSalaries, salaryAccessor);
// trump 阵容下所有人的最小工资和最大工资
trumpSalariesExtent = d3.extent(trumpSalaries, salaryAccessor);
// 找出所有人的最大工资和最小工资
salariesExtent = [Math.min(obamaSalariesExtent[0], trumpSalariesExtent[0]),
Math.max(obamaSalariesExtent[1], trumpSalariesExtent[1])];
// 计算平均工资
obamaMeanSalary = Math.round(obamaTotalSalaries/obamaSalaries.length);
trumpMeanSalary = Math.round(trumpTotalSalaries/trumpSalaries.length);
// 计算工资的中位数
obamaMedianSalary = d3.median(obamaSalaries, salaryAccessor);
trumpMedianSalary = d3.median(trumpSalaries, salaryAccessor);
// 给y轴比例尺域,工资和y轴的高度邦定关系,返回一个方法,给工资,返回所在y轴位置
yPosScale.domain(salariesExtent).range([height,0]);
// 圆的大小给工资绑定关系 同理给工资,返回半径大小 最大3 最小0.5
radiusScale.domain(salariesExtent).range([0.5, beeRadius])
/*
console.info("Obama");
console.info(" count: "+obamaStaffCount);
console.info(" total: "+obamaTotalSalaries);
console.info(" mean : "+obamaMeanSalary);
console.info(" median : "+obamaMedianSalary);
console.info("Trump");
console.info(" count: "+trumpStaffCount);
console.info(" total: "+trumpTotalSalaries);
console.info(" mean : "+trumpMeanSalary);
console.info(" median : "+trumpMedianSalary);
*/
// 布局
initLayout();
// 画蜂群
drawBeeswarm();
// 画平均值 line
drawMeans();
// // 画中位数 line
drawMedians();
});
// 处理数据
function csvParser(d) {
d.administration = d.administration;
d.name = d.name;
d.salary = +d.salary;
d.positionTitle = d.positionTitle;
if (d.administration === "Obama") {
// obama 人员信息,总薪资,人数
obamaSalaries.push(d);
obamaTotalSalaries += d.salary;
obamaStaffCount++;
} else {
// trump 人员信息,总薪资,人数
trumpSalaries.push(d);
trumpTotalSalaries += d.salary;
trumpStaffCount++;
}
return d;
};
function initLayout() {svg = d3.select("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
// 画布为g, 移动到中间
drawingArea = svg.append("g")
// get, add or remove CSS classes
.classed("drawingArea", true)
.attr("transform", "translate("+[margin.left+halfWidth,margin.top]+")");
// 我自己加的轴线 画图区域都是以这条中轴线来决定,x的位置
drawingArea.append('line')
.attr('x', 0)
.attr('x1', 0)
.attr('y', 0)
.attr('y1', svgHeight - margin.top - margin.bottom)
.attr('stroke', 'red')
// 头部用个g包起来
header = drawingArea.append("g").attr("id", "header");
// 画头
drawHeader();
// 画底部
footer = drawingArea.append("g")
.attr("id", "footer")
.attr("transform", "translate("+[0,headerHeight+height+footerHeight]+")");
drawFooter();
var graphContainer = drawingArea.append("g")
.attr("transform", "translate("+[0,headerHeight]+")")
axisContainer = graphContainer.append("g").attr("id", "axis-container");
// 画坐标轴
drawAxis()
avgContainer = graphContainer.append("g").attr("id","average-container");
medianContainer = graphContainer.append("g").attr("id","median-container");
circleContainer = graphContainer.append("g").attr("id","circle-container");
tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
}
// 画 头部
function drawHeader() {
header.append("text")
.attr("id", "title")
.attr("transform", "translate("+[0, titleY]+")")
.attr("text-anchor", "middle")
.text("White House Salaries")
// 画名字 obama trump
drawNames();
// 画人员总数对比
drawStaffCounts();
//drawStaffCountsWithPictorialUnitChart() //not used; add confusion with regards to circles in distribution
// 画总薪水对比
drawTotals();
}
// 顶部画名字,日期,vs 总体就是 中间线,距离左边区域-halfSpaceBetween.header 右边区域 halfSpaceBetween.header
function drawNames() {
// 文字间的间距
var labelMarginX = 5;
var names = header.append("g")
.attr("transform", "translate("+[0,nameY]+")")
var obama = names.append("text")
.classed("name", true)
.attr("transform", "translate("+[-halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "end")
.text("Obama");
names.append("text")
.attr("text-anchor", "middle")
.text("vs");
var trump = names.append("text")
.classed("name", true)
.attr("transform", "translate("+[halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "start")
.text("Trump");
names.append("text")
.classed("date tiny", true)
// 获取文字的宽度,在文字后边或前边写日期
.attr("transform", "translate("+[-(halfSpaceBetween.header+obama.node().getBBox().width+labelMarginX), 0]+")")
.attr("text-anchor", "end")
.text("(2016)");
names.append("text")
.classed("date tiny", true)
.attr("transform", "translate("+[halfSpaceBetween.header + trump.node().getBBox().width+labelMarginX, 0]+")")
.attr("text-anchor", "start")
.text("(2017)");
}
// 写人数对比 跟上边写名字类似,主要把握,偏移量
function drawStaffCounts() {
var labelMarginX = 5;
var staffCounts = header.append("g")
.attr("transform", "translate("+[0,staffY]+")")
var oStaffCount = staffCounts.append("text")
.classed("staff-count", true)
.attr("transform", "translate("+[-halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "end")
.text(obamaStaffCount);
staffCounts.append("text")
.attr("text-anchor", "middle")
.text(">");
var tStaffCount = staffCounts.append("text")
.classed("staff-count", true)
.attr("transform", "translate("+[halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "start")
.text(trumpStaffCount);
staffCounts.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[-(halfSpaceBetween.header+oStaffCount.node().getBBox().width+labelMarginX), 0]+")")
.attr("text-anchor", "end")
.text("staff count ------");
staffCounts.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[halfSpaceBetween.header + tStaffCount.node().getBBox().width+labelMarginX, 0]+")")
.attr("text-anchor", "start")
.text("------ staff count");
}
// 没用
function drawStaffCountsWithPictorialUnitChart() {
var labelMarginX = 5,
countMargin = 300 + labelMarginX;
var staffs = header.append("g")
.attr("transform", "translate("+[0,staffY]+")")
var oStaff = staffs.append("g")
.attr("transform", "translate("+[-halfSpaceBetween.header, -12]+")")
oStaff.selectAll("circle")
.data(d3.range(0,obamaStaffCount))
.enter()
.append("circle")
.classed("Obama", true)
.attr("r", 0.5)
.attr("cx", (d)=>{ return -3*(d%100);})
.attr("cy", (d)=>{ return 3*Math.floor(d/100); });
var oStaffCount = staffs.append("text")
.classed("staff-count", true)
.attr("transform", "translate("+[-(halfSpaceBetween.header+countMargin), 0]+")")
.attr("text-anchor", "end")
.text(obamaStaffCount);
staffs.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[-(halfSpaceBetween.header+countMargin+oStaffCount.node().getBBox().width+labelMarginX), 0]+")")
.attr("text-anchor", "end")
.text("staff count ------");
staffs.append("text")
.attr("text-anchor", "middle")
.text(">");
var tStaff = staffs.append("g")
.attr("transform", "translate("+[halfSpaceBetween.header, -12]+")")
tStaff.selectAll("circle")
.data(d3.range(0,trumpStaffCount))
.enter()
.append("circle")
.classed("Trump", true)
.attr("r", 0.5)
.attr("cx", (d)=>{ return 3*(d%100);})
.attr("cy", (d)=>{ return 3*Math.floor(d/100); });
var tStaffCount = staffs.append("text")
.classed("staff-count", true)
.attr("transform", "translate("+[halfSpaceBetween.header+countMargin, 0]+")")
.attr("text-anchor", "start")
.text(trumpStaffCount);
staffs.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[halfSpaceBetween.header+countMargin+tStaffCount.node().getBBox().width+labelMarginX, 0]+")")
.attr("text-anchor", "start")
.text("------ staff count");
}
// 画总薪资的对比
function drawTotals() {
var labelMarginX = 5;
var totals = header.append("g")
.attr("transform", "translate("+[0,totalY]+")")
var oTotal = totals.append("text")
.classed("total", true)
.attr("transform", "translate("+[-halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "end")
.text(d3.format("$,")(obamaTotalSalaries));
totals.append("text")
.attr("text-anchor", "middle")
.text(">");
var tTotal = totals.append("text")
.classed("total", true)
.attr("transform", "translate("+[halfSpaceBetween.header, 0]+")")
.attr("text-anchor", "start")
.text(d3.format("$,")(trumpTotalSalaries));
totals.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[-(halfSpaceBetween.header+oTotal.node().getBBox().width+labelMarginX), 0]+")")
.attr("text-anchor", "end")
.text("total in 2016 ------");
totals.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[halfSpaceBetween.header + tTotal.node().getBBox().width+labelMarginX, 0]+")")
.attr("text-anchor", "start")
.text("------ total in 2017");
}
// 底部信息
function drawFooter() {
footer.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[-halfWidth, 0]+")")
.attr("text-anchor", "start")
.text("#MakeoverMonday (2017, week 29) by @_Kcnarf")
footer.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[halfWidth, 0]+")")
.attr("text-anchor", "end")
.text("bl.ocks.org/Kcnarf/4608704a70fc24e2c06ca0116830de47")
}
function drawAxis() {
// 定义y轴线条的长度 3/4 halfWidth
var lineHalfWidth = halfWidth-halfWidth/4,
labelMargin = 5;
var ticks = axisContainer.selectAll(".tick")
// https://github.com/d3/d3-array/blob/master/README.md#range
// d3.range 返回对数值较合适的分割比例
.data(d3.range(salariesExtent[0], salariesExtent[1], 20000))
.enter()
.append("g")
.classed("tick", true)
// 根据薪资 处在不同的高度
.attr("transform", (d)=>{ return "translate("+[0, yPosScale(+d)]+")"; })
// 画标线
ticks.append("line")
.attr("x1", -lineHalfWidth)
.attr("y1", 0)
.attr("x2", lineHalfWidth)
.attr("y2", 0);
// 写右侧标线的label
ticks.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[lineHalfWidth+labelMargin,3]+")")
.attr("text-anchor", "start")
.text((d)=>{ return (d===0)? 0 : d/1000+"k"; });
// 写左侧标线的label
ticks.append("text")
.classed("tiny light", true)
.attr("transform", "translate("+[-(lineHalfWidth+labelMargin),3]+")")
.attr("text-anchor", "end")
.text((d)=>{ return (d===0)? 0 : d/1000+"k"; });
// 左右两边的单位
axisContainer.append("text")
.classed("unit light", true)
.attr("transform", "translate("+[-(lineHalfWidth+labelMargin), 0]+")")
.attr("text-anchor", "end")
.text("($ per year)")
axisContainer.append("text")
.classed("unit light", true)
.attr("transform", "translate("+[(lineHalfWidth+labelMargin), 0]+")")
.attr("text-anchor", "start")
.text("($ per year)")
}
// 画小圆点
function drawBeeswarm() {
// https://bl.ocks.org/Kcnarf/5c989173d0e0c74ab4b62161b33bb0a8
var beeswarm = d3.beeswarm()
.radius(beeRadius)
.orientation("vertical") // 设置排列的方向 https://github.com/Kcnarf/d3-beeswarm#beeswarm_distributeOn horizontal
// 当开始安排数据的时候,设置数据的y值,返回的是y轴的位置
.distributeOn((d)=>{ return yPosScale(d.salary); });
// https://github.com/d3/d3-array/blob/master/README.md#shuffle 将数据随机打乱,就像洗牌一样
// .side() symetric 对称 negative 反面 positive 正面 arrange方法返回一个数组 {x: ..., y: ..., datum: ...}
// .side 设置对称,围绕主轴排列数据,将数据放置在轴的上方和下方。 “正面”只在主轴之上安排数据。 “负面”仅在主轴下面安排数据 这里所说的正面反面其实就是,正负
obamaArrangement = beeswarm.data(d3.shuffle(obamaSalaries)).side("negative").arrange();
trumpArrangement = beeswarm.data(d3.shuffle(trumpSalaries)).side("positive").arrange();
salariesArrangement = obamaArrangement.concat(trumpArrangement);
drawCircles();
};
// 画蜂群
function drawCircles() {
var circles = circleContainer.selectAll("circle")
.data(salariesArrangement)
.enter()
.append("g")
.attr("transform", (d)=>{
if (d.datum.administration==="Obama") {
// obama 所有的点,偏移 -halfSpaceBetween.circle
return "translate("+[d.x-halfSpaceBetween.circle,d.y]+")";
} else {
// trump 所有的点 偏移 halfSpaceBetween.circle
return "translate("+[d.x+halfSpaceBetween.circle,d.y]+")";
}
});
//transparent circle, for hover purpose
// 画个圆,用于hover
circles.append("circle")
.attr("r", beeRadius)
.on("mouseover", function(d) {
tooltip.transition()
.duration(0)
.style("opacity", .9);
// https://github.com/d3/d3-format/blob/master/README.md#format
// d3.format("$,")(22020) => "$22,020"
tooltip.html(""+d3.format("$,")(d.datum.salary))
.style("left", (d3.event.pageX - 30) + "px")
.style("top", (d3.event.pageY - 24) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(duration)
.style("opacity", 0);
});
//colored, sized, circle
// 画根据数据而来的圆
circles.append("circle")
.attr('r', 0)
.transition()
.duration(1000)
.delay((d, i) => i * 5)
.attr("r", (d)=>{ return radiusScale(d.datum.salary); })
.attr("class", (d)=>{ return d.datum.administration; })
}
// 画平均值
function drawMeans() {
var tickWidth = 30,
lineHalfWidth = halfWidth-halfWidth/4+tickWidth,
labelMargin = 5;
// 将 oMean 移动到 对应的y轴位置
var oMean = avgContainer.append("g")
.attr("id", "obama-average")
.classed("average", true)
.attr("transform", "translate("+[0, yPosScale(obamaMeanSalary)]+")")
// 画一条线,x从 -lineHalfWidth 到 -halfSpaceBetween.slope
oMean.append("line")
.classed("Obama", true)
.attr("x1", -lineHalfWidth)
.attr("y1", 0)
.attr("x2", -halfSpaceBetween.slope)
.attr("y2", 0);
// 文字 平均值
oMean.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[-(lineHalfWidth+labelMargin),3]+")")
.attr("text-anchor", "end")
.text(d3.format("$,")(obamaMeanSalary));
// 文字 mean 底部有个背景 这里利用样式fill stroke stroke-width 把文字模糊了,实现了跟底色一样的背景色
oMean.append("text")
.classed("tiny text-background", true)
.attr("transform", "translate("+[-lineHalfWidth+tickWidth,3]+")")
.attr("text-anchor", "start")
.text("mean");
// 在上边的有背景的文字 上写 mean
oMean.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[-lineHalfWidth+tickWidth,3]+")")
.attr("text-anchor", "start")
.text("mean");
// 画一个从噢obama平均值到trump平均值的一条line
avgContainer.append("line")
.classed("slope", true)
.attr("id", "mean-slope")
.attr("x1", -halfSpaceBetween.slope)
.attr("y1", yPosScale(obamaMeanSalary))
.attr("x2", halfSpaceBetween.slope)
.attr("y2", yPosScale(trumpMeanSalary));
// trump 画平均值,跟上边类似
var tMean = avgContainer.append("g")
.attr("id", "trump-average")
.classed("average", true)
.attr("transform", "translate("+[0, yPosScale(trumpMeanSalary)]+")")
tMean.append("line")
.classed("Trump", true)
.attr("x1", halfSpaceBetween.slope)
.attr("y1", 0)
.attr("x2", lineHalfWidth)
.attr("y2", 0);
tMean.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[lineHalfWidth+labelMargin,3]+")")
.attr("text-anchor", "start")
.text(d3.format("$,")(trumpMeanSalary));
tMean.append("text")
.classed("tiny text-background", true)
.attr("transform", "translate("+[lineHalfWidth-tickWidth,3]+")")
.attr("text-anchor", "end")
.text("mean");
tMean.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[lineHalfWidth-tickWidth,3]+")")
.attr("text-anchor", "end")
.text("mean");
}
// 画中位数的线 大致思路跟画平均值差不多
function drawMedians() {
var tickWidth = 30,
lineHalfWidth = halfWidth-halfWidth/4+tickWidth,
labelMargin = 5;
var oMedian = medianContainer.append("g")
.attr("id", "obama-median")
.classed("median", true)
.attr("transform", "translate("+[0, yPosScale(obamaMedianSalary)]+")")
oMedian.append("line")
.classed("Obama", true)
.attr("x1", -lineHalfWidth)
.attr("y1", 0)
.attr("x2", -halfSpaceBetween.slope)
.attr("y2", 0);
oMedian.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[-(lineHalfWidth+labelMargin),3]+")")
.attr("text-anchor", "end")
.text(d3.format("$,")(obamaMedianSalary));
oMedian.append("text")
.classed("tiny text-background", true)
.attr("transform", "translate("+[-lineHalfWidth+tickWidth,3]+")")
.attr("text-anchor", "start")
.text("median");
oMedian.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[-lineHalfWidth+tickWidth,3]+")")
.attr("text-anchor", "start")
.text("median");
avgContainer.append("line")
.classed("slope", true)
.attr("id", "mean-slope")
.attr("x1", -halfSpaceBetween.slope)
.attr("y1", yPosScale(obamaMedianSalary))
.attr("x2", halfSpaceBetween.slope)
.attr("y2", yPosScale(trumpMedianSalary));
var tMedian = medianContainer.append("g")
.attr("id", "trump-median")
.classed("median", true)
.attr("transform", "translate("+[0, yPosScale(trumpMedianSalary)]+")")
tMedian.append("line")
.classed("Trump", true)
.attr("x1", halfSpaceBetween.slope)
.attr("y1", 0)
.attr("x2", lineHalfWidth)
.attr("y2", 0);
tMedian.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[lineHalfWidth+labelMargin,3]+")")
.attr("text-anchor", "start")
.text(d3.format("$,")(trumpMedianSalary));
tMedian.append("text")
.classed("tiny text-background", true)
.attr("transform", "translate("+[lineHalfWidth-tickWidth,3]+")")
.attr("text-anchor", "end")
.text("median");
tMedian.append("text")
.classed("tiny", true)
.attr("transform", "translate("+[lineHalfWidth-tickWidth,3]+")")
.attr("text-anchor", "end")
.text("median");
}
</script>
</body>
</html>
Updated missing url https://raw.githack.com/Kcnarf/d3-beeswarm/master/build/d3-beeswarm.js to https://raw.githack.com/kcnarf/d3-beeswarm/master/build/d3-beeswarm.js
https://d3js.org/d3.v4.min.js
https://raw.githack.com/Kcnarf/d3-beeswarm/master/build/d3-beeswarm.js