# D3 Build ## Lesson Objectives 1. Add link to d3 library 1. Add an `` tag and size it with D3 1. Create some fake data for our app 1. Add SVG circles and style them 1. Create a linear scale 1. Attach data to visual elements 1. Use data attached to a visual element to affect its appearance 1. Create a time scale 1. Parse and format times ## Add link to d3 library First thing we want to do is create basic `index.html` file: ```html ``` Now add a link to D3 at the bottom of your `` tag in `index.html`: ```html ``` Now create `app.js`, which will store all of our code: ```javascript console.log('this works'); ``` and link to it in `index.html` at the bottom of the `` tag: ```html ``` ## Add an `` tag and size it with D3 At the top of the `` tag in `index.html`, add an `` tag: ```html ``` In `app.js` create variables to hold the width and height of the `` tag: ```javascript var WIDTH = 800; var HEIGHT = 600; ``` Next, we can use `d3.select()` to select a single element, in this case, the `` element: ```javascript var WIDTH = 800; var HEIGHT = 600; d3.select('svg'); ``` The return value of this is a d3 version of the element (just like jQuery), so we "chain" commands onto this. Let's add some styling to adjust the height/width of the element: ```javascript d3.select('svg') .style('width', WIDTH) .style('height', HEIGHT); ``` ## Create some fake data for our app In `app.js` let's create an array of "run" objects (**NOTE I'm storing the date as a string on purpose. Also, it's important that this be an array of objects, in order to work with D3**): ```javascript var WIDTH = 800; var HEIGHT = 600; var runs = [ { id: 1, date: 'October 1, 2017 at 4:00PM', distance: 5.2 }, { id: 2, date: 'October 2, 2017 at 5:00PM', distance: 7.0725 }, { id: 3, date: 'October 3, 2017 at 6:00PM', distance: 8.7 } ]; ``` ## Add SVG circles and style them Add three circles to your `` element (each one will represent a run): ```html ``` Create `app.css` with some styling for the circles and our `svg` element: ```css circle { r:5; fill: black; } svg { border: 1px solid black; } ``` and link to it in `index.html` ```html ``` ## Create a linear scale - Let's position the circles vertically, based on the distance run - One of the most important things that D3 does is provide the ability to map points in the "domain" of data to points in the visual "range" using what's called a `scale`. - There are lots of different kinds of scales, but for now we're just going to use a `linear` scale which will map numeric data values to numeric visual values. In `app.js`: ```javascript d3.select('svg') .style('width', WIDTH) .style('height', HEIGHT); var yScale = d3.scaleLinear(); //create the scale yScale.range([HEIGHT, 0]); //set the visual range (e.g. 600 to 0) yScale.domain([0, 10]); //set the data domain (e.g. 0 to 10) console.log(yScale(5)); //get a visual point from a data value console.log(yScale.invert(450)); //get a data values from a visual point ``` - Here we're saying that a data point of 0 to map to a visual height value of 600 - This is because the lower the distance run (data value), the more we want to move the visual point down the Y axis - remember that the Y axis starts at 0 at the top and increases in value ## Attach data to visual elements We can attach each of our "run" objects to one of our circles, so that each circle can access that data: ```javascript yScale.range([HEIGHT, 0]); yScale.domain([0, 10]); d3.selectAll('circle').data(runs); //selectAll is like select, but selects all elements that match the query string ``` ## Use data attached to a visual element to affect its appearance When setting a value for an element's style, class, id or any other attribute, we can pass that method a callback instead of a static value. ```javascript d3.selectAll('circle').data(runs) .attr('cy', function(datum, index){ return yScale(datum.distance); }); ``` - That callback function runs for each visual element selected - The result of the function is then assigned to whatever aspect of the element is being set (in this case the `cy` attribute) - The callback function takes two params - the individual `datum` object (from the original `runs` array of objects) attached to that particular visual element - the `index` of that `datum` in the original `runs` array ## Create a time scale - Let's position the circles horizontally, based on the date that they happened - First create a time scale: ```javascript var xScale = d3.scaleTime(); //scaleTime maps date values with numeric visual points xScale.range([0,WIDTH]); xScale.domain([new Date('2017-10-1'), new Date('2017-10-31')]); console.log(xScale.domain()); //you can get the domain whenever you want like this console.log(xScale.range()); //you can get the range whenever you want like this ``` ## Parse and format times - Note that our `date` data isn't in the format expected by the xScale domain - D3 provides us an easy way to convert strings to dates and vice versa ```javascript var parseTime = d3.timeParse("%B%e, %Y at %-I:%M%p"); console.log(parseTime('October 3, 2017 at 6:00PM')); var formatTime = d3.timeFormat("%B%e, %Y at %-I:%M%p"); console.log(formatTime(new Date())); ``` Let's use this when calculating `cx` attributes for our circles: ```javascript var parseTime = d3.timeParse("%B%e, %Y at %-I:%M%p"); d3.selectAll('circle') .attr('cx', function(datum, index){ return xScale(parseTime(datum.date)); //use parseTime to convert the date string property on the datum object to a Date object }); ```