freeCodeCamp / CurriculumExpansion

Creative Commons Attribution Share Alike 4.0 International
314 stars 104 forks source link

D3 Challenges #20

Closed QuincyLarson closed 7 years ago

QuincyLarson commented 8 years ago

For each challenge, please reply to this GitHub issue with:

  1. Challenge description text
  2. Test suite (using the assert method)
  3. The seed code, which is pre-populated in the editor at the beginning of the challenge
  4. A working solution that makes all tests pass

We still need to come up with a list of concepts to cover, where each topic can be taught interactively, then translate these concept into the names of the challenges.


Edit:

With inputs from @elibei, these are the topics we would like to cover under D3 [WIP]

evaristoc commented 8 years ago

What I think it could work is the following: Based on reference: http://chimera.labs.oreilly.com/books/1230000000345/index.html

  1. A short exercise with SVG. SVG is different from CSS shapes in many ways, so a short introduction would be nice. Ch 3 of the book. Pick a couple of exercises that highlight some differences and similarities.
  2. A first encounter with d3.js: generating page elements. Ch 5 of the book of the same title. Some exercises in that section are relevant.
  3. First aspect to understand: d3.js is mostly about DATA. The first part of the "Binding Data" in the Ch 5 would be useful.
  4. The SELECTION aspect of d3.js. Same Ch 5, same part ("Binding Data"), subtitle "Please Make your Selection"
  5. Using data to draw things. Ch 6 up to "The Power of data()".
  6. Difference between selections and appends. I don't know what to recommend here, but relevant!
  7. Understanding Enter-Update-Exit cycle. Here I would suggest another resource instead: https://medium.com/@c_behrens/enter-update-exit-6cafc6014c36#.or8lsjkpb. I would recommend to break the exercises according to what C. Behrens recommends.
  8. (Back to the book) I would suggest to finish the Ch 6: Adding SVG, Making a Barchart and Making a Scatterplot

Other chapters (7 and 8) are about adding components and styling the just-created charts, but I think that those 8 points given above would cover the basis. I think they could be split in 3-5 exercises (average) per point, but I would emphasis on point 6 and more specially point 7, which is one of the most difficult things to understand, IMO.

Working with transitions and layouts (Ch 9 and beyond) is important but better if you master the Enter-Update-Exit cycle and understand selections and appends FIRST. Also, the selection can add hard-to-debug errors (silent ones) that would affect the whole cycle. What I haven't seen in practice but not in tutorials is the impact of wrongly coding selections and appends. Sometimes the chaining nature of JS could play you a trick. I would recommend to find and include 1 to 3 exercises working with selections that involve the SVG group element in ways that are prone to error and try to debug. The group element is an important one that could be tricky to handle.

Also, the book was very helpful in suggesting ideas of how to debug d3.js but there is no specific chapter.

erictleung commented 8 years ago

Some advice from a professor teaching D3.js to her journalism class that may help develop this portion of the curriculum http://blogger.ghostweather.com/2016/01/teaching-semester-of-d3js.html

evaristoc commented 8 years ago

Possible Related Exercises (TENTATIVE LIST):

A first encounter with d3.js: Ch 5. Exercises:

[] d3 select:

Select ONE paragraph element and add the following text "This is a paragraph!"

More about selection; using data. Ch 5. Exercises:

[] d3 selectAll and data:

Select SEVERAL paragraph elements and take the previous exercise but write "This is paragraph!" for 5 paragraph elements using the following list [ 5, 10, 15, 20, 25 ]

[] d3 accessing data:

Take the previous list but write "This is the value x" for 5 paragraph elements using the following list [ 5, 10, 15, 20, 25 ]; substitute x by the value of the list using an anonymous function when writing the text

Using data to draw things. Ch 6. Exercises:

[] d3 modifying attributes:

Take the following css bar

div.bar {
  display: inline-block;
  width: 20px;
  height: 75px;   /* We'll override height later */
  background-color: teal;
}

make a selectAll for bar class, and modify its height with the list from previous exercise

[] d3 - just testing a new list:

Use previous exercise but use this list instead: [ 25, 7, 5, 26, 11, 8, 25, 14, 23, 19, 14, 11, 22, 29, 11, 13, 12, 17, 18, 10, 24, 18, 25, 9, 3 ]

Drawing in svg. Ch 6. Exercises:

[] d3 working with svg:

Select/Append a non-existing svg; use the following width and height = [400,400] as svg attributes

[] d3 appending circles:

Select/Append several svg circle to the svg above, using the list above to add attribute radius. Use the following function:

circles.attr("cx", function(d, i) {
          return (i * 50) + 25;
      })
     .attr("cy", h/2)
     .attr("r", function(d) {
          return d;
     });

to spread the position of those circles

Drawing in svg. Ch 6. Exercises (cont.):

[] d3 creating bars with rect:

Use the same list to draw bars: use shape rect instead

[] d3 more about working with rect to create bars:

Correct spacing between bars functionally using padding attritute of the rectshape using the following .attr("width", w / dataset.length - barPadding)

[] d3 understanding svg coordinates:

Correct bar direction by modifying the height and y attributes of the selected rect shapes accordingly; Remember: svg coordinates start at x=0, y=0 at the top-left and it is always positive through out the whole svg area

[] d3 attribute color:

Color you bars: makes them all look blue!

Enter-Update-Exit cycle. medium article.

