# D3.js ## Basics ### Selection ```javascript d3.select('#some-id') //like document.querySelector() d3.selectAll('.some-class') //like document.querySelectorAll() d3.select('main').selectAll('span'); //can chain to select ancestors ``` ### .style() ```javascript d3.select('div').style('color', 'orange'); //sets the style for an element d3.select('div').style('color', 'orange').style('font-size': '20px'); //will return the selection for chaining ``` ### .attr() ```javascript d3.select('div').attr('anExampleAttribute', 'someValue'); //adds/changes an attribute on an selection ``` ### .classed() ```javascript d3.selectAll('.house').classed('house'); // returns true if all elements in selection contain the chosen class d3.selectAll('div').classed('frog', true); //adds the class and returns the selection d3.selectAll('div').classed('frog', false); //removes the class and returns the selection ``` ### .append() ```javascript d3.selectAll('div').append('span'); //append html to a selection and return appended element ``` ### .html() ```javascript d3.selectAll('div').html('hi'); //change the inner html of an element ``` ### .text() ```javascript d3.selectAll('div').text('hi'); //set the content of the selection to the exact text (escapes html) ``` ## AJAX Named based off of what kind of data they accept ```javascript d3.json('path', function(error, data){}); d3.csv('path', function(error, data){}); d3.tsv('path', function(error, data){}); d3.xml('path', function(error, data){}); d3.html('path', function(error, data){}); d3.text('path', function(error, data){}); ``` ## Data binding ```javascript d3.selectAll('circle')//make a "ghost call" to all circles, even if there are none already .data(dataArray) //joins each element in dataArray to an element in the selection .enter() //returns the sub section of dataArray that has not been matched with DOM elements .append('circle'); //creates a DOM element for each of the remaining dataArray elements ``` once data has been bound to elements, you can call something like: ```javascript d3.selectAll('circle').attr('r', function(d,i){ //d is data for the current element, i is the index of that element in the array //callback will be executed for each DOM element //return value is how each value will be set return d.value * 2 //takes value property of d (data), multiplies it by two and sets the radius to that }) ``` Can remove elements: ```javascript d3.selectAll('circle')//make a "ghost call" to all circles, even if there are none already .data(dataArray) //joins each element in dataArray to an element in the selection .exit() //returns the sub section of DOM elements that has not been matched with dataArray elements .remove(); //removes those elements ``` ## Linear Scale A scale will map a data value to a visual value. 1. Create a scale. There are many types. Here we'll use a linear scale ```javascript var yScale = d3.scaleLinear(); ``` 1. Set up a visual range ```javascript yScale.range([height,0]); ``` 1. Add the domain ```javascript yScale.domain(yDomain); ``` 1. Can check range and domain after initialization ```javascript yScale.range(); yScale.domain(); ``` 1. Can now pass a data value into the scale to get a visual value ```javascript yScale(361); //returns the visual value that maps to this data value ``` 1. Can go the opposite way ```javascript yScale.invert(800); //returns the data value that maps to this visual value ``` 1. If data min/max of a data set (called the "domain") are not found, you can find them: ```javascript var yMax = d3.max(data, function(element){ return parseInt(element.TMAX); }) var yMin = d3.min(data, function(element){ return parseInt(element.TMAX); }) var yDomain = [yMin, yMax]; ``` - Can combine this into one call if max/min come from same element: ```javascript var yDomain = d3.extent(data, function(element){ return parseInt(element.TMAX); }); ``` ## Time Scale 1. Create the scale ```javascript var xScale = d3.scaleTime(); ``` 1. Set up the visual range ```javascript xScale.range([0, width]); ``` 1. Set up the time range ```javascript xScale.domain([new Date('2016-1-1'), new Date('2017-1-1')]); ``` ### Dealing with alternate date formats Date formatting options: https://github.com/d3/d3-time-format#locale_format To parse an alternate format into a date object ```javascript var parseTime = d3.timeParse("%Y%m%d"); parseTime('20160101') //returns a date object ``` To create an alternately formated string from a date object ```javascript var formatTime = d3.timeFormat("%Y%m%d"); formatTime(new Date()); //returns a string in the above format ``` ## Axes - Create the x axis ```javascript var xAxis = d3.svg.axis(); ``` - Associate the scale with the axis ```javascript xAxis.scale(xScale); ``` - Set where on the graph the axis should appear ```javascript xAxis.orient('bottom'); ``` - Set the number of ticks ```javascript xAxis.ticks(8); ``` - Append a group containing the axis after data has populated the scale ```javascript viz.append('g').call(yAxis); ``` ## Events ```javascript select.on('mouseenter', function(data, index){ d3.select(this); //select just element that was hovered console.log(d3.event); //the event object }) ``` click, mouseenter and mouseleave are common use `d3.event.sourceEvent.stopPropagation();` when events conflict ## Behaviors ### Zooming ```javascript //generator for a behavior //scale from 1 - 10 //.on function says, when there's an event of type 'zoom', call the 'zoomed' function. Could be any event var zoom = d3.behavior.zoom().scaleExtent([1,10]).on('zoom', zoomed); var svg = d3.select('#viz-wrapper').append('svg').call(zoom); function zoomed(){ console.log(d3.event.translate);//get mouse position console.log(d3.event.scale);//bounded by 1,10 as set up above viz.attr('transform', 'translate(' + d3.event.translate + ')' + 'scale(' + d3.event.scale + ')'); } ``` ### Dragging ```javascript var drag = d3.behavior.drag() .on('dragstart', dragStart) .on('drag', drag) .on('dragend', dragEnd); //.... dotsGroup.call(drag); //.... function dragStart(d){ //d is the data for the dragged object d3.select(this); //the visual object d3.event.x; //x position of cursor d3.event.y; //y position of cursor } ``` You can use the xScale.invert and yScale.invert to get data from d3.event.x and d3.event.y ## Basic Layouts - https://github.com/d3/d3/wiki/Plugins - http://c3js.org/