D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
mph006
Full window
Github gist
Olympic 100M Dash Medals - Chart Resizing, Event Listeners, Voronoi Overlay
<!DOCTYPE html> <meta charset="utf-8"> <title>Olympics 100 Meter Dash</title> <style type="text/css"> body { font-family: arial, sans; font-size: 13px; margin: 10px auto; max-width:900px; } .g-graphic-container{ position: relative; max-width: 900px; } svg{ max-width: 900px; /* border: thin solid #f0f;*/ } .line { fill:none; } .axis text { font-size: 12px; fill: #777; } .axis path { display: none; } .axis line { stroke-width:.3px; stroke: #dedede; /*stroke-dasharray: 2px 2px;*/ } .country-container { display: inline-block; margin-right:10px; } .info{ position: absolute; top: 10px; left: 250px; } .country-label{ position: absolute; top:15px; left:150px; } #countrySelect{ position: absolute; top: 30px; left:500px; } #decileSelect{ position: absolute; top: 30px; left:650px; } table { width: 120px; margin: 10px 10px; border-collapse: collapse; position: absolute; } .g-num { text-align: left; } td { padding: 3px 0; border-bottom: 1px solid #dedede; } .medal-table{ position: absolute; top: 100px; left: 120px; background-color: white; } .tooltip{ border: thin solid #dedede; visibility: hidden; display: inline-block; position: absolute; left:275px; top:20px; background-color: white; padding: 10px; } .g-row{ cursor: pointer; } h1{ position: relative; font-size: 25px; top:10px; left: 200px; color:#606060; } path{ fill:white; fill-opacity: 0.1; stroke: #f0f; } path:hover{ fill:orange; } </style> <body> <div class="g-graphic-container"> <h1>Olympic 100 Meter Dash Records</h1> <div class="info"></div> <div class="tooltip"> <div class="athlete-name"></div> <div class="origin-country"></div> <div class="win-year"></div> <div class="medal-status"></div> <div class="meters-back"></div> <div class="seconds-back"></div> </div> </div> </body> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script> <script type="text/javascript"> var countryMap = { "USA": "United States", "GBR": "Great Britain", "JAM": "Jamaica", "CAN": "Canada", "TRI": "Trinidad and Tobago", "AUS": "Australia", "GER": "Germany", "NAM": "Namibia", "CUB": "Cuba", "URS": "Soviet Union", "BAR": "Barbados", "EUA": "United Team of Germany", "HUN": "Hungary", "PAN": "Panama", "NED": "Netherlands", "POR": "Portugal", "NZL": "New Zealand", "RSA": "South Africa", "BUL": "Bulgaria" }; var colors = { "GOLD": "#efcf6d", "SILVER": "#b0b0b1", "BRONZE": "#c59e8a" } var margin = {top: 20, right: 50, bottom: 20, left: 80}; var reSpace = new RegExp(" ", 'g'); var graphic = d3.select(".g-graphic-container"); var width = graphic.node().clientWidth - margin.left - margin.right, height = 600 - margin.top - margin.bottom; var xScale = d3.scale.linear() .range([width,0]); var yScale = d3.scale.ordinal() // .range([height, 0]); .rangeRoundPoints([0, height]); var xAxis = d3.svg.axis() .scale(xScale) .tickSize(-height) .tickPadding(8) .tickFormat(d3.round) .orient("bottom"); var yAxis = d3.svg.axis() .scale(yScale) .tickSize(-width) .tickPadding(8) .orient("right"); var globalData; //https://stackoverflow.com/questions/14167863/how-can-i-bring-a-circle-to-the-front-with-d3 d3.selection.prototype.moveToFront = function() { return this.each(function(){ this.parentNode.appendChild(this); }); }; 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 + ")"); var countryNest; d3.tsv("medalists.tsv", ready); function mouseoverMedal(d){ var sel = d3.select(this); sel.moveToFront(); sel.style("stroke","black").style("stroke-width","2px"); d3.select(".tooltip") .style("visibility","visible") .style("transform","translate("+(d3.mouse(this)[0]-366)+"px,"+(d3.mouse(this)[1]-10)+"px)"); d3.select(".athlete-name").text(d.name); d3.select(".win-year").text(d.year); d3.select(".medal-status").text(d.medal); d3.select(".meters-back").text("Meters Back: "+Number((d.metersBack).toFixed(4))); d3.select(".seconds-back").text("Seconds Behind: "+Number((d.secondsBehind).toFixed(4))); d3.select(".origin-country").text(d.country); } function mouseoverTable(d){ d3.select(this).style("background-color","#fdfd96"); d3.selectAll("#"+d.key.replace(reSpace,"-")) .style("stroke","black") .style("stroke-width","2px"); } function mouseleaveMedal(){ d3.select(".tooltip").style("visibility","hidden"); d3.select(this).style("stroke","none"); } function mouseleaveTable(d){ d3.select(this).style("background-color","white"); d3.selectAll("#"+d.key.replace(reSpace,"-")) .style("stroke","none"); } function createTable(countryNest){ var table = d3.select("body") .append("table") .attr("class","medal-table"); var row = table.selectAll(".g-row") .data(countryNest) .enter().append("tr") .attr("class", "g-row") .on("mouseover",mouseoverTable) .on("mouseleave",mouseleaveTable); row.append("td") .attr("class", "g-x g-num") .text(function(d) { return d.key;}); row.append("td") .attr("class", "g-y g-num") .text(function(d){ return d.values;}); } function polygon(d) { return "M" + d.join("L") + "Z"; } //https://bl.ocks.org/mbostock/8033015 function addVoronoi(){ var voronoi = d3.geom.voronoi() .clipExtent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]); verticies = globalData.map(function(d){ //needs a random for some reason... return [xScale(d.metersBack)+Math.random(),yScale(d.year)+Math.random()] }); svg.append("g").selectAll("path") .data(voronoi(verticies), polygon) .enter().append("path") .attr("d", polygon) //bind the mouseover data to the global data datum by index, not the polygon itself .datum(function(d,i){return globalData[i]}) .on("mouseover",function(d){ console.log(d); }); } function ready(error, data) { if (error) return console.warn(error); var winnerTime = parseFloat(data[0].time); data.forEach(function(d){ d.time = parseFloat(d.time); d.countryCode = d.countryCode.trim(); d.country = countryMap[d.countryCode]; d.medal =d.medal.trim(); //100M Dash d.secondsBehind = d.time - winnerTime; var metersPerSecond = 100/d.time; d.metersBack = d.secondsBehind * metersPerSecond; }); globalData = data; var countryNest = d3.nest() .key(function(d){return d.country;}) .rollup(function(leaves) { return leaves.length; }) .entries(data); countryNest.sort(function(a,b){return b.values - a.values; }); var years = data.map(function(d){return d.year;}) yearArray = d3.set(years).values(); xScale.domain(d3.extent(data,function(d){return d.metersBack;})); yScale.domain(yearArray.reverse()); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + (height) + ")") .call(xAxis) .selectAll("g"); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" +width+ ",0)") .call(yAxis); addVoronoi(); svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("r",5) .attr("class","medal") .attr("id",function(d){return d.country.replace(reSpace,"-");}) .attr("cx",function(d){return xScale(d.metersBack);}) .attr("cy",function(d){return yScale(d.year)}) .style("fill",function(d){return colors[d.medal];}) .on("mouseover",mouseoverMedal) .on("mouseleave", mouseleaveMedal); createTable(countryNest); } function updateWindow(){ width = graphic.node().clientWidth - margin.left - margin.right; if(width<=460){ d3.select(".medal-table").style("visibility","hidden").style("left","50px"); d3.select("h1").style("font-size","11px"); } else{ d3.select("h1").style("font-size","25px"); d3.select(".medal-table").style("visibility","visible"); } xScale.range([width,0]); d3.select("svg").attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); svg.selectAll("circle") .attr("cx",function(d){return xScale(d.metersBack);}); svg.select(".y") .attr("transform", "translate(" +width+ ",0)"); svg.select(".x") .attr("transform", "translate(0," + (height) + ")") .call(xAxis); } window.onresize = updateWindow; </script>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js