[] d3 preparing for Enter-Update-Exit:

Take your previous bars, but only for the following list [15, 8, 42, 4]

[] d3 preparing for Update:

Build an update function: add all the functionality but the list into an update function: function update(){...} and then call the function

[] d3 Enter:

Substitute the list by adding a value: [15, 8, 42, 4, 32], and add and enter() step to your update function

[] d3 Using event for Update:

Add an event to the update function and see what happens:

.on(“click”, function(e, i){
  numbers.splice(i, 1);
  update();
})

[] d3 Exit:

Now add the following line at the end of your update function and see what happens:

selection.exit().remove();
evaristoc commented 8 years ago

@QuincyLarson : FYI I have already mentioned in a post in the CT area and the DSR that d3.js v4 is already launched and it is a bit different (apparently easier) than the usual version, 3.x.

evaristoc commented 8 years ago

@QuincyLarson I think the only way to test if the exercises are correct is by evaluating that the tags, elements and attributes are correct, so it is about checking the html for having an specific structure. Exercises should be then VERY specific to avoid correct behaviours but incorrect html structures. I see this in general as a parsing of the html and comparing against a template html.

QuincyLarson commented 8 years ago

@evaristoc yes - we should definitely focus on d3.js v4.

I agree with you on making sure that the exercises are very specific. All challenges should basically ask the camper to do exactly one task. The less ambiguous, the better (as ambiguity tends to make people get frustrated and quit - campers can cope with ambiguity when they reach the projects).

cdikibo commented 8 years ago

My Proposal for D3 Waypoints

This might be outdated with the new version of D3. But here's the original post in its entirety.
I imagine that the D3 Waypoints will be very similar to the jQuery waypoints structure wise. Meaning there will be HTML/CSS/JS breakdown on the page.

SVG

  1. We have to start out with the svg. Making it an variable along with variables for height, width, our dataset(a simple array) and padding
  2. [ ] d3.select()
    • Have a waypoint choosing a specific html element, i.e. "body", "#visual"
  3. [ ] append()
    • Have a waypoint to attach the "svg" to the html element picked earlier
  4. [ ] attr()

    • Have waypoint(s) to add the height and width
    • [ ] selectAll()
    • [ ] data()
    • [ ] enter()
    • [ ] exit()
    • [ ] style()
    • [ ] text()

    Axis

    • [ ] d3.scale.linear()
    • [ ] domain()
    • [ ] extent()
    • [ ] min()
    • [ ] max()
    • [ ] range()

Axis Control

Dealing with (external) Data

evaristoc commented 8 years ago

@cdikibo Sounds a very comprehensive lists. I will let you lead the process, as I cannot take ownership of the activities related to this project. Please count on my support, I will be helping this section.

Hope the exercises I am proposing fit your list?

Quincy wrote to me:

if you could come up with titles for these challenges (in addition to your descriptions) and the sequence in which you think we should teach htem, that would be really helpfull.

I would say, @cdikibo: [x] Sequence: as suggested in my list [x] Adding names to each exercise

Expected Outcomes:

See guidelines given by Quincy at the beginning of this thread

Examples of expected outcome for the extension program:

See https://github.com/FreeCodeCamp/CurriculumExpansion/issues/10#issuecomment-230450274, https://github.com/FreeCodeCamp/CurriculumExpansion/issues/6#issue-163615115

The closer to this type of outcome, the better

cdikibo commented 8 years ago

I'll take ownership of these challenges. I'll have another list of potential exercises and their names coming out soon.

QuincyLarson commented 8 years ago

@cdikibo Thank you for taking ownership of these challenges. I will be around https://gitter.im/freecodecamp/curriculumdevelopment all weekend helping coordinate this. Let me know if I can help with anything 😄

QuincyLarson commented 8 years ago

@cdikibo Have you had a chance to work on that list of potential exercises? Please let me know whether I can do anything to help.

ghost commented 8 years ago

@cdikibo any update on this? These challenges are one of our highest priority and we'd love to get these shipped!

cc/ @QuincyLarson

QuincyLarson commented 8 years ago

@atjonathan I think we have another candidate for a topic that need an owner. I don't know what happened to @cdikibo - I hope she is OK.

alayek commented 8 years ago

@QuincyLarson I think @elibei might be interested in contributing as well.

QuincyLarson commented 8 years ago

@alayek yes, thanks for this suggestion :)

@elibei just finished a nonprofit project. I've reached out on Gitter to see whether @elibei is up for taking over these D3 challenges.

alayek commented 8 years ago

Adding elements with D3

Challenge Text

D3 JS lets you add, edit, or delete documents based on data. In this exercise, we shall start with adding a simple text in the page; using D3.

The select() function lets you pick the first element that matches the input string.

const anchor = d3.select("a");

It returns an HTML node, on which you can use even binding functions.

Similarly, there are append() and text() functions.

The append() function appends a HTML node, and returns a handle to the node

The text() function either sets the text of the selected node, or gets the current text. To set the value, you would have to pass a string as argument.

Challenge Instructions

Add the text "Learning D3" in the empty webpage. Target the body node using D3 select and append a <h1> tag, whose text is the desired string.

Note

You can chain three functions mentioned above. Also, use string to select tags, not HTML notation. For example, if you want to append or select <h2>, use "h2".

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    /* Add your code below */

    /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has h2
// Check if h2 has the text "Learning D3"
// Check if the code uses d3.select().append().text()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    d3.select("body").append("h1").text("Learning D3 Again!");
</script>
</body>
</html>
alayek commented 8 years ago

Working with Data

Challenge Text

As stated earlier, D3 JS focuses on data-driven approach. So, let's start by working with an array of data.

We want to display some things on the webpage based on the data at hand.

To make D3 aware of the data we have, we have to use the data() function and pass our array into the function.

We would also need to use enter() function, which is used to create missing elements in-memory before rendering.

Say, you want to create 5 lines of texts, based on 5 entries of the array. Then you need to call enter() on the selection, before proceeding to creating the text elements.

Helpful links

Challenge Instructions

Target the body node using D3 select and append as many <h1> tags as many are there elements in the array

Note

You can chain the functions mentioned above. Also, use string to select tags, not HTML notation. For example, if you want to append or select <h2>, use "h2".

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */

    /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */
    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h1")
        .text('New Title');
    /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Dynamic Data

Challenge Text

We have learned the tools to display dynamic data.

Let's start with displaying array elements.

We have already learned about data() function and enter(); to pass an array to D3 and let it display some text according to the number of data elements present.

But now it's time we display the actual data, no cloak-and-dagger about it. We need to modify the text() function we had used earlier.

The text() function takes a callback, where you can simply return the argument to callback to display the data. Obviously, the argument to callback is an invidual data-point itself.

selection.text((d) => d)

Helpful links

Challenge Instructions

Display the content of the array, and append "USD" (with a space; like "10 USD") to each of them. Use h1 to hold each node.

Note

You can chain the functions mentioned above. Also, use string to select tags, not HTML notation. For example, if you want to append or select <h2>, use "h2".

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */

    /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */
    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h1")
        .text((d) => d + " USD ");
    /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Styling your elements

Challenge Text

We can use style() function on any selection in D3, to give the rendered value an inline style.

The input to the style() function can be a comma-separated key value pair.

selection.style("color","blue");

Helpful links

Challenge Instructions

Turn all the displayed text into verdana font.

Note

Use font-family property.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h2")
        .text((d) => ("Value : " + d))
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h2")
        .text((d) => ("Value : " + d))
        /* Add your code below */
        .style('font-family', 'verdana')
        /* Add your code above */
</script>
</body>
</html>
evaristoc commented 8 years ago

@alayek can you please test if my code is correct when using ES6? Not installed in my case..

This exercise should be broken in different stages though...

d3 modifying attributes or styling elements???

-- Challenge Text

Change attributes of bars created using css attributes on div elements

//Someone else can work on this please?

-- Helpful links

//Someone else can work on this please?

-- Challenge Instructions

//Someone else can work on this please?

-- Challenge Seed

--- Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
div.bar {
  display: inline-block;
  width: 20px;
  height: 75px;   /* We'll override height later */
  background-color: teal;
}
</style>
</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */

    /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO //Someone else please?

-- Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
div.bar {
  display: inline-block;
  width: 20px;
  height: 75px;   /* We'll override height later */
  background-color: teal;
}
</style>
</head>
<body>
<script type="text/javascript">
    var dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5]
    /* Add your code below */
    d3.select("body").selectAll(".bar")
        .data(dataset)
        .enter()
        .append("div")
        .attr("class", "bar")
        .style("height", (d) =>  (d + "px"));
    /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Logic in your style

Challenge Text

Since D3 is all about visualization and presentation of data, it is expected that, you would need to provide some logic in your styling

In this challenge, we would render styling conditionally on each data element.

Say, we would want to color each data point blue, if they are less than 20; else red.

We can pass this as an if-else logic into the style() function. It accepts a callback, which has each of the data point as its argument.

selection.style("color", (d) => {
    /* Some logic that returns conditional value of color */
});

This can be used with other properties as well.

Helpful links

Challenge Instructions

