Skip to content

Instantly share code, notes, and snippets.

@CBasis
Last active February 24, 2019 20:39
Show Gist options
  • Save CBasis/283d75cdf5dbbb5ead80d508bd970948 to your computer and use it in GitHub Desktop.
Save CBasis/283d75cdf5dbbb5ead80d508bd970948 to your computer and use it in GitHub Desktop.
gatestestnew
license: mit
We can make this file beautiful and searchable if this error is corrected: It looks like row 224 should actually have 3 columns, instead of 4. in line 223.
start_year,grant_title,total_amount
2019,SAPInsideTrackBrisbane,10
2019,SAPInsideTrackGuadalajara,10
2019,SAPTechNightMelbourne,10
2019,SAPInsideTrackMaidenhead,10
2019,SAPInsideTrackSao,10
2019,SAPInsideTrackFrankfurt,10
2019,SAPInsideTrackTimisoara,10
2019,SAPInsideTrackIskenderun,10
2019,SAPInsideTrackMadrid,10
2019,SAPInsideTrackIstanbul,10
2019,SAPInsideTrackCopenhagen,10
2019,SAPInsideTrackBelo,10
2019,SAPInsideTrackHannover,10
2019,SAPInsideTrackWrocław,10
2019,SAPInsideTrackBelgium,10
2019,SAPInsideTrackRuhrpott,10
2019,SAPInsideTrackOslo,10
2019,SAPInsideTrackBerlin,10
2019,SAPInsideTrackBursa,10
2019,SAPInsideTrackMunich,10
2019,SAPInsideTrackTokyo,10
2019,SAPInsideTrackthe,10
2019,SAPInsideTrackHamburg,10
2019,SAPInsideTrackVienna,10
2019,SAPInsideTrackIstanbul,10
2019,SAPInsideTrackCaribbean,10
2019,SAPInsideTrackAtlanta,10
2019,SAPInsideTrackRiga,10
2019,SAPInsideTrackMontréal,10
2019,SAPInsideTrackToronto,10
2019,SAPInsideTrackRecife,10
2019,SAPInsideTrackWalldorf,10
2019,SAPInsideTrackCampinas,10
2018,SAPTechNightSydney,10
2018,SAPInsideTrackIstanbul,10
2018,SAPInsideTrackWien,10
2018,SAPInsideTrackBrisbane,10
2018,SAPInsideTrackCopenhagen,10
2018,SAPInsideTrackNetherlands,10
2018,SAPInsideTrackTokyo,10
2018,SAPInsideTrackBarcelona,10
2018,SAPInsideTrackMunich,10
2018,SAPInsideTrackSan,10
2018,SAPInsideTrackSão,10
2018,SAPInsideTrackfor,10
2018,SAPInsideTrackBern,10
2018,SAPInsideTrackBerlin,10
2018,SAPTechNightSydney,10
2018,SAPInsideTrackSao,10
2018,SAPInsideTrackRome,10
2018,SAPInsideTrackBogota,10
2018,SAPInsideTrackHamburg,10
2018,SAPInsideTrackBrisbane,10
2018,SAPInsideTrackWrocław,10
2018,SAPInsideTrackVancouver,10
2018,SAPInsideTrackKids,10
2018,SAPTechNightMelbourne,10
2018,SAPInsideTrackCopenhagen,10
2018,SAPInsideTrackAnkara,10
2018,SAPInsideTrackNewtown,10
2018,SAPInsideTrackAnkara,10
2018,SAPInsideTrackBelgium,10
2018,SAPInsideTrackIstanbul,10
2018,SAPTechNightSydney,10
2018,SAPInsideTrackTimisoara,10
2018,SAPInsideTrackBelo,10
2018,SAPInsideTrackRibeirao,10
2018,SAPInsideTrackbefore,10
2018,SAPInsideTrackFrankfurt,10
2018,SAPInsideTrackWalldorf,10
2018,SAPInsideTrackCampinas,10
2018,SAPInsideTrackBrisbane,10
2018,SAPInsideTrackHannover,10
2018,SAPInsideTrackSalvador,10
2017,SAPInsideTrackIstanbul,10
2017,SAPInsideTrackSydney,10
2017,SAPInsideTrackParis,10
2017,SAPInsideTrackSão,10
2017,SAPInsideTrackNetherlands,10
2017,SAPInsideTrackCopenhagen,10
2017,SAPInsideTrackBogota,10
2017,SAPInsideTrackTokyo,10
2017,SAPInsideTrackMelbourne,10
2017,5thITConferenceon,10
2017,SAPInsideTrackMonterrey,10
2017,SAPInsideTrackMunich,10
2017,SAPInsideTrackSilicon,10
2017,SAPInsideTrackRecife,10
2017,SAPInsideTrackNovo,10
2017,SAPInsideTrackBern,10
2017,SAPInsideTrackfor,10
2017,SAPInsideTrackBerlin,10
2017,SAPInsideTrackVancouver,10
2017,SAPInsideTrackBuenos,10
2017,SAPInsideTrackMumbai,10
2017,SAPInsideTrackMelbourne,10
2017,SAPInsideTrackBangalore,10
2017,SAPInsideTrackSao,10
2017,SAPInsideTrackBarcelona,10
2017,SAPInsideTrackBrussels,10
2017,SAPInsideTrackTech,10
2017,SAPInsideTrackHamburg,10
2017,SAPInsideTrackWrocław,10
2017,SAPInsideTrackBallerup,10
2017,SAPInsideTrackBrisbane,10
2017,SAPInsideTrackIstanbul,10
2017,SAPInsideTrackFrankfurt,10
2017,SAPInsideTrackSydney,10
2017,SAPInsideTrackWalldorf,10
2017,SAPInsideTrackTokyo,10
2017,SAPInsideTrackCampinas,10
2017,SAPInsideTrackHannover,10
2016,SAPInsideTrackIstanbul,10
2016,SAPInsideTrackChennai,10
2016,SAPInsideTrackSão,10
2016,SAPInsideTrackRecife,10
2016,SAPInsideTrackNetherlands,10
2016,SAPInsideTrackCaribbean,10
2016,SAPInsideTrackSydney,10
2016,SAPInsideTrackTokyo,10
2016,SAPInsideTrackNovo,10
2016,SAPInsideTrackMexico,10
2016,SAPInsideTrackMunich,10
2016,SAPInsideTrackPeru,10
2016,SAPInsideTrackJoinville,10
2016,SAPInsideTrackBrisbane,10
2016,SAPInsideTrackManchester,10
2016,SAPInsideTrackSilicon,10
2016,SAPInsideTrackBern,10
2016,SAPInsideTrackKolkata,10
2016,SAPInsideTrackChicago,10
2016,SAPInsideTrackMexico,10
2016,SAPInsideTrackSao,10
2016,SAPInsideTrackMississauga,10
2016,SAPInsideTrackHamburg,10
2016,SAPInsideTrackGurgaon,10
2016,SAPInsideTrackBelgium,10
2016,SAPInsideTrackWrocław,10
2016,SAPInsideTrackIstanbul,10
2016,SAPInsideTrackFrankfurt,10
2016,SAPInsideTrackSydney,10
2016,SAPInsideTrackSao,10
2015,SAPInsideTrackWalldorf,10
2015,SAPInsideTrackIstanbul,10
2015,SAPInsideTrackHyderabad,10
2015,SAPInsideTrackSao,10
2015,SAPInsideTrackCaribbean,10
2015,SAPInsideTrackTokyo,10
2015,SAPInsideTrackNetherlands,10
2015,SAPInsideTrackBangalore,10
2015,SAPInsideTrackMunich,10
2015,SAPInsideTrackSheffield,10
2015,SAPInsideTrackWrocław,10
2015,SAPInsideTrackPalo,10
2015,SAPInsideTrackMexico,10
2015,SAPInsideTrackKuala,10
2015,SAPInsideTrackJoinville,10
2015,SAPInsideTrackHamburg,10
2015,SAPInsideTrackWashington,10
2015,SAPInsideTrackMelbourne,10
2015,SAPInsideTrackSao,10
2015,SAPInsideTrackFrankfurt,10
2015,SAPInsideTrackRome,10
2015,SAPMeetupWrocław-,10
2014,SAPInsideTrackIstanbul,10
2014,SAPInsideTrackSao,10
2014,SAPInsideTrackNetherlands,10
2014,SAPInsideTrackBengaluru,10
2014,SAPInsideTrackMunich,10
2014,SAPInsideTrackKochi,10
2014,SAPInsideTrackSheffield,10
2014,SAPInsideTrackHyderabad,10
2014,SAPInsideTrackSão,10
2014,SAPInsideTrackNew,10
2014,SAPInsideTrackOslo,10
2014,SAPInsideTrackNoida,10
2014,SAPInsideTrackChicago,10
2014,SAPInsideTrackChennai,10
2014,SAPInsideTrackHamburg,10
2014,SAPInsideTrackMelbourne,10
2014,SAPInsideTrackVancouver,10
2014,SAPInsideTrackHyderabad,10
2014,SAPInsideTrackSingapore,10
2013,Manchester-SAPInside,10
2013,SAPInsideTrack2013,10
2013,SAPInsideTrackCalgary,10
2013,SAPInsideTrackCalgary,10
2013,SAPInsideTrackChicago,10
2013,SAPInsideTrackHamburg,10
2013,SAPInsideTrackIndia,10
2013,SAPInsideTrackIsrael,10
2013,SAPInsideTrackIstanbul,10
2013,SAPInsideTrackManchester,10
2013,SAPInsideTrackMelbourne,10
2013,SAPInsideTrackMunich,10
2013,SAPInsideTrackNewtown,10
2013,SAPInsideTrackNew,10
2013,SAPInsideTrackPrague,10
2013,SAPInsideTrackSingapore,10
2013,SAPInsideTrackSt.,10
2013,SAPInsideTrackToronto,10
2013,SAPInsideTrackWashington,10
2012,SAPInsideTrack2012,10
2012,SAPInsideTrackAnkara,10
2012,SAPInsideTrackBelgium,10
2012,SAPInsideTrackCalgary,10
2012,SAPInsideTrackCalgary,10
2012,SAPInsideTrackCalgary,10
2012,SAPInsideTrackChicago,10
2012,SAPInsideTrackHamburg,10
2012,SAPInsideTrackIndia,10
2012,SAPInsideTrackMilan,10
2012,SAPInsideTrackMilan,10
2012,SAPInsideTrackNew,10
2012,SAPInsideTrackPalo,10
2012,SAPInsideTrackPalo,10
2012,SAPInsideTrackSao,10
2012,SAPInsideTrackSingapore,10
2012,SAPInsideTrackSydney,10
2012,SAPInsideTrackToronto,10
2012,SAPInsideTrackVancouver,10
2012,SAPInsideTrackWashington,10
2012,SAPNetworkHamburg,Germany,10
2011,SAPInsideTrackNetherlands,10:00,19:00,10
2011,SAPINSIDETRACK-,10
2011,SAPINSIDETRACK-,10
2011,SAPInsideTrackAnkara,10
2011,SAPInsideTrackChicago,10
2011,SAPInsideTrackIndia,10
2011,SAPInsideTrackIstanbul,10
2011,SAPInsideTrackMilan,10
2011,SAPInsideTrackNewtown,10
2011,SAPInsideTrackRio,10
2011,SAPInsideTrackSao,10
2011,SAPInsideTrackSt.,10
2011,SAPInsideTrackSydney,10
2011,SAPInsideTrackVancouver,10
2011,SAPInsideTrackWashington,10
2010,SAPInsideTrackBangalore,10
2010,SAPInsideTrackBrussels,10
2010,SAPInsideTrackLondon,10
2010,SAPInsideTrackNetherlands,10
2010,SAPInsideTrackBonn,10
2010,SAPInsideTrackIsrael,10
2010,SAPInsideTrackIstanbul,10
2010,SAPInsideTrackNetherlands,10
2010,SAPInsideTrackNewtown,10
2010,SAPInsideTrackPalo,10
2010,SAPInsideTrackSao,10
2010,SAPInsideTrackSt.,10
2010,SAPInsideTrackSydney,10
2010,SAPInsideTrackVancouver,10
2010,SAPInsideTrackWalldorf,10
2010,SAPInsideTrackWarwick,10
2009,SAPInsideTrackCommunity,10
2009,SAPInsideTrackEindhoven,10
2009,SAPInsideTrackPalo,10
2009,SAPInsideTrackSao,10
2009,SAPInsideTrackLondon,10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Bubble Chart Experiment</title>
</head>
<style>
a, a:visited, a:active {
color: #444;
}
.container {
max-width: 900px;
margin: auto;
}
.button {
min-width: 130px;
padding: 4px 5px;
cursor: pointer;
text-align: center;
font-size: 13px;
border: 1px solid #e0e0e0;
text-decoration: none;
}
.button.active {
background: #000;
color: #fff;
}
#vis {
width: 940px;
height: 600px;
clear: both;
margin-bottom: 10px;
}
#toolbar {
margin-top: 10px;
}
.year {
font-size: 21px;
fill: #aaa;
cursor: default;
}
.tooltip {
position: absolute;
top: 100px;
left: 100px;
-moz-border-radius:5px;
border-radius: 5px;
border: 2px solid #000;
background: #fff;
opacity: .9;
color: black;
padding: 10px;
width: 300px;
font-size: 12px;
z-index: 10;
}
.tooltip .title {
font-size: 13px;
}
.tooltip .name {
font-weight:bold;
}
.footer {
text-align: center;
}
</style>
<body>
<div class="container">
<h1>Gates Foundation Educational Spending</h1>
<div id="toolbar">
<a href="#" id="all" class="button active">All Grants</a>
<a href="#" id="year" class="button">Grants By Year</a>
</div>
<div id="vis"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
function bubbleChart() {
// Constants for sizing
var width = 840;
var height = 600;
// tooltip for mouseover functionality
var tooltip = floatingTooltip('gates_tooltip', 240);
// Locations to move bubbles towards, depending
// on which view mode is selected.
var center = { x: width / 2, y: height / 2 };
var yearCenters = {
2008: { x: 9 * width / 9, y: height / 2 },
2009: { x: 8 * width / 9, y: height / 2 },
2010: { x: 7 * width / 9, y: height / 2 },
2011: { x: 6 * width / 9, y: height / 2 },
2012: { x: 5 * width / 9, y: height / 2 },
2013: { x: 4 * width / 9, y: height / 2 },
2014: { x: 3 * width / 9, y: height / 2 },
2015: { x: 2 * width / 9, y: height / 2 },
2016: { x: width / 9, y: height / 2 }
};
// X locations of the year titles.
var yearsTitleX = {
2008: 9 * width / 9,
2009: 8 * width / 9,
2010: 7 * width / 9,
2011: 6 * width / 9,
2012: 5 * width / 9,
2013: 4 * width / 9,
2014: 3 * width / 9,
2015: 2 * width / 9,
2016: 1 * width / 9
};
// @v4 strength to apply to the position forces
var forceStrength = 0.03;
// These will be set in create_nodes and create_vis
var svg = null;
var bubbles = null;
var nodes = [];
// Charge function that is called for each node.
// As part of the ManyBody force.
// This is what creates the repulsion between nodes.
//
// Charge is proportional to the diameter of the
// circle (which is stored in the radius attribute
// of the circle's associated data.
//
// This is done to allow for accurate collision
// detection with nodes of different sizes.
//
// Charge is negative because we want nodes to repel.
// @v4 Before the charge was a stand-alone attribute
// of the force layout. Now we can use it as a separate force!
function charge(d) {
return -Math.pow(d.radius, 2.0) * forceStrength;
}
// Here we create a force layout and
// @v4 We create a force simulation now and
// add forces to it.
var simulation = d3.forceSimulation()
.velocityDecay(0.2)
.force('x', d3.forceX().strength(forceStrength).x(center.x))
.force('y', d3.forceY().strength(forceStrength).y(center.y))
.force('charge', d3.forceManyBody().strength(charge))
.on('tick', ticked);
// @v4 Force starts up automatically,
// which we don't want as there aren't any nodes yet.
simulation.stop();
// Nice looking colors - no reason to buck the trend
// @v4 scales now have a flattened naming scheme
var fillColor = d3.scaleLinear()
.domain([2008, 2019])
.range(['#4286f4', '#0a9850'])
.interpolate(d3.interpolateHcl);
/*
* This data manipulation function takes the raw data from
* the CSV file and converts it into an array of node objects.
* Each node will store data and visualization values to visualize
* a bubble.
*
* rawData is expected to be an array of data objects, read in from
* one of d3's loading functions like d3.csv.
*
* This function returns the new node array, with a node in that
* array for each element in the rawData input.
*/
function createNodes(rawData) {
// Use the max total_amount in the data as the max in the scale's domain
// note we have to ensure the total_amount is a number.
var maxAmount = d3.max(rawData, function (d) { return +d.total_amount; });
// Sizes bubbles based on area.
// @v4: new flattened scale names.
var radiusScale = d3.scalePow()
.exponent(0.5)
.range([2, 15])
.domain([0, maxAmount]);
// Use map() to convert raw data into node data.
// Checkout http://learnjsdata.com/ for more on
// working with data.
var myNodes = rawData.map(function (d) {
return {
id: d.id,
// radius: radiusScale(+d.total_amount),
radius: radiusScale(+d.total_amount),
value: +d.total_amount,
name: d.grant_title,
// org: d.organization,
// group: d.group,
year: d.start_year,
x: Math.random() * 900,
y: Math.random() * 800
};
});
// sort them to prevent occlusion of smaller nodes.
myNodes.sort(function (a, b) { return b.value - a.value; });
return myNodes;
}
/*
* Main entry point to the bubble chart. This function is returned
* by the parent closure. It prepares the rawData for visualization
* and adds an svg element to the provided selector and starts the
* visualization creation process.
*
* selector is expected to be a DOM element or CSS selector that
* points to the parent element of the bubble chart. Inside this
* element, the code will add the SVG continer for the visualization.
*
* rawData is expected to be an array of data objects as provided by
* a d3 loading function like d3.csv.
*/
var chart = function chart(selector, rawData) {
// convert raw data into nodes data
nodes = createNodes(rawData);
// Create a SVG element inside the provided selector
// with desired size.
svg = d3.select(selector)
.append('svg')
.attr('width', width)
.attr('height', height);
// Bind nodes data to what will become DOM elements to represent them.
bubbles = svg.selectAll('.bubble')
.data(nodes, function (d) { return d.id; });
// Create new circle elements each with class `bubble`.
// There will be one circle.bubble for each object in the nodes array.
// Initially, their radius (r attribute) will be 0.
// @v4 Selections are immutable, so lets capture the
// enter selection to apply our transtition to below.
var bubblesE = bubbles.enter().append('circle')
.classed('bubble', true)
.attr('r', 0)
.attr('fill', function (d) { return fillColor(+d.start_year); })
.attr('stroke', function (d) { return d3.rgb(fillColor(d.group)).darker(); })
.attr('stroke-width', 2)
.on('mouseover', showDetail)
.on('mouseout', hideDetail);
// @v4 Merge the original empty selection and the enter selection
bubbles = bubbles.merge(bubblesE);
// Fancy transition to make bubbles appear, ending with the
// correct radius
bubbles.transition()
.duration(2000)
.attr('r', function (d) { return d.radius; });
// Set the simulation's nodes to our newly created nodes array.
// @v4 Once we set the nodes, the simulation will start running automatically!
simulation.nodes(nodes);
// Set initial layout to single group.
groupBubbles();
};
/*
* Callback function that is called after every tick of the
* force simulation.
* Here we do the acutal repositioning of the SVG circles
* based on the current x and y values of their bound node data.
* These x and y values are modified by the force simulation.
*/
function ticked() {
bubbles
.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; });
}
/*
* Provides a x value for each node to be used with the split by year
* x force.
*/
function nodeYearPos(d) {
console.log(yearCenters[d.year].x);
return yearCenters[d.year].x;
}
/*
* Sets visualization in "single group mode".
* The year labels are hidden and the force layout
* tick function is set to move all nodes to the
* center of the visualization.
*/
function groupBubbles() {
hideYearTitles();
// @v4 Reset the 'x' force to draw the bubbles to the center.
simulation.force('x', d3.forceX().strength(forceStrength).x(center.x));
// @v4 We can reset the alpha value and restart the simulation
simulation.alpha(1).restart();
}
/*
* Sets visualization in "split by year mode".
* The year labels are shown and the force layout
* tick function is set to move nodes to the
* yearCenter of their data's year.
*/
function splitBubbles() {
showYearTitles();
// @v4 Reset the 'x' force to draw the bubbles to their year centers
simulation.force('x', d3.forceX().strength(forceStrength).x(nodeYearPos));
// @v4 We can reset the alpha value and restart the simulation
simulation.alpha(1).restart();
}
/*
* Hides Year title displays.
*/
function hideYearTitles() {
svg.selectAll('.year').remove();
}
/*
* Shows Year title displays.
*/
function showYearTitles() {
// Another way to do this would be to create
// the year texts once and then just hide them.
var yearsData = d3.keys(yearsTitleX);
var years = svg.selectAll('.year')
.data(yearsData);
years.enter().append('text')
.attr('class', 'year')
.attr('x', function (d) { return yearsTitleX[d]; })
.attr('y', 40)
.attr('text-anchor', 'middle')
.text(function (d) { return d; });
}
/*
* Function called on mouseover to display the
* details of a bubble in the tooltip.
*/
function showDetail(d) {
// change outline to indicate hover state.
d3.select(this).attr('stroke', 'black');
var content = '<span class="name">Title: </span><span class="value">' +
d.name +
'</span><br/>' +
'<span class="name">Amount: </span><span class="value">$' +
addCommas(d.value) +
'</span><br/>' +
'<span class="name">Year: </span><span class="value">' +
d.year +
'</span>';
tooltip.showTooltip(content, d3.event);
}
/*
* Hides tooltip
*/
function hideDetail(d) {
// reset outline
d3.select(this)
.attr('stroke', d3.rgb(fillColor(d.group)).darker());
tooltip.hideTooltip();
}
/*
* Externally accessible function (this is attached to the
* returned chart function). Allows the visualization to toggle
* between "single group" and "split by year" modes.
*
* displayName is expected to be a string and either 'year' or 'all'.
*/
chart.toggleDisplay = function (displayName) {
if (displayName === 'year') {
splitBubbles();
} else {
groupBubbles();
}
};
// return the chart function from closure.
return chart;
}
/*
* Below is the initialization code as well as some helper functions
* to create a new bubble chart instance, load the data, and display it.
*/
var myBubbleChart = bubbleChart();
/*
* Function called once data is loaded from CSV.
* Calls bubble chart function to display inside #vis div.
*/
function display(error, data) {
if (error) {
console.log(error);
}
myBubbleChart('#vis', data);
}
/*
* Sets up the layout buttons to allow for toggling between view modes.
*/
function setupButtons() {
d3.select('#toolbar')
.selectAll('.button')
.on('click', function () {
// Remove active class from all buttons
d3.selectAll('.button').classed('active', false);
// Find the button just clicked
var button = d3.select(this);
// Set it as the active button
button.classed('active', true);
// Get the id of the button
var buttonId = button.attr('id');
// Toggle the bubble chart based on
// the currently clicked button.
myBubbleChart.toggleDisplay(buttonId);
});
}
/*
* Helper function to convert a number into a string
* and add commas to it to improve presentation.
*/
function addCommas(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
// Load the data.
d3.csv('gates_money.csv', display);
// setup the buttons.
setupButtons();
function floatingTooltip(tooltipId, width) {
// Local variable to hold tooltip div for
// manipulation in other functions.
var tt = d3.select('body')
.append('div')
.attr('class', 'tooltip')
.attr('id', tooltipId)
.style('pointer-events', 'none');
// Set a width if it is provided.
if (width) {
tt.style('width', width);
}
// Initially it is hidden.
hideTooltip();
/*
* Display tooltip with provided content.
*
* content is expected to be HTML string.
*
* event is d3.event for positioning.
*/
function showTooltip(content, event) {
tt.style('opacity', 1.0)
.html(content);
updatePosition(event);
}
/*
* Hide the tooltip div.
*/
function hideTooltip() {
tt.style('opacity', 0.0);
}
/*
* Figure out where to place the tooltip
* based on d3 mouse event.
*/
function updatePosition(event) {
var xOffset = 20;
var yOffset = 10;
var ttw = tt.style('width');
var tth = tt.style('height');
var wscrY = window.scrollY;
var wscrX = window.scrollX;
var curX = (document.all) ? event.clientX + wscrX : event.pageX;
var curY = (document.all) ? event.clientY + wscrY : event.pageY;
var ttleft = ((curX - wscrX + xOffset * 2 + ttw) > window.innerWidth) ?
curX - ttw - xOffset * 2 : curX + xOffset;
if (ttleft < wscrX + xOffset) {
ttleft = wscrX + xOffset;
}
var tttop = ((curY - wscrY + yOffset * 2 + tth) > window.innerHeight) ?
curY - tth - yOffset * 2 : curY + yOffset;
if (tttop < wscrY + yOffset) {
tttop = curY + yOffset;
}
tt
.style('top', tttop + 'px')
.style('left', ttleft + 'px');
}
return {
showTooltip: showTooltip,
hideTooltip: hideTooltip,
updatePosition: updatePosition
};
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment