xxxxxxxxxx
<html lang="en">
<head>
<!--font-->
<link href='https://fonts.googleapis.com/css?family=Karla|Quantico|Audiowide' rel='stylesheet' type='text/css'>
<meta charset="utf-8">
<style>
body {
/*background-color: #D2D2D2;*/
/*background-image: url();*/
}
:root {
--annotation-color: #0d2a52;
--google-font: "Karla";
--button-font: 11px;
--margin-header-bottom: 10px;
--margin-top: 10px;
}
.buttons {
float: left;
margin-left: 25%;
margin-bottom: var(--margin-header-bottom);
}
.button {
width: 80px;
margin: 0 auto;
padding: 6px 6px;
margin-right: 10px;
cursor: pointer;
text-align: center;
font-size: 11px;
font-family: var(--google-font);
border: 1px solid #e0e0e0;
float: left;
-moz-border-radius: 6px;
border-radius: 6px;
}
.button:hover {
background: #e0e0e0;
}
.button.current {
background: #000;
color: #fff;
}
.annotation path {
stroke: var(--annotation-color);
fill: none;
}
.annotation path.connector-arrow,
.title text, .annotation text {
fill: var(--annotation-color);
font-family: var(--google-font);
font-size: 15px;
}
.annotation-note-bg {
fill: rgba(0, 0, 0, 0);
}
.annotation-note-title, text.title {
font-weight: bold;
}
.chartWrapper {
width: 100%;
height: 100%;
position: absolute;
}
.chartOne {
width: 50%;
height: 80%;
float: left;
}
.chartTwo {
width: 50%;
height: 80%;
float: right;
}
.xAxis text,
.yAxis text {
font-family: var(--google-font);
}
.yAxis text {
}
.yAxis path,
.yAxis .tick line,
.xAxis .tick line {
stroke: none;
}
.bartext {
font-family: var(--google-font);
font-weight: bolder;
fill: black; /*does this do anything impt.?*/
}
.ttip {
width: 300px;
position: relative;
background-color: rgba(255, 255, 255, 0.95);
display: none;
border: 3px solid;
padding: 5px;
}
br {
display: block;
content: "";
margin-top: 7.5px;
}
.ttipText {
font-size:14px;
padding: 0;
margin: 0;
font-weight: 300;
font-family: var(--google-font);
}
h1,
h2 {
font-family: var(--google-font);
font-size: 24px;
font-style: normal;
text-transform: uppercase;
margin-bottom: var(--margin-header-bottom);
margin-top: var(--margin-top);
margin-left: 175px;
}
h3,
h4 {
font-family: var(--google-font);
font-size: 15px;
font-style: italic;
text-transform: uppercase;
margin-bottom: var(--margin-header-bottom);
margin-left: 175px;
margin-top: var(--margin-top);
padding: 0;
}
.slider-wrapper {
width: 50%;
justify-content: center;
margin-left: 25%;
margin-top: 5px;
}
.slider {
height: 23px;
margin-right: 10px;
margin-bottom: 10px;
float: left;
shape-rendering: geometricPrecision;
}
</style>
<title>Risk Bar Chart</title>
<!--js files-->
<script src="d3.v4.min.js"></script>
<script src="jquery.js"></script>
<!--<script src="d3-annotation.js"></script>-->
</head>
<body>
<div class="chartWrapper" id="wrapper">
<!--right chart-->
<div class="chartOne" id="chartOne">
<h1 id="headerOne">Statistical Risk Assessments</h1>
<h3 id="headerThree">Percentage risks of state-led mass killings</h3>
<div id="slider-wrapper" class="slider-wrapper">
<svg id="slider" class="slider"></svg>
</div>
</div>
<!--left chart-->
<div class="chartTwo" id="chartTwo">
<h2 id="headerTwo">Country:</h2>
<h4 id="headerFour">Variables used to generate risk forecast</h4>
<!--buttons-->
<div id="model" class="buttons">
<div class="button current" data-val="all">All</div>
<div class="button" data-val="badRegime">Bad Regime</div>
<div class="button" data-val="eliteThreat">Elite Threat</div>
<div class="button" data-val="randomForest" style="margin-right:0">Random Forest</div>
</div>
</div>
</div>
<script type="text/javascript">
// todo
// --- fix binary values of some variables
// --- add axis styling - https://bl.ocks.org/mbostock/3371592
// --- add annotations
// --- add values for slider
// --- fix color scale (paler)
// --- add scrollytelling feature
// (1a) minimum height and width for chart
// (1b) remove text after certain point
// (2) Resize graph correctly (for page enlargement)
// (3) Add color legend
/*------------------------------------
INITIAL SETTINGS
------------------------------------*/
// number of countries to include in graph
var numCountries = 20;
var countryStart = 0,
countryEnd = countryStart + numCountries;
// transition duration
var duration = 1000;
// preparing data: files
var dictionary_data = "cjh12sep2016.csv", // all data
badRegime = "badRegime.csv",
eliteThreat = "eliteThreat.csv",
randomForest = "randomForest.csv",
secondChartData = "secondChartData.csv"; // order and labels of second chart data
// SVG margins
var margin = {top: 0, right: 10, bottom: 30, left: 180, tooltip: 15},
width,
height;
// function to set dimensions (height and width)
function setDimensions() {
width = parseInt(d3.select(".chartOne").style("width")) - margin.left - margin.right;
height = parseInt(d3.select(".chartOne").style("height")) - margin.top - margin.bottom;
}
// setting height and width
setDimensions();
// SVG for first chart
var firstChart = d3.select("#chartOne")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "chartOne")
.attr("id", "chartOne")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// SVG for second chart
var secondChart = d3.select("#chartTwo")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "chartTwo")
.attr("id", "chartTwo")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// function to reorder data
function reorderDict(object, orderedList) {
tempDict = {};
for (var i = 0; i < orderedList.length; i++) {
test = object[orderedList[i]];
tempDict[orderedList[i]] = test;
}
return tempDict;
}
// function for processing data
function truncateDecimals (number, digits) {
var multiplier = Math.pow(10, digits),
adjustedNum = number * multiplier,
truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
return truncatedNum / multiplier;
}
// bar attributes
var bar = {padding: 0.1,
clicked: null};
var filled = "#0d2a52",
unfilled = "#E0E0E0";
// initializing scales for first chart
var first_xScale = d3.scaleLinear(),
first_yScale = d3.scaleBand()
.padding(bar.padding);
// initializing scales for second chart
var second_xScale = d3.scaleLinear(),
second_yScale = d3.scaleBand()
.padding(bar.padding);
// function to set scales' ranges
function setScales(w, h) {
first_xScale.range([0, w - margin.left]);
first_yScale.range([0, h]);
second_xScale.range([0, w - margin.left]);
second_yScale.range([0, h]);
}
// setting scales' ranges
setScales(width, height);
// axes for first chart
var first_xAxis = d3.axisBottom()
.scale(first_xScale)
.ticks(6)
.tickFormat(d3.format(".1%"));
var first_yAxis = d3.axisLeft()
.scale(first_yScale);
firstChart.append("g")
.attr("class", "xAxis")
.attr("transform", "translate(0," + height + ")");
firstChart.append("g")
.attr("class", "yAxis");
// axes for second chart
var second_xAxis = d3.axisBottom()
.scale(second_xScale)
.ticks(Math.max(width / 75, 3))
.tickFormat(function(d) { return d + "%" });
var second_yAxis = d3.axisLeft()
.scale(second_yScale);
secondChart.append("g")
.attr("class", "xAxis")
.attr("transform", "translate(0," + height + ")");
secondChart.append("g")
.attr("class", "yAxis");
// create tooltip for second chart
var ttip = d3.select("body")
.append("div")
.attr("class", "ttip");
// eight step color scale
var step = d3.scaleLinear()
.domain([0, 7])
.range([0, 1]);
var colors = d3.scaleLinear()
.domain([0, step(1), step(2), step(3), step(4), step(5), step(6), 7])
.range(["#006600", "#00b33c",
"#53ff1a", "#ffff00",
"#ff9900", "#ff6600",
"#e60000", "#ff0000"]);
var thresholds = [.1, .2, .3, .4, .5, .6, .7, .8, .9],
color_intervals = [.05, .15, .25, .35, .45, .55, .65, .75, .85, .95];
var shades = color_intervals.map(function(t) { return colors(t); });
var colorScale = d3.scaleThreshold()
.domain(thresholds)
.range(shades);
// format decimal
var formatDecimal = d3.format(",.1%");
// button selected
var selectedButton = "all",
buttonArray = null;
var badRegimeIndex = [],
eliteThreatIndex = [],
randomForestIndex = [];
// initializing variables for slider to select country range
var dragging = false,
sliderStart,
sliderEnd,
sliderPadding,
handleStart,
handleEnd,
handlePos,
sliderHandleRadius,
sliderScale = d3.scaleLinear(),
sliderTrack = d3.select(".slider")
.append("rect"),
sliderHandle = d3.select(".slider")
.append("circle")
.attr("id", "sliderHandle"),
$slider = $('.slider');
// preparing data: locations
var forecast = [], // ordered list
forecastFull = [],
varDict = {}, // dictionary
varDictKeys = [],
varDictFull = {};
var reordered = [], // for second chart data
labels = [], // for second chart labels
tooltipVar = [],
tooltipMetric = [],
tooltipDataSource = [];
var varPers,
varVals;
/*------------------------------------
INITIALIZING CHARTS WITH DATA
------------------------------------*/
d3.queue()
.defer(d3.csv, dictionary_data)
.defer(d3.csv, secondChartData)
.defer(d3.csv, badRegime)
.defer(d3.csv, eliteThreat)
.defer(d3.csv, randomForest)
.await(function(error, dictionary_data, secondChartData, badRegime, eliteThreat, randomForest) {
if (error) {
console.error(error);
}
else {
// processing data for second chart
for (var i = 0; i < secondChartData["columns"].length; i += 5) {
reordered.push(secondChartData["columns"][i]);
labels.push(secondChartData["columns"][i + 1]);
tooltipVar.push(secondChartData["columns"][i + 2]);
tooltipMetric.push(secondChartData["columns"][i + 3]);
tooltipDataSource.push(secondChartData["columns"][i + 4]);
}
// process all data for first chart
for (var i = 0; i < dictionary_data.length; i++) {
var newCountry = Object();
for (prop in dictionary_data[i]) {
// changes variable names to workable format
var newProp = prop.replace(/\./g, "_");
newCountry[newProp] = dictionary_data[i][prop];
// converts quantitative variables to numbers
if (prop !== "sftgcode" && prop !== "country") {
newCountry[newProp] = Number(newCountry[newProp]);
}
}
reorderedNewCountry = reorderDict(newCountry, reordered);
// pushing data to relevant locations
forecastFull.push(newCountry);
varDictFull[dictionary_data[i].country] = reorderedNewCountry;
varDictKeys.push(dictionary_data[i].country);
}
// modifying data for binary variables
for (mod in varDictFull[forecastFull[0].country]) {
// check if variable is relevant
if ((mod.slice(0, 6) !== "postcw")
&& (mod.slice(mod.length - 3, mod.length)) === "PER") {
// temporary variables for holding variables' data
var tempVal;
var tempList = [];
// recording variables' data
for (country in varDictFull) {
tempVal = varDictFull[country][mod];
if (tempList.indexOf(tempVal) === -1) {
tempList.push(tempVal)
}
}
// checking if variable is binary
if (tempList.length === 2) {
// changing percentile rankings of binary variables with values of 0
for (country in varDictFull) {
if (varDictFull[country][mod] === 0) {
var max = Math.max.apply(null, tempList);
varDictFull[country][mod] = (1 - max);
}
}
// changing values of binary variables to strings for tooltip
for (var i = 0; i < forecastFull.length; i ++) {
if (forecastFull[i][mod.slice(0, -3)] === 0) {
forecastFull[i][mod.slice(0, -3)] = "No";
} else if (forecastFull[i][mod.slice(0, -3)] === 1) {
forecastFull[i][mod.slice(0, -3)] = "Yes";
}
}
}
}
}
// function to subset data based on range of countries
function subsetData(startCountryIndex, endCountryIndex) {
// clear existing data
forecast = [];
varDict = {};
// subset forecasting data
forecast = forecastFull.slice(startCountryIndex, endCountryIndex);
// subset dictionary data
var countries = varDictKeys.slice(startCountryIndex, endCountryIndex);
for (var i = 0; i < countries.length; i++) {
varDict[countries[i]] = varDictFull[countries[i]];
}
}
// creating lists of indexes for each model (for buttons)
function findIndex(csvFile, list) {
for (var i = 0; i < csvFile["columns"].length; i++) {
var temp = csvFile["columns"][i].replace(/\./g, "_");
for (var n = 0; n < reordered.length; n++) {
if (reordered[n].search(temp) !== -1) {
list.push(n)
}
}
}
}
findIndex(badRegime, badRegimeIndex);
findIndex(eliteThreat, eliteThreatIndex);
findIndex(randomForest, randomForestIndex);
// create slider for country selection
// scales according to number of countries in file in argument
function createSlider(data) {
// record radius of slider handle
sliderHandleRadius = (document.getElementById('slider').getBoundingClientRect().height) / 2;
sliderPadding = sliderHandleRadius / 2;
// change width of slider svg wrapper
$slider.css('width', first_xScale(1) + (sliderHandleRadius * 2));
$slider.css({"-webkit-transform":function() {
return "translate(" + -sliderHandleRadius + "px,0)";
}});
// record start and end positions of slider
sliderStart = 0 + sliderPadding;
sliderEnd = sliderStart + document.getElementById('slider').getBoundingClientRect().width
- sliderPadding - sliderPadding;
// record start and end positions of handle
handleStart = sliderStart + sliderHandleRadius;
handleEnd = sliderEnd - sliderHandleRadius;
// adjust slider scale
sliderScale = d3.scaleLinear()
.rangeRound([0, data.length - numCountries])
.domain([handleStart, handleEnd]);
// create track for slider
sliderTrack
.attr("width", function() {
return document.getElementById('slider').getBoundingClientRect().width
- (2 * sliderHandleRadius);
})
.style("height", "25%")
.style("x", 0 + sliderHandleRadius)
.style("y", "37.5%")
.style("fill", "black");
sliderHandle
.style("cursor", "ew-resize")
.attr("cx", function() {
return handleStart;
})
.style("cy", "50%")
.style("r", sliderHandleRadius)
.attr("fill", "black")
.call(d3.drag()
.on("drag", dragged)
.on("end", function() {
dragging = false;
}));
}
function dragged() {
d3.select(this).style("cx", function() {
dragging = true;
if (d3.event.x + sliderHandleRadius >= sliderEnd) {
return sliderEnd - sliderHandleRadius;
} else if (d3.event.x - sliderHandleRadius <= sliderStart) {
return sliderStart + sliderHandleRadius;
}
return d3.event.x;
});
handlePos = document.getElementById('sliderHandle').style["cx"];
countryStart = sliderScale(handlePos);
countryEnd = sliderScale(handlePos) + numCountries;
drawFirstChart(countryStart, countryEnd);
}
createSlider(dictionary_data);
// selecting top country in graph as the current country
var current_country = forecastFull[0].country;
// change heading of second chart with new country
document.getElementById("headerTwo").innerHTML =
"<span>Selected country: <span style='text-decoration:underline;'>"
+ current_country + "</span></span>";
// function to push currently selected country's data
function pushVariableData(country) {
varPers = []; // percentage rankings of variables
varVals = []; // values of variables
// record percentile ranking of each variable
for (mod in varDictFull[country]) {
varPers.push(truncateDecimals(varDictFull[country][mod] * 100, 1));
}
// record value of each variable
for (var i = 0; i < forecastFull.length; i ++) {
if (forecastFull[i]["country"] === country) {
for (mod in varDictFull[country]) {
varVals.push(forecastFull[i][mod.slice(0, -3)]);
}
}
}
}
pushVariableData(current_country);
function drawFirstChart (countryStart, countryEnd) {
subsetData(countryStart, countryEnd);
// scale range of data in the domain of first chart
first_xScale.domain([0, d3.max(forecast, function(d) { return d.mean_p; })]);
first_yScale.domain(forecast.map(function (d) { return d.country; }));
// join data
var bar = firstChart.selectAll(".bar")
.data(forecast);
var text = firstChart.selectAll(".bartext")
.data(forecast);
// update
bar
.attr("x", 0)
.attr("y", function(d) { return first_yScale(d.country) })
.attr("height", first_yScale.bandwidth())
.attr("fill", function(d) {
if (d.country === current_country) {
return filled; // first country loaded
} else {
return unfilled;
}
})
.transition().duration(50)
.attr("width", function(d) { return first_xScale(d.mean_p); });
text
.text(function(d) { return d3.format(".1%")(d.mean_p); })
.attr("text-anchor", "middle")
.attr("font-size", function() { return first_yScale.bandwidth() / 2; })
.attr("x", function(d) {
// this works because most text has same # of chars, but not good in general
var textLength = this.getComputedTextLength();
return first_xScale(d.mean_p) + textLength;
})
.attr("y", function(d) {
return first_yScale(d.country) + (first_yScale.bandwidth() / 1.5);
});
// enter
bar
.enter()
.append("rect")
.attr("class", "bar")
.attr("fill", function(d) {
if (d.country === current_country) {
return filled; // first country loaded
} else {
return unfilled;
}
})
.attr("x", 0)
.attr("y", function(d) { return first_yScale(d.country) })
.attr("height", first_yScale.bandwidth())
.transition().duration(50)
.attr("width", function(d) { return first_xScale(d.mean_p); });
text
.enter()
.append("text")
.attr("class", "bartext")
.text(function(d) { return d3.format(".1%")(d.mean_p); })
.attr("text-anchor", "middle")
.attr("font-size", function() { return first_yScale.bandwidth() / 2; })
.attr("x", function(d) {
// this works because most text has same # of chars, but not good in general
var textLength = this.getComputedTextLength();
return first_xScale(d.mean_p) + textLength;
})
.attr("y", function(d) {
return first_yScale(d.country) + (first_yScale.bandwidth() / 1.5);
});
// exit
bar.exit().remove();
text.exit().remove();
// axes
firstChart.select(".xAxis")
.call(first_xAxis)
.attr("transform", "translate(0," + height + ")");
firstChart.select(".yAxis")
.call(first_yAxis);
}
function drawSecondChart() {
// scale range of data in the domain of second chart
second_xScale.domain([0, 100]);
second_yScale.domain(labels.map(function (d) { return d; }));
// join data
var text = secondChart.selectAll(".bartext")
.data(varPers);
var barTransparent = secondChart.selectAll(".barTransparent")
.data(varPers);
var bar = secondChart.selectAll(".bar")
.data(varPers);
// update
text
.text(function(d) { return formatDecimal(d / 100); })
.attr("text-anchor", "middle")
.attr("font-size", function() { return second_yScale.bandwidth() / 1.5; })
.attr("x", function() {
var textLength = this.getComputedTextLength();
return second_xScale(100) + (textLength / 2) + (second_yScale.bandwidth() / 2); })
.attr("y", function(d, i) {
var numBars = varPers.length;
return (i * height / numBars) + (second_yScale.bandwidth() / 1.5);
});
barTransparent
.attr("x", 0)
.attr("y", function(d, i) {
var numBars = varPers.length;
return i * height / numBars;
})
.attr("height", second_yScale.bandwidth())
.attr("width", second_xScale(100));
bar
.attr("x", 0)
.attr("y", function(d, i) {
var numBars = varPers.length;
return i * height / numBars;
})
.attr("height", second_yScale.bandwidth())
.transition().duration(duration)
.attr("fill", function(d) { return colorScale((d / 100)); })
.attr("width", function(d) { return second_xScale(d); });
// enter
text
.enter()
.append("text")
.attr("class", "bartext")
.text(function(d) { return formatDecimal(d / 100); })
.attr("text-anchor", "middle")
.attr("font-size", function() { return second_yScale.bandwidth() / 1.5; })
.attr("x", function() {
var textLength = this.getComputedTextLength();
return second_xScale(100) + (textLength / 2) + (second_yScale.bandwidth() / 2); })
.attr("y", function(d, i) {
var numBars = varPers.length;
return (i * height / numBars) + (second_yScale.bandwidth() / 1.5);
});
barTransparent
.enter()
.append("rect")
.attr("class", "barTransparent")
.attr("fill", "#a9b1bc")
.attr("opacity", 0.2)
.attr("stroke", "black")
.attr("stroke-opacity", 0)
.attr("stroke-width", 2)
.attr("x", 0)
.attr("y", function(d, i) {
var numBars = varPers.length;
return i * height / numBars;
})
.attr("height", second_yScale.bandwidth())
.attr("width", second_xScale(100));
bar
.enter()
.append("rect")
.attr("class", "bar")
.attr("id", "firstSecondBars")
.attr("fill", function(d) { return colorScale((d / 100)); })
.attr("stroke", "black")
.attr("stroke-opacity", 0)
.attr("stroke-width", 2)
.attr("x", 0)
.attr("y", function(d, i) {
var numBars = varPers.length;
return i * height / numBars;
})
.attr("height", second_yScale.bandwidth())
.transition().duration(50)
.attr("width", function(d) { return second_xScale(d); });
// exit
text.exit().remove();
barTransparent.exit().remove();
bar.exit().remove();
// axes
secondChart.select(".xAxis")
.call(second_xAxis)
.attr("transform", "translate(0," + height + ")");
secondChart.select(".yAxis")
.call(second_yAxis);
}
drawFirstChart(countryStart, countryEnd);
drawSecondChart();
// interactivity for first chart
firstChart.selectAll(".bar")
.on("click", function(d) {
// change color of bars
firstChart.selectAll(".bar")
.attr("fill", unfilled);
d3.select(this)
.attr("fill", function() { return filled });
setDimensions();
// record which bar was clicked
current_country = d.country;
bar.clicked = this;
// change heading of second chart with new country
document.getElementById("headerTwo").innerHTML =
"<span>Selected country: <span style='text-decoration:underline;'>"
+ current_country + "</span></span>";
// push data from new country
pushVariableData(current_country);
drawSecondChart();
// // change bars of second chart
// secondChart.selectAll(".bar")
// .data(varPers)
// .transition()
// .duration(duration)
// .attr("width", function(d) { return second_xScale(d); })
// .attr("y", function(d, i) {
// var numBars = varPers.length;
// return i * height / numBars;
// })
// .attr("height", second_yScale.bandwidth())
// .attr("fill", function(d, i) {
// if (selectedButton === "allIndex" || buttonArray === null ||
// buttonArray.includes(i)) {
// return colorScale((d / 100));
// } else {
// return "#a9b1bc"
// }
// });
//
// // change text of second chart
// secondChart.selectAll("#secondBarText")
// .data(varPers)
// .transition()
// .duration(duration)
// .tween("text", function(d) {
// var that = d3.select(this);
// var i = d3.interpolate(that.text().replace(/%/g, ""), d);
// return function (t) {
// that.text(formatDecimal(i(t)/100));
// };
// });
})
.on("mouseover", function() {
if (dragging === false) {
//fill bar
d3.select(this)
.attr("fill", filled);
}
})
.on("mouseout", function() {
var topBar = d3.min(forecast, function(d) {return first_yScale(d.country); });
if (bar.clicked === null && d3.format(".2f")(this.y.animVal.value) !==
d3.format(".2f")(topBar)) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", unfilled);
} else if (bar.clicked !== null && this !== bar.clicked) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", unfilled);
}
});
// interactivity for second chart (first bars)
secondChart.selectAll(".bar")
.on("mouseover", function(d, i) {
// create text for tooltip
var lineOne = '<p class="ttipText">' + '<b>' + tooltipVar[i] + ": " + '</b>' +
varVals[i] + '</p>',
lineThree = '<p class="ttipText">' + '<b>' + "Metric: " + '</b>' +
tooltipMetric[i] + '</p>',
lineFour = '<p class="ttipText">' + '<b>' + "Data source: " + '</b>' +
tooltipDataSource[i] + '</p>',
lineTwo = '<p class="ttipText">' + "Among the " + '<b>' + dictionary_data.length + '</b>' +
" countries measured since 1945, " + '<b>' + current_country + '</b>' +
" has a percentile rank of " + '<b>' + d + "%" + '</b>' + " for this variable" + '</p>';
// make tooltip visible and add text
ttip
.style("display", "inline-block")
.html(lineOne + "</br>" + lineTwo + "</br>" + lineThree + "</br>" + lineFour);
// add black stroke to selected bar
d3.select(this)
.style("stroke-opacity", 1);
})
.on("mousemove", function() {
// update height and width variables
setDimensions();
// update tooltip position
ttip
.style("top", function() {
// record tooltip height because it varies
ttipHeight = $(this).height(); // using jQuery
// set position of tooltip based on its and page's height
if ((d3.event.pageY + ttipHeight) <= height) {
return (d3.event.pageY + margin.tooltip) + "px";
} else {
return (d3.event.pageY - margin.tooltip * 2 - ttipHeight) + "px";
}
})
.style("left",(d3.event.pageX / 1.1) + "px"); // figure out why 1.1 is best
})
.on("mouseout", function() {
// make tooltip invisible
ttip.style("display", "none");
// remove bar stroke
d3.select(this)
.style("stroke-opacity", 0);
});
// function for interactivity for buttons
function modelUpdate() {
d3.selectAll("#firstSecondBars")
.data(varPers)
.transition()
.duration(duration)
.attr("fill", function(d, i) {
if (selectedButton === "allIndex" || buttonArray.includes(i)) {
return colorScale((d / 100));
} else {
return "#a9b1bc"
}
})
}
d3.selectAll("#model .button").on("click", function() {
selectedButton = d3.select(this).attr("data-val") + "Index";
buttonArray = window[selectedButton];
d3.select("#model .current").classed("current", false);
d3.select(this).classed("current", true);
modelUpdate(model);
});
/*------------------------------------
UPDATING CHARTS ON WINDOW RESIZE
------------------------------------*/
function resize() {
// reset dimensions
setDimensions();
// reset scales
setScales(width, height);
// redraw charts
drawFirstChart(countryStart, countryEnd);
drawSecondChart();
}
resize();
// call the resize function whenever a resize event occurs
window.addEventListener("resize", resize);
}
});
/*------------------------------------
EXTRA CODE TO INCLUDE LATER
------------------------------------*/
// // create color legend for second chart
// var colorLegend = secondChart.selectAll(".legend")
// .attr("class", "colorLegend")
// .data(shades)
// .enter()
// .append("g")
// .attr("class", "legend")
// .attr("transform", "translate(0,20)");
//
// colorLegend.append("rect")
// .attr("x", function(d, i) { return 35 * i; })
// .attr("y", height)
// .attr("width", second_xScale(12))
// .attr("height", 35 / 3)
// .style("fill", function(d) { return d; });
</script>
</body>
</html>