Write the callback function that returns proper styling - for data element value less than 20, returns `"color",

Note

You can also use ternary operator.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h1")
        .text((d) => ("Value : " + d))
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("h1")
        .data(dataset)
        .enter()
        .append("h2")
        .text((d) => ("Value : " + d))
        /* Add your code below */
        .style("color", d => d >= 20 ? "red" : "blue");
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Adding classes using D3

Challenge Text

It's not always a good idea to keep adding inline styles in HTML elements. It could get cumbersome for even smaller apps.

What we would ideally want to do, is to add CSS class es to HTML nodes, with attr(); maybe even conditionally.

But keeping the actual styling better left inside the CSS classes.

We can add a class by using "class" property inside attr().

selection.attr("class", "container");

This can be used with other properties as well.

Helpful links

Challenge Instructions

Add the CSS class bar to the selection using attr()

Note

Write code only inside where you are asked to add your code.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        /* Add your code below */
        .attr("class", "bar");
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Dynamically updating height

Challenge Text

Now that we know how to display data from an array, and how to add CSS classes, we are finally at a place where we can use both of these tricks and display a bar chart!

There are two phases to this:

  • Create a div for each of the data points in the array.
  • Give each of these divs a dynamic height, using style(), that matches their value.

Helpful links

Challenge Instructions

After calling enter(), use append() to add a <div> for each of the data points. Then use attr() to add a CSS class bar to each of the elements.

Finally, add dynamic height using style() and a callback function; which returns the value of the data point and a "px" string added to it.

Note

Write code only inside where you are asked to add your code.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        .attr("class", "bar")
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        .attr("class", "bar")
        /* Add your code below */
        .style("height", (d) => d + "px")
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Cleaning up bars

Challenge Text

We have a bar chart, except it could be better.

There are two phases to doing this:

  • Make each of the bar separate from each other, instead of being coalesced together. This can be obtained by adding margin to the CSS of .bar class.
  • Present each of the bars distinctly from each other. This can be done by multiplying the value to get the height.

Helpful links

Challenge Instructions

Add a margin of 3px to the .bar class in CSS styling.

The callback that sets the height, should return a value 10 times the original data point value.

Note

Write code only inside where you are asked to add your code.

Multiplying by the same constant only alters the scale. Kinda like zooming in. It doesn't alter any data.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        /* Add code only below this line */

        /* Add code only above this line */
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        .attr("class", "bar")
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    d3.select("body").selectAll("div")
        .data(dataset)
        .enter()
        .append("div")
        .attr("class", "bar")
        /* Add your code below */
        .style("height", (d) => 10 * d + "px")
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Using SVG

Challenge Text

SVG stands for Scalable Vector Graphics.

Scalable in this context means that an SVG media object would not appear pixelated when you zoom in or zoom out. It would scale with the display system the right way, whether it's Apple Watch or a large screen TV monitor.

SVG has great support for common geometric shapes.

Since D3 is about presenting data and helping you visualize; it's important that we also use SVG whenever possible.

CSS can be made scalable too, by using relative parameters (vh or vw; and percentages); but SVG is much more easy to use.

Helpful links

Challenge Instructions

Add an <svg> node using append(), and give it a width attribute of 500 and height attribute of 100

Note

Note that width and height attributes have no units. Just numbers.

This is the building block of scaling, because the element will always have a 5:1 width to height ratio, no matter what the zoom level is.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
        /* Add your code below */

        /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
        /* Add your code below */
                .append("svg")
                .attr("width", w)
                .attr("height", h);
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Displaying a bar using SVG

Challenge Text

We have created an SVG element so far, with a given width and height.

But it's not showing, right?

SVG only created the space of given width and height.

We have to render a rectangle <rect> SVG shape to actually display something, like, say a bar.

Helpful links

Challenge Instructions

Add a <rect> SVG shape using append(), and give it a width attribute of 25 and height attribute of 100. Also, set x and y attribute to 0 each.

Note

Read up on different attributes of <rect> from the documentation on MDN and other sources.

You might need to use select() from earlier, because <rect> would be added inside a <svg> node. Also don't forget to use data() and enter().

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
                    /* Add your code below */

                    /* Add your code above */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);
        /* Add your code below */
        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", 0)
           .attr("y", 0)
           .attr("width", 25)
           .attr("height", 100);
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Add spacing between the bars using SVG

Challenge Text

We have created an SVG Rect element, next we need to make lot of rectangles like this.

Actually, not a lot. To be precise, exactly the number of rectangles as there are data points in the array.

However, we need to find a way to place each of the rectangles.

The placement of a rectangle is handled by x and y attributes. Earlier we set them to 0.

Now, we want all the bars to be in the same vertical level (y stays 0), but as we iterate over array elements, the x value should increase.

Remember attr() in D3 accepts a callback function to dynamically set the attribute specfied? This callback takes two arguments (second one is optional).

selection.attr('property', (d, i) => {
       /* 
       * d is the data point value
       * i is the index of the data point in the array
       */
})

Helpful links

Challenge Instructions

Add a dynamic origin (x attribute) for each of the bars, by making sure the first <rect> has x = 0, second <rect> has x = 30, third <rect> has x = 60, fourth <rect> has x = 90, and so on.

Note

We picked 30 as the distance of one <rect> from the next one. Ideally, any value greater than 25, the width of each of the <rect> would have worked.

Try to relate the distance as i varies. For instance, if i = 0 => x = 0, if i = 1, x = 30, if i = 2, x = 60 etc.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => {
               /* Add your code below */

               /* Add your code above */
           })
           .attr("y", 0)
           .attr("width", 25)
           .attr("height", 100);
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);
        /* Add your code below */
        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", 0)
           .attr("width", 25)
           .attr("height", 100);
        /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Changing heights of the bars with SVG

Challenge Text

We only need to alter the height of each bar according to the value of the data point, as given in the array.

Just like we set the x attribute earlier dynamically, we need to set theheight attribute of the <rect> node.

selection.attr('property', (d, i) => {
       /* 
       * d is the data point value
       * i is the index of the data point in the array
       */
})

Helpful links

Challenge Instructions

Set the height of the bar i using the value of d in the callback of attr(). It should be d * 3, instead of just d (for better distinction).

Note

We have already described why d * 3 is same as d when it comes to data visualization. In fact, the former one helps discern it better.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", 0)
           .attr("width", 25)
           .attr("height", (d, i) => {
               /* Add your code below */

               /* Add your code above */
           });
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", 0)
           .attr("width", 25)
           /* Add your code below */
           .attr("height", (d, i) => d * 3);
           /* Add your code above */
</script>
</body>
</html>
alayek commented 8 years ago

Flipping the bars

Challenge Text

We are almost there, except our bar chart is inverted!

This is because of how SVG parses (x, y) coordinate information (not because a demogorgon took you into upside-down)

In SVG, a <rect> shape has its origin at top-left corner. x increases toward the right. So, if you increase x, the rectangle moves toward the right.

But if you increase y, it goes down, instead of going up.

We have to keep the height propertional to the data point's value d. So, to fix this, we would have to decrease y as the value increases.

When d=0, y should be the height of SVG node, h. When d is greater than 0, y is less than h.

A relationship like y = h - d would work great!

Helpful links

Challenge Instructions

Set the y attribute of the bar i using the value of d in the callback of attr("y"). Don't return h - d; instead, take into account that d is being multplied by a factor of 3 to obtain the height.

Note

In general, the relationship is y = h - m * d, where m is a multiplying factor

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => {
                /* Add code below this line */

                /* Add code above this line */
           })
           .attr("width", 25)
           .attr("height", (d, i) => d * 3);
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3);
</script>
</body>
</html>
alayek commented 8 years ago

Coloring the bars

Challenge Text

We finally got the bars right, but they are all monochromatic, filled with black color.

At the very least, SVG should let us change the color of the bars.

In SVG, a <rect> shape has its color defined by fill attribute. You can obviously do more complex, visually appealing stuff with gradient and transperancy.

Helpful links

Challenge Instructions

Set the fill attribute of all the bars to color navy.

Note You can also set Hexadecimal colors.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           /* Add code below this line */

           /* Add code above this line */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           /* Add code below this line */
           .attr("fill", "navy");
           /* Add code above this line */
</script>
</body>
</html>
alayek commented 8 years ago

Adding Labels

Challenge Text

We can label a graph element, such as a bar, in D3.

We will be using SVG element, <text>. Like <rect> element, a <text> element needs to have x and y attributes, for placing it on the SVG canvas.

This exercise is to demonstrate the level of control D3 gives you, to label your bars.

We would add the value of each bar on top of each bar, with a 3-unit distance (we left pixel-land when we started with SVG) from corresponding bar.

Helpful links

Challenge Instructions

Create <text> node, exactly like we created <rect> nodes.

Pass the dataset into them, call enter() and then place them on top of each bar, with a 3 unit distance from each bar.

You can use text() function from D3 to set the text. Note Their x and y attribute would have same value as the bar at location i. Only difference would be in the value of y.

Decide if the value of y would be greater than the value of y, or less. Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy");

   /* Add code below this line */

   /* Add code above this line */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy");

        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3));
</script>
</body>
</html>
alayek commented 8 years ago

Styling Labels

Challenge Text

Just as we can label a bar, we can also style the label.

We will set the color and font-size of the text in this exercise

The color of the text, is dictated by fill attribute of the <text> node. The font-size is governed by, you guessed it right, font-size attribute.

Helpful links

Challenge Instructions

Set the font-size to 25px, and color of the text to red

Note

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy");

    svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   
           /* Add code below this line */

           /* Add code above this line */
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar {
        width: 25px;
        height: 100px;
        margin: 3px;
        display: inline-block;
        background-color: blue;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy");

        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   
           /* Add code below this line */
           .attr("font-size", "25px")
           .attr("fill", "red");
           /* Add code above this line */
</script>
</body>
</html>
alayek commented 8 years ago

Adding Hover Effect

Challenge Text

Just because we are using SVG, doesn't mean we cannot use CSS!

We can set a CSS class using class attribute on an SVG element.

In this exercise, we will set a CSS class, that creates some color change effect on mouse hover.

Helpful links

Challenge Instructions

Set the CSS class of all <rect> nodes to bar

Note You might need to use attr() function from D3. Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy")
           /* Add code below this line */

           /* Add code above this line */

    svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy")
           /* Add code below this line */
           .attr("class", "bar");
           /* Add code above this line */

        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   
           .attr("font-size", "25px")
           .attr("fill", "red");
</script>
</body>
</html>
alayek commented 8 years ago

Adding Tooltip

Challenge Text

We can add a tooltip to each of the bars using <title> node under each <rect>

<title> is another SVG element, commonly used for tool-tip, which can be set by calling text() on the <title> node after creation.

Helpful links

Challenge Instructions

Create <title> element under each <rect> node, and call text() function on it, to set the text to each data value.

Note You might need to use callback function to return the data point value d. Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

    const w = 500;
    const h = 100;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy")
           attr("class", "bar")
           /* Add code below this line */

           /* Add code above this line */

    svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [ 5, 10, 15, 20, 25, 20, 15, 10, 5];

        const w = 500;
        const h = 100;

        const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

        svg.selectAll("rect")
           .data(dataset)
           .enter()
           .append("rect")
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - 3 * d )
           .attr("width", 25)
           .attr("height", (d, i) => d * 3)
           .attr("fill", "navy")
           /* Add code below this line */
           .append('title')
           .text((d) => d);
           /* Add code above this line */

        svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text((d) => d)
           .attr("x", (d, i) => i * 30)
           .attr("y", (d, i) => h - (d * 3 + 3))   
           .attr("font-size", "25px")
           .attr("fill", "red");
</script>
</body>
</html>
alayek commented 8 years ago

Creating Circles

Challenge Text

Scatter plots are consisted of small circles.

SVG lets you create <circle> shape, just like it supports <rect> shape.

To draw a circle in SVG, you need to pass it the position of its center (cx and cy attributes), and radius (r attribute).

As in <rect>, the attribute cy is measured from the top boundary of SVG canvas, not bottom.

Helpful links

Challenge Instructions

Create <circle> elements that correspond to the data points provided in the array. Each data point contains value of cx, cy (in that order).

All circle should have radius of 5.

Note Don't forget to use selectAll(), data(), enter(). Also, remember to flip the scatter plot, by passing proper value in attr("cy")

You don't have to scale the value either, using some multiplicative factor; like we did for bar chart. Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

   /* Add code below this line */

   /* Add code above this line */

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

   /* Add code below this line */
       svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d, i) => d[0])
               .attr("cy", (d, i) => h - d[1])
               .attr("r", (d, i) => 5); 
   /* Add code above this line */

</script>
</body>
</html>
alayek commented 8 years ago

Labeling Circles

Challenge Text

Like bar charts, we would like to add text against all points as well.

You need to display comma separated value for x and y field of each entry in the 2D array.

We can keep the text at the same height as cy attribute of each point.

But to display it to the right of the point on scatter plot, we should pass x attribute of the text to be greater than cx attribute by some constant.

Helpful links

Challenge Instructions

Label each point on the scatter plot by adding text at (cx + 5, cy) position.

Note A circle which is vertically below another height, should have lesser value in the y coordinate.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);

    svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d, i) => d[0])
               .attr("cy", (d, i) => h - d[1])
               .attr("r", (d, i) => 5);    
    /* Add code below this line */

        /* Add code above this line */

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
    svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d, i) => d[0])
               .attr("cy", (d, i) => h - d[1])
               .attr("r", (d, i) => 5);

   /* Add code below this line */
   svg.selectAll("text")
               .data(dataset)
               .enter()
               .append("text")
               .text((d) => (d[0] + "," + d[1]))
               .attr("x", (d) => (d[0] + 5))
               .attr("y", (d) => (h - d[1]));
   /* Add code above this line */

</script>
</body>
</html>
alayek commented 8 years ago

Creating a Scale

Challenge Text

We have seen how to create simple bar chart and scatter plots.

Now, we are going to take a look at something more fundamental; yet necessary for any plotting - Scale

Often, you are required to plot a set of values at a given scale. For instance, if you are plotting various countries' GDP using plot, you have to divide the absolute GDP by a few Billions, if not trillions; and keep only the relevant figures.

You would almost never plot raw data as-is. Which is why, before even plotting, you have to set the scale for your entire data set, so that the x and y values can fit your canvas width and height.

To create a simple linear scale, use D3 function scaleLinear().

 const scale = d3.scaleLinear()`

