adjustments to scatter plot

master
Matt Huntington 7 years ago
parent a13fd4523e
commit a5b63fee71

@ -633,7 +633,7 @@ Now the browser should look like this:
## Create click handler
Let's say that we want it so that when the user clicks on the `<svg>` element, it creates a new run.
Let's say that we want it so that when the user clicks on the `<svg>` element, it creates a new run. Add the following to the bottom of `app.js`:
```javascript
d3.select('svg').on('click', function(){
@ -653,11 +653,13 @@ d3.select('svg').on('click', function(){
});
```
You might notice that `createTable()` just adds on all the run rows again
Let's examine what we just wrote. `d3.select('svg').on('click', function(){` Sets up a click handler on the `svg` element. The anonymous function that gets passed in as the second parameter to `.on()` gets called each time the user clicks on the SVG. Once inside that callback function, we use `d3.event.offsetX` to get the x position of the mouse inside the SVG and `d3.event.offsetY` to get the y position. We then use `xScale.invert()` and `yScale.invert()` to turn the x/y visual points into data values (date and distance, respectively). We then use those data values to create a new run object. We create an id for the new run by getting the id of the last element in the `runs` array and adding 1 to it. Lastly, we push the new run onto the `runs` array and call `createTable()`.
Click on the SVG to create a new run. You might notice that `createTable()` just adds on all the run rows again
![](https://i.imgur.com/Vu2CwCI.png)
Let's clear out the rows previous created and re-render everything:
Let's alter the `createTable()` function so that when it runs, it clears out any rows previously created and re-renders everything. Add `d3.select('tbody').html('')` to the top of the `createTable` function in `app.js`:
```javascript
var createTable = function(){
@ -671,9 +673,11 @@ var createTable = function(){
}
```
Now refresh the page, and click on the SVG to create a new run. The table should look like this now:
![](https://i.imgur.com/YcoPxK7.png)
Now put the code for creating `<circles>` inside a render function:
The only issue now is that circles aren't being created when you click on the SVG. To fix this, let's wrap the code for creating `<circle>` elements in a render function, and call `render()` immediately after it's defined:
```javascript
var render = function(){
@ -713,7 +717,7 @@ var render = function(){
render();
```
For future use, let's move the `xScale` and `yScale` out of the render function along with the code for creating the domains/ranges:
If you refresh the browser, you'll see an error in the console. This is because the `bottomAxis` and `leftAxis` use `xScale` and `yScale` which are now scoped to exist only inside the `render()` function. For future use, let's move the `xScale` and `yScale` out of the render function along with the code for creating the domains/ranges:
```javascript
var parseTime = d3.timeParse("%B%e, %Y at %-I:%M%p");
@ -752,14 +756,21 @@ var render = function(){
render();
```
Let's call `render()` inside our `<svg>` click handler:
Now go to the bottom of `app.js` and add a line to call `render()` inside our `<svg>` click handler:
```javascript
var newRun = { //create a new "run" object
id: runs[runs.length-1].id+1, //generate a new id by adding 1 to the last run's id
date: formatTime(date), //format the date object created above to a string
distance: distance //add the distance
}
runs.push(newRun);
createTable();
render(); //add this line
```
Now when you click the SVG, a circle will appear:
![](https://i.imgur.com/5KjqmNp.png)
## Remove data

@ -24,23 +24,6 @@ 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)
var yDomain = d3.extent(runs, function(datum, index){
return datum.distance; //compare distance properties of each item in the data array
})
yScale.domain(yDomain);
d3.select('svg').selectAll('circle') //since no circles exist, we need to select('svg') so that d3 knows where to append the new circles
.data(runs) //attach the data as before
.enter() //find the data objects that have not yet been attached to visual elements
.append('circle'); //for each data object that hasn't been attached, append a <circle> to the <svg>
d3.selectAll('circle')
.attr('cy', function(datum, index){
return yScale(datum.distance);
});
var parseTime = d3.timeParse("%B%e, %Y at %-I:%M%p");
var formatTime = d3.timeFormat("%B%e, %Y at %-I:%M%p");
var xScale = d3.scaleTime();
@ -50,10 +33,30 @@ var xDomain = d3.extent(runs, function(datum, index){
});
xScale.domain(xDomain);
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, which xScale then converts to a visual value
});
var yScale = d3.scaleLinear(); //create the scale
yScale.range([HEIGHT, 0]); //set the visual range (e.g. 600 to 0)
var yDomain = d3.extent(runs, function(datum, index){
return datum.distance; //compare distance properties of each item in the data array
})
yScale.domain(yDomain);
var render = function(){
d3.select('svg').selectAll('circle') //since no circles exist, we need to select('svg') so that d3 knows where to append the new circles
.data(runs) //attach the data as before
.enter() //find the data objects that have not yet been attached to visual elements
.append('circle'); //for each data object that hasn't been attached, append a <circle> to the <svg>
d3.selectAll('circle')
.attr('cy', function(datum, index){
return yScale(datum.distance);
});
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, which xScale then converts to a visual value
});
}
render();
var bottomAxis = d3.axisBottom(xScale); //pass the appropriate scale in as a parameter
d3.select('svg')
@ -67,6 +70,7 @@ d3.select('svg')
.call(leftAxis); //no need to transform, since it's placed correctly initially
var createTable = function(){
d3.select('tbody').html(''); //clear out all rows from the table
for (var i = 0; i < runs.length; i++) {
var row = d3.select('tbody').append('tr');
row.append('td').html(runs[i].id);
@ -76,3 +80,20 @@ var createTable = function(){
}
createTable();
d3.select('svg').on('click', function(){
var x = d3.event.offsetX; //gets the x position of the mouse relative to the svg element
var y = d3.event.offsetY; //gets the y position of the mouse relative to the svg element
var date = xScale.invert(x) //get a date value from the visual point that we clicked on
var distance = yScale.invert(y); //get a numeric distance value from the visual point that we clicked on
var newRun = { //create a new "run" object
id: runs[runs.length-1].id+1, //generate a new id by adding 1 to the last run's id
date: formatTime(date), //format the date object created above to a string
distance: distance //add the distance
}
runs.push(newRun); //push the new run onto the runs array
createTable(); //render the table
render(); //add this line
});

Loading…
Cancel
Save