This creates a simple scale.

Helpful links

Challenge Instructions

Create a scale and call it with input argument 50.

Note Guess the scaling factor scaleLinear() sets by default.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    /* Add code below this line */
        const scale = undefined // create scale here
        const output = scale(); // invoke with argument 50
    /* Add code above this line */

    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    /* Add code below this line */
        const scale = d3.scaleLinear() // create scale here
        const output = scale(50); // invoke with argument 50
    /* Add code above this line */

    d3.select('body')
      .append('h2')
      .text(output)

</script>
</body>
</html>
alayek commented 8 years ago

Setting Domain and Range

Challenge Text

The previous scale was identity relationship - we want something more interesting.

Say the x-coordinate, or domain, might vary between 50 and 480. Meaning we have data available only for those points.

The y-coordinate, or the range, varies between 10 and 500 within that interval of x-coordinates.

We can express this using domain() and range() of the scale.

We would want to set this, because it would help set axis later.

// Let's set a domain
// The domain is the possible input values
scale.domain([50, 480]);

// And now a  range
// The range of possible output values
scale.range([10, 500]);

scale(50) //returns 10
scale(480) // returns 500
scale(325) // returns 323.37209302325584
scale(750) // returns 807.6744186046511

Helpful links

Challenge Instructions

Create a scale and set it with domain [250, 500] and range [10, 150].

Note You can totally chain these functions!

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    /* Add code below this line */
        const scale = d3.scaleLinear() // create scale here

    /* Add code above this line */
    const output = scale(50);
    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    /* Add code below this line */
        const scale = d3.scaleLinear() // create scale here
                        .domain([250, 500])
                        .range([10, 150])
    /* Add code above this line */
    const output = scale(50); // -102
    d3.select('body')
      .append('h2')
      .text(output)    
</script>
</body>
</html>
alayek commented 8 years ago

d3.max() and d3.min()

Challenge Text

Now that we know how we can use D3 functions to set the domain and range of our data, it's time to look at some utilities that makes that easy for us.

To set domain and range, we need to know some minimum and maximum present within the data given. It's error prone to do that manually, especially if the dataset is large.

D3 gives you two functions - d3.max() and d3.min() to extract these information.

const exampleData = [34, 234, 73, 90, 6, 52];
d3.min(exampleData) // returns 6
d3.max(exampleData) // returns 234

But a dataset might have nested arrays, like [x,y] coordinate pair we used in scatter plot.

In that case, we need to tell D3 how we want our maxiumum and minimum to be computed.

This is achieved by passing a callback in max() or min() function.

This callback's argument is current inner array. The callback needs to return the element from the inner array over which you want to compute the maximum or minimum.

const locationData = [[1,7],[6,3],[8,3]];

// This is how you make it work with an array of arrays
// this returns the smallest number of the first element
const minX = d3.min(locationData, (d) => d[0]);

Helpful links

Challenge Instructions

Compute the maximum of z-coordinate from the 3D array; where the 3rd value in coordinate is the z-index.

Note D3 can totally plot 3D arrays. Where do you think the 3 in D3 comes from!

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const positionData = [[1, 7, -4],[6, 3, 8],[2, 8,3]]
    /* Add code below this line */
    const output = undefined; // change this line       
    /* Add code above this line */

    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const positionData = [[1, 7, -4],[6, 3, 8],[2, 8,3]]
    /* Add code below this line */
    const output = d3.max(positionData, (d) => d[2])    
    /* Add code above this line */

    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>
alayek commented 8 years ago

Dynamic Scales

Challenge Text

We can use the D3 min and max utility to set the scale properly.

Given a conplex data-set, we would want to first scale it to fit the SVG container's width and height.

We would even want to have some padding, so that the axes have some breathing room from the boundary of the SVG canvas.

Let's see an example of how to set the X-axis scale for a typical scatter plot.

const dataset = [
                [ 34,     78 ],
                [ 109,   280 ],
                [ 310,   120 ],
                [ 79,   411 ],
                [ 420,   220 ],
                [ 233,   145 ],
                [ 333,   96 ],
                [ 222,    333 ],
                [ 78,    320 ],
                [ 21,   123 ]
            ];
const w = 500;
const h = 500;

// padding from SVG box boundary
const padding = 30;

const xScale = d3.scaleLinear()
                  .domain([0, d3.max(dataset, (d)=> d[0])])
                  .range([padding, w - padding ]);

The last line of code requires some understanding. The domain setting is straightforward. It's setting the minimum of available values of x-coordinate to 0, and maximum is computed by D3 max() utility.

The range() of values the x-values should map to - to fit within the width of SVG container, is set to be between padding and w - padding.

If you have trouble understanding this part, you might want to take a piece of paper and a pen; draw a line starting at 0, ending at w.

Now mark off padding from both ends and see the length available to place all the x-values.

The 0 in your X axis data would match to padding mark; and xMax would match to w - padding mark.

Helpful links

Challenge Instructions

Compute the yScale using similar logic. Assume minimum possible value in y-domain is 0.

Note We don't want to do flipping later. So, when setting the range for Y coordinates, you can use the higher value first, and lower value second. This would flip all Y-data by default. Essentially, scaling with a negative factor.

To help you think, draw a line, this time vertically; and see where yMin and yMax would match.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    // padding from SVG canvas borders
    const padding = 30;

    // create an x and y scale

    const xScale = d3.scaleLinear()
                    .domain([0, d3.max(dataset, (d) => d[0])])
                    .range([padding, w - padding]);

    /* Add code below this line */
    const yScale = undefined;
    /* Add code above this line */
    const output = yScale(411); // returns 30
    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;

    // padding from SVG canvas borders
    const padding = 30;

    // create an x and y scale

    const xScale = d3.scaleLinear()
                    .domain([0, d3.max(dataset, (d) => d[0])])
                    .range([padding, w - padding]);

    /* Add code below this line */
    const yScale = d3.scaleLinear()
                    .domain([0, d3.max(dataset, (d) => d[1])])
                    .range([h - padding, padding]);
    /* Add code above this line */

    const output = yScale(411); // returns 30
    d3.select('body')
      .append('h2')
      .text(output)
</script>
</body>
</html>
alayek commented 8 years ago

Using pre-defined scales

Challenge Text

Now that we have set up our scale, we are ready to plot the scatter plot once again.

This time, we will use the scales we have set up.

This would keep our data within proper bounds. We can use our scale as a processing function that returns processed value for rendering.

shape
   .attr("x", (d) => xScale(d[0]))

To set any attribute value inside any shape (<rect>, <text>, or <circle>), we pass it via the scaling function.

However, if it doesn't need to be geometrically rendered, but only displayed as text or tooltip; we can use the raw value without scaling.

Helpful links

Challenge Instructions

Use scaling to render values after converting them properly. In particular, render circles and text right to them.

All circle must have a radius of 5 units. The offset in x coordinate for text would be 10 units.

Text should display raw value. Note No flipping here!

Our scaling already took care of that.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;
    const padding = 60;

    const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[0])])
                .range([padding, w - padding]);

    const yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[1])])
                .range([h - padding, padding]);

    const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);                     

    // update the code to use the scale

    // Add code below
    // 
    // Add code above

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;
    const padding = 60;

    const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[0])])
                .range([padding, w - padding]);

    const yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[1])])
                .range([h - padding, padding]);

    const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);                     

    // update the code to use the scale

    // Add code below
    svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d) => xScale(d[0]))
               .attr("cy",(d) => yScale(d[1]))
               .attr("r", (d) => 5)

    svg.selectAll("text")
               .data(dataset)
               .enter()
               .append("text")
               .text((d) =>  (d[0] + "," + d[1]))
               .attr("x", (d) => xScale(d[0] + 10))
               .attr("y", (d) => yScale(d[1]))

    // Add code above          

</script>
</body>
</html>
alayek commented 8 years ago

Adding axes

Challenge Text

There's only one thing missing from our plot to make it look all professional - Axes

Axes is plural for Axis. In particular, we would want an x-axis, and an y-axis.

Let's use D3's utility functions axisLeft and axisBottom to render Y and X axis, respectively.

const xAxis = d3.axisBottom(xScale);

This creates an xAxis. We still need to render this.

To render an axis, we shall use the most general SVG component, the g element.

Unlike rect and circle and text; we are rendering just a stright line, when rendering an axis. So, using g suffices.

However, this requires a little bit of transform offset, because otherwise the line would be rendered along the border of SVG canvas and won't be visible at all.

svg.append("g")
  .attr("transform", "translate(0, y)")
  .call(xAxis);

The above renders an X-axis, which is passed as an argument to the call() function.

Similarly, Y-axis can be rendered, except the translate argument is of the form (x, 0)

Helpful links

  • D3 Axis API
  • The <g> element Challenge Instructions Add the x and y axis, then render them properly. The padding should be offset for both the axes - x-axis must be at a height (y translation) of padding from bottom border of the SVG canvas.

The y-axis must be at the right of leftmost border of SVG canvas by padding amount of x-axis translation.

Text should display raw value.

Note The translate argument should be a string. So generate a string using concatenation and value of padding.

Focus on what would be the y-translation of the x-axis. This value is the final value on the coordinate axis; not the amount of movement or translation.

Challenge Seed

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;
    const padding = 60;

    const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[0])])
                .range([padding, w - padding]);

    const yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[1])])
                .range([h - padding, padding]);

    const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);                     

    // update the code to use the scale
    svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d) => xScale(d[0]))
               .attr("cy",(d) => yScale(d[1]))
               .attr("r", (d) => 5)

    svg.selectAll("text")
               .data(dataset)
               .enter()
               .append("text")
               .text((d) =>  (d[0] + "," + d[1]))
               .attr("x", (d) => xScale(d[0] + 10))
               .attr("y", (d) => yScale(d[1]))

    // Add code below

    // Add code above

</script>
</body>
</html>

Challenge Tests

// TODO
// Check if body has 9 h1 tags
// Check if h1 has the text "New Title"
// Check if the code uses enter()

Challenge Solution

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 </title>
<style>
    .bar:hover {
      fill: brown;
    }
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

</head>
<body>
<script type="text/javascript">
    const dataset = [
                  [ 34,     78 ],
                  [ 109,   280 ],
                  [ 310,   120 ],
                  [ 79,   411 ],
                  [ 420,   220 ],
                  [ 233,   145 ],
                  [ 333,   96 ],
                  [ 222,    333 ],
                  [ 78,    320 ],
                  [ 21,   123 ]
              ];

    const w = 500;
    const h = 500;
    const padding = 60;

    const xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[0])])
                .range([padding, w - padding]);

    const yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, (d) => d[1])])
                .range([h - padding, padding]);

    const svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);                     

    // update the code to use the scale

    svg.selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("cx", (d) => xScale(d[0]))
               .attr("cy",(d) => yScale(d[1]))
               .attr("r", (d) => 5)

    svg.selectAll("text")
               .data(dataset)
               .enter()
               .append("text")
               .text((d) =>  (d[0] + "," + d[1]))
               .attr("x", (d) => xScale(d[0] + 10))
               .attr("y", (d) => yScale(d[1]))
    // Add code below

    svg.append("g")
        .attr("transform", "translate(0," + (h - padding) + ")")
        .call(xAxis);

    svg.append("g")
        .attr("transform", "translate(" + padding + ",0)")
        .call(yAxis);

    // Add code above              

</script>
</body>
</html>
QuincyLarson commented 8 years ago

@alayek this is some incredible progress!

alayek commented 8 years ago

What is plotting

Challenge Text

Presenting data in its visual form helps the viewers easily get insight into how the data has changed over time, and if there is any visible trend.

In this section, we will get familiar with D3.js, that lets you easily plot data in a webpage and present to your viewers.

But before that, one needs to understand basics of plotting and coordinate geometry.

Helpful links

Challenge Instructions

Go check up the links provided to familiarize yourself with what a plot looks like.

Note

Plots are ubiquitos, even if you are not from statistics or data analysis background. So chances are, you have already encountered them.

Challenge Seed

//TODO : nothing to add

Challenge Tests

// TODO: nothing to add

Challenge Solution

QuincyLarson commented 8 years ago

@alayek keep in mind that these helpful links will only be included in the wiki article. When you write the description, assume that the camper will never actually read them (because very few campers tend to navigate to our wiki).

QuincyLarson commented 7 years ago

@alayek there are only 5 D3 challenges left to be designed. I've gone ahead and imported them into seed file on https://github.com/FreeCodeCamp/FreeCodeCamp/pull/10473

Finishing this is a lower priority than finishing React.

alayek commented 7 years ago

@QuincyLarson I need some guidance with respect to what the initial section would look like - the part where the basic geometry concepts are introduced.

It's not, strictly speaking, JavaScript. And I don't want to go full on Coordinate Geometry either - because that would scare off beginners.

Any idea what we could submit for challenges and tests, for the first part?

@elibei @t3h2mas FYI.

QuincyLarson commented 7 years ago

@alayek you might introduce the notion of the three axes by giving them an existing graph and asking them to plot some additional points at specific coordinates. I would introduce these simply as: Y - up and down X - left and right Z - toward the screen and away from the screen

QuincyLarson commented 7 years ago

@alayek as for translations, these may be beyond the scope of what we need to teach. If we do want to cover them, here's a basic explanation we could look at for inspiration: https://people.richland.edu/james/lecture/m116/functions/translations.html

QuincyLarson commented 7 years ago

@alayek @elibei This section is almost done. Do either of you have time to finish these remaining challenges this week?