diff --git a/04-data-visualization/data-visualization-with-d3.json b/04-data-visualization/data-visualization-with-d3.json index ebc0bed..e0d30ca 100644 --- a/04-data-visualization/data-visualization-with-d3.json +++ b/04-data-visualization/data-visualization-with-d3.json @@ -1,509 +1,509 @@ { - "name": "Data Visualization with D3", - "order": 1, - "time": "5 hours", - "helpRoom": "Help", - "challenges": [ - { - "id": "587d7fa6367417b2b2512bc2", - "title": "Add Document Elements with D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 has several methods that let you add and change elements in your document.", - "The select() method selects one element from the document. It takes an argument for the name of the element you want and returns an HTML node for the first element in the document that matches the name. Here's an example:", - "const anchor = d3.select(\"a\");", - "The above example finds the first anchor tag on the page and saves an HTML node for it in the variable anchor. You can use the selection with other methods. The \"d3\" part of the example is a reference to the D3 object, which is how you access D3 methods.", - "Two other useful methods are append() and text().", - "The append() method takes an argument for the element you want to add to the document. It appends an HTML node to a selected item, and returns a handle to that node.", - "The text() method either sets the text of the selected node, or gets the current text. To set the value, you pass a string as an argument inside the parentheses of the method.", - "Here's an example that selects an unordered list, appends a list item, and adds text:", - "
d3.select(\"ul\")
  .append(\"li\")
  .text(\"Very important item\");
", - "D3 allows you to chain several methods together with periods to perform a number of actions in a row.", - "
", - "Use the select method to select the body tag in the document. Then append an h1 tag to it, and add the text \"Learning D3\" into the h1 element." - ], - "tests": [ - { - "text": "The body should have one h1 element.", - "testString": "assert($('body').children('h1').length == 1, 'The body should have one h1 element.');" - }, - { - "text": "The h1 element should have the text \"Learning D3\" in it.", - "testString": "assert($('h1').text() == \"Learning D3\", 'The h1 element should have the text \"Learning D3\" in it.');" - }, - { - "text": "Your code should access the d3 object.", - "testString": "assert(code.match(/d3/g), 'Your code should access the d3 object.');" - }, - { - "text": "Your code should use the select method.", - "testString": "assert(code.match(/\\.select/g), 'Your code should use the select method.');" - }, - { - "text": "Your code should use the append method.", - "testString": "assert(code.match(/\\.append/g), 'Your code should use the append method.');" - }, - { - "text": "Your code should use the text method.", - "testString": "assert(code.match(/\\.text/g), 'Your code should use the text method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa6367417b2b2512bc3", - "title": "Select a Group of Elements with D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 also has the selectAll() method to select a group of elements. It returns an array of HTML nodes for all the items in the document that match the input string. Here's an example to select all the anchor tags in a document:", - "const anchors = d3.selectAll(\"a\");", - "Like the select() method, selectAll() supports method chaining, and you can use it with other methods.", - "
", - "Select all of the li tags in the document, and change their text to \"list item\" by chaining the .text() method." - ], - "tests": [ - { - "text": "There should be 3 li elements on the page, and the text in each one should say \"list item\". Capitalization and spacing should match exactly.", - "testString": "assert($('li').text().match(/list item/g).length == 3, 'There should be 3 li elements on the page, and the text in each one should say \"list item\". Capitalization and spacing should match exactly.');" - }, - { - "text": "Your code should access the d3 object.", - "testString": "assert(code.match(/d3/g), 'Your code should access the d3 object.');" - }, - { - "text": "Your code should use the selectAll method.", - "testString": "assert(code.match(/\\.selectAll/g), 'Your code should use the selectAll method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa7367417b2b2512bc4", - "title": "Work with Data in D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The D3 library focuses on a data-driven approach. When you have a set of data, you can apply D3 methods to display it on the page. Data comes in many formats, but this challenge uses a simple array of numbers.", - "The first step is to make D3 aware of the data. The data() method is used on a selection of DOM elements to attach the data to those elements. The data set is passed as an argument to the method.", - "A common workflow pattern is to create a new element in the document for each piece of data in the set. D3 has the enter() method for this purpose.", - "When enter() is combined with the data() method, it looks at the selected elements from the page and compares them to the number of data items in the set. If there are fewer elements than data items, it creates the missing elements.", - "Here is an example that selects a ul element and creates a new list item based on the number of entries in the array:", - "
<body>
  <ul></ul>
  <script>
    const dataset = [\"a\", \"b\", \"c\"];
    d3.select(\"ul\").selectAll(\"li\")
      .data(dataset)
      .enter()
      .append(\"li\")
      .text(\"New item\");
  </script>
</body>
", - "It may seem confusing to select elements that don't exist yet. This code is telling D3 to first select the ul on the page. Next, select all list items, which returns an empty selection. Then the data() method reviews the dataset and runs the following code three times, once for each item in the array. The enter() method sees there are no li elements on the page, but it needs 3 (one for each piece of data in dataset). New li elements are appended to the ul and have the text \"New item\".", - "
", - "Select the body node, then select all h2 elements. Have D3 create and append an h2 tag for each item in the dataset array. The text in the h2 should say \"New Title\". Your code should use the data() and enter() methods." - ], - "tests": [ - { - "text": "Your document should have 9 h2 elements.", - "testString": "assert($('h2').length == 9, 'Your document should have 9 h2 elements.');" - }, - { - "text": "The text in the h2 elements should say \"New Title\". The capitalization and spacing should match exactly.", - "testString": "assert($('h2').text().match(/New Title/g).length == 9, 'The text in the h2 elements should say \"New Title\". The capitalization and spacing should match exactly.');" - }, - { - "text": "Your code should use the data() method.", - "testString": "assert(code.match(/\\.data/g), 'Your code should use the data() method.');" - }, - { - "text": "Your code should use the enter() method.", - "testString": "assert(code.match(/\\.enter/g), 'Your code should use the enter() method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa7367417b2b2512bc5", - "title": "Work with Dynamic Data in D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The last two challenges cover the basics of displaying data dynamically with D3 using the data() and enter() methods. These methods take a data set and, together with the append() method, create a new DOM element for each entry in the data set.", - "In the previous challenge, you created a new h2 element for each item in the dataset array, but they all contained the same text, \"New Title\". This is because you have not made use of the data that is bound to each of the h2 elements.", - "The D3 text() method can take a string or a callback function as an argument:", - "selection.text((d) => d)", - "In the example above, the parameter d refers to a single entry in the dataset that a selection is bound to.", - "Using the current example as context, the first h2 element is bound to 12, the second h2 element is bound to 31, the third h2 element is bound to 22, and so on.", - "
", - "Change the text() method so that each h2 element displays the corresponding value from the dataset array with a single space and \"USD\". For example, the first heading should be \"12 USD\"." - ], - "tests": [ - { - "text": "The first h2 should have the text \"12 USD\".", - "testString": "assert($('h2').eq(0).text() == \"12 USD\", 'The first h2 should have the text \"12 USD\".');" - }, - { - "text": "The second h2 should have the text \"31 USD\".", - "testString": "assert($('h2').eq(1).text() == \"31 USD\", 'The second h2 should have the text \"31 USD\".');" - }, - { - "text": "The third h2 should have the text \"22 USD\".", - "testString": "assert($('h2').eq(2).text() == \"22 USD\", 'The third h2 should have the text \"22 USD\".');" - }, - { - "text": "The fourth h2 should have the text \"17 USD\".", - "testString": "assert($('h2').eq(3).text() == \"17 USD\", 'The fourth h2 should have the text \"17 USD\".');" - }, - { - "text": "The fifth h2 should have the text \"25 USD\".", - "testString": "assert($('h2').eq(4).text() == \"25 USD\", 'The fifth h2 should have the text \"25 USD\".');" - }, - { - "text": "The sixth h2 should have the text \"18 USD\".", - "testString": "assert($('h2').eq(5).text() == \"18 USD\", 'The sixth h2 should have the text \"18 USD\".');" - }, - { - "text": "The seventh h2 should have the text \"29 USD\".", - "testString": "assert($('h2').eq(6).text() == \"29 USD\", 'The seventh h2 should have the text \"29 USD\".');" - }, - { - "text": "The eighth h2 should have the text \"14 USD\".", - "testString": "assert($('h2').eq(7).text() == \"14 USD\", 'The eighth h2 should have the text \"14 USD\".');" - }, - { - "text": "The ninth h2 should have the text \"9 USD\".", - "testString": "assert($('h2').eq(8).text() == \"9 USD\", 'The ninth h2 should have the text \"9 USD\".');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa7367417b2b2512bc6", - "title": "Add Inline Styling to Elements", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 lets you add inline CSS styles on dynamic elements with the style() method.", - "The style() method takes a comma-separated key-value pair as an argument. Here's an example to set the selection's text color to blue:", - "selection.style(\"color\",\"blue\");", - "
", - "Add the style() method to the code in the editor to make all the displayed text have a font-family of verdana." - ], - "tests": [ - { - "text": "Your h2 elements should have a font-family of verdana.", - "testString": "assert($('h2').css('font-family') == 'verdana', 'Your h2 elements should have a font-family of verdana.');" - }, - { - "text": "Your code should use the style() method.", - "testString": "assert(code.match(/\\.style/g), 'Your code should use the style() method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa7367417b2b2512bc7", - "title": "Change Styles Based on Data", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 is about visualization and presentation of data. It's likely you'll want to change the styling of elements based on the data. You can use a callback function in the style() method to change the styling for different elements.", - "For example, you may want to color a data point blue if has a value less than 20, and red otherwise. You can use a callback function in the style() method and include the conditional logic. The callback function uses the d parameter to represent the data point:", - "
selection.style(\"color\", (d) => {
  /* Logic that returns the color based on a condition */
});
", - "The style() method is not limited to setting the color - it can be used with other CSS properties as well.", - "
", - "Add the style() method to the code in the editor to set the color of the h2 elements conditionally. Write the callback function so if the data value is less than 20, it returns \"red\", otherwise it returns \"green\".", - "Note
You can use if-else logic, or the ternary operator." - ], - "tests": [ - { - "text": "The first h2 should have a color of red.", - "testString": "assert($('h2').eq(0).css('color') == \"rgb(255, 0, 0)\", 'The first h2 should have a color of red.');" - }, - { - "text": "The second h2 should have a color of green.", - "testString": "assert($('h2').eq(1).css('color') == \"rgb(0, 128, 0)\", 'The second h2 should have a color of green.');" - }, - { - "text": "The third h2 should have a color of green.", - "testString": "assert($('h2').eq(2).css('color') == \"rgb(0, 128, 0)\", 'The third h2 should have a color of green.');" - }, - { - "text": "The fourth h2 should have a color of red.", - "testString": "assert($('h2').eq(3).css('color') == \"rgb(255, 0, 0)\", 'The fourth h2 should have a color of red.');" - }, - { - "text": "The fifth h2 should have a color of green.", - "testString": "assert($('h2').eq(4).css('color') == \"rgb(0, 128, 0)\", 'The fifth h2 should have a color of green.');" - }, - { - "text": "The sixth h2 should have a color of red.", - "testString": "assert($('h2').eq(5).css('color') == \"rgb(255, 0, 0)\", 'The sixth h2 should have a color of red.');" - }, - { - "text": "The seventh h2 should have a color of green.", - "testString": "assert($('h2').eq(6).css('color') == \"rgb(0, 128, 0)\", 'The seventh h2 should have a color of green.');" - }, - { - "text": "The eighth h2 should have a color of red.", - "testString": "assert($('h2').eq(7).css('color') == \"rgb(255, 0, 0)\", 'The eighth h2 should have a color of red.');" - }, - { - "text": "The ninth h2 should have a color of red.", - "testString": "assert($('h2').eq(8).css('color') == \"rgb(255, 0, 0)\", 'The ninth h2 should have a color of red.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa7367417b2b2512bc8", - "title": "Add Classes with D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "Using a lot of inline styles on HTML elements gets hard to manage, even for smaller apps. It's easier to add a class to elements and style that class one time using CSS rules. D3 has the attr() method to add any HTML attribute to an element, including a class name.", - "The attr() method works the same way that style() does. It takes comma-separated values, and can use a callback function. Here's an example to add a class of \"container\" to a selection:", - "selection.attr(\"class\", \"container\");", - "
", - "Add the attr() method to the code in the editor and put a class of bar on the div elements." - ], - "tests": [ - { - "text": "Your div elements should have a class of bar.", - "testString": "assert($('div').attr('class') == \"bar\", 'Your div elements should have a class of bar.');" - }, - { - "text": "Your code should use the attr() method.", - "testString": "assert(code.match(/\\.attr/g), 'Your code should use the attr() method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa8367417b2b2512bc9", - "title": "Update the Height of an Element Dynamically", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ + "name": "Data Visualization with D3", + "order": 1, + "time": "5 hours", + "helpRoom": "Help", + "challenges": [ + { + "id": "587d7fa6367417b2b2512bc2", + "title": "Add Document Elements with D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 有多种方法可以用来在文档中增加元素、修改元素。", + "select()方法用来从文档中选择元素,它以你查询的元素名称作为参数,返回第一个符合条件的 HTML 节点。以下是一个例子:", + "const anchor = d3.select(\"a\");", + "上面这个例子找到页面上的第一个 a 标签(锚标签),将它作为一个 HTML 节点保存在变量anchor中。你也可以用其他的方法选择页面上的元素。例子中的 \"d3\" 是对 D3 对象的引用,可以通过它来访问 D3 的方法。", + "还可以用append()text()方法。", + "append()方法以你想添加到文档中的元素作为参数,给选中的元素添加一个 HTML 节点,返回那个节点的句柄。", + "text()方法既可以给节点设置新的文本,也可以获取节点的当前文本。 如果要设置文字内容,需要在圆括号中传入一个 string(字符串)类型的参数。", + "下面是一个选择无序列表、添加列表项和文字的例子:", + "
d3.select(\"ul\")
  .append(\"li\")
  .text(\"Very important item\");
", + "在 D3 中可以使用英文句号将多个方法串联在一起执行多个操作。", + "
", + "使用select方法选择文本中的body标签。然后用append方法为它添加一个h1标签,同时在h1中添加文本 \"Learning D3\"。" + ], + "tests": [ + { + "text": "你的body元素应该包含元素h1。", + "testString": "assert($('body').children('h1').length == 1, '你的body元素应该包含元素h1。');" + }, + { + "text": "你的h1元素应该包含文本 \"Learning D3\"。", + "testString": "assert($('h1').text() == \"Learning D3\", '你的h1元素应该包含文本 \"Learning D3\"。');" + }, + { + "text": "你应该能访问d3的对象。", + "testString": "assert(code.match(/d3/g), '你应该能访问d3的对象。');" + }, + { + "text": "你应该使用select方法。", + "testString": "assert(code.match(/\\.select/g), '你应该使用select方法。');" + }, + { + "text": "你应该使用append方法。", + "testString": "assert(code.match(/\\.append/g), '你应该使用append方法。');" + }, + { + "text": "你应该使用text方法。", + "testString": "assert(code.match(/\\.text/g), '你应该使用text方法。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa6367417b2b2512bc3", + "title": "Select a Group of Elements with D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "selectAll()方法选择一组元素。它以 HTML 节点数组的形式返回该文本中所有匹配所输入字符串的对象。以下是一个选择文本中所有锚标签的例子:", + "const anchors = d3.selectAll(\"a\");", + "像select()方法一样,selectAll()也支持链式调用,你可以在它之后调用其他方法。", + "
", + "选择所有的li标签,通过.text()方法将它的文本改为 \"list item\" 。" + ], + "tests": [ + { + "text": "页面上应该有 3 个li元素,每个元素的文本内容应为 \"list item\"。大小写和空格必须一致。", + "testString": "assert($('li').text().match(/list item/g).length == 3, '页面上应该有3个li元素,每个元素的文本内容应为 \"list item\"。大小写和空格必须一致。');" + }, + { + "text": "你应该能访问d3的对象。", + "testString": "assert(code.match(/d3/g), '你应该能访问d3的对象。');" + }, + { + "text": "你应该使用selectAll方法。", + "testString": "assert(code.match(/\\.selectAll/g), '你应该使用selectAll方法。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa7367417b2b2512bc4", + "title": "Work with Data in D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 是数据驱动的库,可以使用 D3 的方法将数组形式的数据显示在页面上。", + "第一步是让 D3 知道数据。data方法选择连接着数据的 DOM 元素,数据集作为参数传递给该方法。", + "常见的方法是在文档中为数据集中的每一个数据创建一个元素,为此,你可以使用 D3 的enter()方法", + "当enter()data()方法一起使用时,它把从页面中选择的元素和数据集中的元素作比较。如果页面中选择的元素较少则创建缺少的元素。", + "以下是一个选择ul元素并根据添加的数组创建新的列表项的例子。", + "
<body>
  <ul></ul>
  <script>
    const dataset = [\"a\", \"b\", \"c\"];
    d3.select(\"ul\").selectAll(\"li\")
      .data(dataset)
      .enter()
      .append(\"li\")
      .text(\"New item\");
  </script>
</body>
", + "选择不存在的 li 元素似乎有些难以理解。事实上,这段代码先选择页面上的 ul 元素,再选择所有的列表项——li,它将返回空。然后data()方法接收数组作为参数,并运行三次后面的代码,每次对应数组中的一个对象。enter()方法发现页面中没有 li 元素,但是需要 3 个(每个对应dataset中的一个对象)。它将在 ul 中添加带有文本 \"New item\" 的 li 元素。", + "
", + "选择body节点,然后选择所有的h2元素。让 D3 为dataset数组中的每一个对象创建并添加文本为 \"New Title\" 的h2标签。你应该使用data()enter()方法。" + ], + "tests": [ + { + "text": "你的文档应该有 9 个h2元素。", + "testString": "assert($('h2').length == 9, '你的文档应该有 9 个h2元素。');" + }, + { + "text": "h2元素中的文本应为 \"New Title\"。大小写和空格必须一致。", + "testString": "assert($('h2').text().match(/New Title/g).length == 9, 'h2元素中的文本应为 \"New Title\"。大小写和空格必须一致。');" + }, + { + "text": "你应该使用data()方法。", + "testString": "assert(code.match(/\\.data/g), '你应该使用data()方法。');" + }, + { + "text": "你应该使用enter()方法。", + "testString": "assert(code.match(/\\.enter/g), '你应该使用enter()方法。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa7367417b2b2512bc5", + "title": "Work with Dynamic Data in D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "最后两个挑战涉及到使用 D3 的data()enter()方法来动态展示数据。它们以数据集为参数,和append()方法一起使用,为数据集中的每一个元素对象创建一个 DOM 元素。", + "在之前的挑战中,你为dataset数组中的每一个对象创建了一个新的h2元素,但是他们的文本都是相同的 \"New Title\"。 这是因为你还没有使用和每个h2元素关联的数据。", + "text()方法以字符串或者回调函数作为参数:", + "selection.text((d) => d)", + "上面这个例子中的参数d指关联数据集的一个对象。", + "以当前例子为例,第一个h2元素和 12 关联,第二个h2元素和 31 关联,第三个h2元素和 22 关联,以此类推。", + "
", + "改变text()方法使得每个h2元素显示dataset数组中的对应值加上一个空格和 \"USD\"。例如,第一个标题应该为 \"12 USD\"。" + ], + "tests": [ + { + "text": "第一个h2的文本应该为 \"12 USD\"。", + "testString": "assert($('h2').eq(0).text() == \"12 USD\", '第一个h2的文本应该为 \"12 USD\"。');" + }, + { + "text": "第二个h2的文本应该为 \"31 USD\"。", + "testString": "assert($('h2').eq(1).text() == \"31 USD\", '第二个h2的文本应该为 \"31 USD\"。');" + }, + { + "text": "第三个h2的文本应该为 \"22 USD\"。", + "testString": "assert($('h2').eq(2).text() == \"22 USD\", '第三个h2的文本应该为 \"22 USD\"。');" + }, + { + "text": "第四个h2的文本应该为 \"17 USD\"。", + "testString": "assert($('h2').eq(3).text() == \"17 USD\", '第四个h2的文本应该为 \"17 USD\"。');" + }, + { + "text": "第五个h2的文本应该为 \"25 USD\"。", + "testString": "assert($('h2').eq(4).text() == \"25 USD\", '第五个h2的文本应该为 \"25 USD\"。');" + }, + { + "text": "第六个h2的文本应该为 \"18 USD\"。", + "testString": "assert($('h2').eq(5).text() == \"18 USD\", '第六个h2的文本应该为 \"18 USD\"。');" + }, + { + "text": "第七个h2的文本应该为 \"29 USD\"。", + "testString": "assert($('h2').eq(6).text() == \"29 USD\", '第七个h2的文本应该为 \"29 USD\"。');" + }, + { + "text": "第八个h2的文本应该为 \"14 USD\"。", + "testString": "assert($('h2').eq(7).text() == \"14 USD\", '第八个h2的文本应该为 \"14 USD\"。');" + }, + { + "text": "第九个h2的文本应该为 \"9 USD\"。", + "testString": "assert($('h2').eq(8).text() == \"9 USD\", '第九个h2的文本应该为 \"9 USD\"。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa7367417b2b2512bc6", + "title": "Add Inline Styling to Elements", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 可以使用style()方法为动态元素添加内联 CSS 样式表。", + "style()方法以用逗号分隔的键值对作为参数。这里是一个将选中文本的颜色设为蓝色的例子:", + "selection.style(\"color\",\"blue\");", + "
", + "在编辑器中添加style()方法,使所有显示的文本的font-familyverdana。" + ], + "tests": [ + { + "text": "你的h2元素的font-family应该为 verdana。", + "testString": "assert($('h2').css('font-family') == 'verdana', '你的h2元素的font-family应该为 verdana。');" + }, + { + "text": "你应该使用style()方法。", + "testString": "assert(code.match(/\\.style/g), '你应该使用style()方法。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa7367417b2b2512bc7", + "title": "Change Styles Based on Data", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 是关于可视化和展示数据的。如果你期望基于数据来改变元素的样式,你可以在style()方法中使用回调函数为不同元素改变样式。", + "例如,你想将值小于 20 的数据点设置为蓝色,其余设置为红色。你可以在style()方法中使用包含条件逻辑的回调函数。回调函数以d作为参数来表示一个数据点:", + "
selection.style(\"color\", (d) => {
  /* 基于情况返回颜色属性的逻辑表达式 */
});
", + "style()方法不仅仅可以设置color——它也适用于其他 CSS 属性。", + "
", + "在编辑器中添加style()方法,根据条件设置h2元素的color属性。写一个回调函数,如果值小于 20 返回 \"red\",否则返回 \"green\"。", + "提示
你可以使用 if-else 语句或者三目操作符。" + ], + "tests": [ + { + "text": "第一个h2color应该为 red。", + "testString": "assert($('h2').eq(0).css('color') == \"rgb(255, 0, 0)\", '第一个h2color应该为 red。');" + }, + { + "text": "第二个h2color应该为 green。", + "testString": "assert($('h2').eq(1).css('color') == \"rgb(0, 128, 0)\", '第二个h2color应该为 green。');" + }, + { + "text": "第三个h2color应该为 green。", + "testString": "assert($('h2').eq(2).css('color') == \"rgb(0, 128, 0)\", '第三个h2color应该为 green。');" + }, + { + "text": "第四个h2color应该为 red。", + "testString": "assert($('h2').eq(3).css('color') == \"rgb(255, 0, 0)\", '第四个h2color应该为 red。');" + }, + { + "text": "第五个h2color应该为 green。", + "testString": "assert($('h2').eq(4).css('color') == \"rgb(0, 128, 0)\", '第五个h2color应该为 green。');" + }, + { + "text": "第六个h2color应该为 red。", + "testString": "assert($('h2').eq(5).css('color') == \"rgb(255, 0, 0)\", '第六个h2color应该为 red。');" + }, + { + "text": "第七个h2color应该为 green。", + "testString": "assert($('h2').eq(6).css('color') == \"rgb(0, 128, 0)\", '第七个h2color应该为 green。');" + }, + { + "text": "第八个h2color应该为 red。", + "testString": "assert($('h2').eq(7).css('color') == \"rgb(255, 0, 0)\", '第八个h2color应该为 red。');" + }, + { + "text": "第九个h2color应该为 red。", + "testString": "assert($('h2').eq(8).css('color') == \"rgb(255, 0, 0)\", '第九个h2color应该为 red。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa7367417b2b2512bc8", + "title": "Add Classes with D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "即使对小型 app 来说在 HTML 元素中大量使用内联样式表也十分难以管理。更方便的是给元素添加遵守 CSS 规则的类。D3 中的attr()方法可以给元素添加任何 HTML 属性,包括类名称。", + "attr()方法和style()的使用方法一样。它以逗号分隔的键值对为参数使用回调函数。这里是一个给选中元素添加类名为 \"container\" 的例子:", + "selection.attr(\"class\", \"container\");", + "
", + "在编辑器中添加attr()方法,给div元素添加类名bar。" + ], + "tests": [ + { + "text": "你的div元素应该有一个bar类。", + "testString": "assert($('div').attr('class') == \"bar\", '你的div元素应该有一个bar类。');" + }, + { + "text": "你应该使用attr()方法。", + "testString": "assert(code.match(/\\.attr/g), '你应该使用attr()方法。');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa8367417b2b2512bc9", + "title": "Update the Height of an Element Dynamically", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ "The previous challenges covered how to display data from an array and how to add CSS classes. You can combine these lessons to create a simple bar chart. There are two steps to this:", "1) Create a div for each data point in the array", "2) Give each div a dynamic height, using a callback function in the style() method that sets height equal to the data value", @@ -512,7 +512,7 @@ "
", "Add the style() method to the code in the editor to set the height property for each element. Use a callback function to return the value of the data point with the string \"px\" added to it." ], - "tests": [ + "tests": [ { "text": "The first div should have a height of 12 pixels.", "testString": "assert($('div').eq(0).css('height') == '12px', 'The first div should have a height of 12 pixels.');" @@ -550,55 +550,55 @@ "testString": "assert($('div').eq(8).css('height') == '9px', 'The ninth div should have a height of 9 pixels.');" } ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa8367417b2b2512bca", - "title": "Change the Presentation of a Bar Chart", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa8367417b2b2512bca", + "title": "Change the Presentation of a Bar Chart", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ "The last challenge created a bar chart, but there are a couple of formatting changes that could improve it:", "1) Add space between each bar to visually separate them, which is done by adding a margin to the CSS for the bar class", "2) Increase the height of the bars to better show the difference in values, which is done by multiplying the value by a number to scale the height", @@ -644,57 +644,57 @@ "testString": "assert($('div').eq(8).css('height') == '90px' && $('div').eq(8).css('margin-right') == '2px', 'The ninth div should have a height of 90 pixels and a margin of 2 pixels.');" } ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa8367417b2b2512bcb", - "title": "Learn About SVG in D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa8367417b2b2512bcb", + "title": "Learn About SVG in D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ "SVG stands for Scalable Vector Graphics.", "Here \"scalable\" means that, if you zoom in or out on an object, it would not appear pixelated. It scales with the display system, whether it's on a small mobile screen or a large TV monitor.", "SVG is used to make common geometric shapes. Since D3 maps data into a visual representation, it uses SVG to create the shapes for the visualization. SVG shapes for a web page must go within an HTML svg tag.", @@ -703,1816 +703,1816 @@ "Add an svg node to the body using append(). Give it a width attribute set to the provided w constant and a height attribute set to the provided h constant using the attr() method for each. You'll see it in the output because there's a background-color of pink applied to it in the style tag.", "Note
Width and height attributes do not have units. This is the building block of scaling - the element will always have a 5:1 width to height ratio, no matter what the zoom level is." ], - "tests": [ - { - "text": "Your document should have 1 svg element.", - "testString": "assert($('svg').length == 1, 'Your document should have 1 svg element.');" - }, - { - "text": "The svg element should have a width attribute set to 500.", - "testString": "assert($('svg').attr('width') == '500', 'The svg element should have a width attribute set to 500.');" - }, - { - "text": "The svg element should have a height attribute set to 100.", - "testString": "assert($('svg').attr('height') == '100', 'The svg element should have a height attribute set to 100.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa8367417b2b2512bcc", - "title": "Display Shapes with SVG", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The last challenge created an svg element with a given width and height, which was visible because it had a background-color applied to it in the style tag. The code made space for the given width and height.", - "The next step is to create a shape to put in the svg area. There are a number of supported shapes in SVG, such as rectangles and circles. They are used to display data. For example, a rectangle (<rect>) SVG shape could create a bar in a bar chart.", - "When you place a shape into the svg area, you can specify where it goes with x and y coordinates. The origin point of (0, 0) is in the upper-left corner. Positive vales for x push the shape to the right, and positive values for y push the shape down from the origin point.", - "To place a shape in the middle of the 500 (width) x 100 (height) svg from last challenge, the x coordinate would be 250 and the y coordinate would be 50.", - "An SVG rect has four attributes. There are the x and y coordinates for where it is placed in the svg area. It also has a height and width to specify the size.", - "
", - "Add a rect shape to the svg using append(), and give it a width attribute of 25 and height attribute of 100. Also, give the rect x and y attributes each set to 0." - ], - "tests": [ - { - "text": "Your document should have 1 rect element.", - "testString": "assert($('rect').length == 1, 'Your document should have 1 rect element.');" - }, - { - "text": "The rect element should have a width attribute set to 25.", - "testString": "assert($('rect').attr('width') == '25', 'The rect element should have a width attribute set to 25.');" - }, - { - "text": "The rect element should have a height attribute set to 100.", - "testString": "assert($('rect').attr('height') == '100', 'The rect element should have a height attribute set to 100.');" - }, - { - "text": "The rect element should have an x attribute set to 0.", - "testString": "assert($('rect').attr('x') == '0', 'The rect element should have an x attribute set to 0.');" - }, - { - "text": "The rect element should have a y attribute set to 0.", - "testString": "assert($('rect').attr('y') == '0', 'The rect element should have a y attribute set to 0.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa8367417b2b2512bcd", - "title": "Create a Bar for Each Data Point in the Set", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The last challenge added only one rectangle to the svg element to represent a bar. Here, you'll combine what you've learned so far about data(), enter(), and SVG shapes to create and append a rectangle for each data point in dataset.", - "A previous challenge showed the format for how to create and append a div for each item in dataset:", - "
d3.select(\"body\").selectAll(\"div\")
  .data(dataset)
  .enter()
  .append(\"div\")
", - "There are a few differences working with rect elements instead of divs. The rects must be appended to an svg element, not directly to the body. Also, you need to tell D3 where to place each rect within the svg area. The bar placement will be covered in the next challenge.", - "
", - "Use the data(), enter(), and append() methods to create and append a rect for each item in dataset. The bars should display all on top of each other, this will be fixed in the next challenge." - ], - "tests": [ - { - "text": "Your document should have 9 rect elements.", - "testString": "assert($('rect').length == 9, 'Your document should have 9 rect elements.');" - }, - { - "text": "Your code should use the data() method.", - "testString": "assert(code.match(/\\.data/g), 'Your code should use the data() method.');" - }, - { - "text": "Your code should use the enter() method.", - "testString": "assert(code.match(/\\.enter/g), 'Your code should use the enter() method.');" - }, - { - "text": "Your code should use the append() method.", - "testString": "assert(code.match(/\\.append/g), 'Your code should use the append() method.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa9367417b2b2512bce", - "title": "Dynamically Set the Coordinates for Each Bar", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The last challenge created and appended a rectangle to the svg element for each point in dataset to represent a bar. Unfortunately, they were all stacked on top of each other.", - "The placement of a rectangle is handled by the x and y attributes. They tell D3 where to start drawing the shape in the svg area. The last challenge set them each to 0, so every bar was placed in the upper-left corner.", - "For a bar chart, all of the bars should sit on the same vertical level, which means the y value stays the same (at 0) for all bars. The x value, however, needs to change as you add new bars. Remember that larger x values push items farther to the right. As you go through the array elements in dataset, the x value should increase.", - "The attr() method in D3 accepts a callback function to dynamically set that attribute. The callback function takes two arguments, one for the data point itself (usually d) and one for the index of the data point in the array. The second argument for the index is optional. Here's the format:", - "
selection.attr(\"property\", (d, i) => {
  /*
  * d is the data point value
  * i is the index of the data point in the array
  */
})
", - "It's important to note that you do NOT need to write a for loop or use forEach() to iterate over the items in the data set. Recall that the data() method parses the data set, and any method that's chained after data() is run once for each item in the data set.", - "
", - "Change the x attribute callback function so it returns the index times 30.", - "Note
Each bar has a width of 25, so increasing each x value by 30 adds some space between the bars. Any value greater than 25 would work in this example." - ], - "tests": [ - { - "text": "The first rect should have an x value of 0.", - "testString": "assert($('rect').eq(0).attr('x') == '0', 'The first rect should have an x value of 0.');" - }, - { - "text": "The second rect should have an x value of 30.", - "testString": "assert($('rect').eq(1).attr('x') == '30', 'The second rect should have an x value of 30.');" - }, - { - "text": "The third rect should have an x value of 60.", - "testString": "assert($('rect').eq(2).attr('x') == '60', 'The third rect should have an x value of 60.');" - }, - { - "text": "The fourth rect should have an x value of 90.", - "testString": "assert($('rect').eq(3).attr('x') == '90', 'The fourth rect should have an x value of 90.');" - }, - { - "text": "The fifth rect should have an x value of 120.", - "testString": "assert($('rect').eq(4).attr('x') == '120', 'The fifth rect should have an x value of 120.');" - }, - { - "text": "The sixth rect should have an x value of 150.", - "testString": "assert($('rect').eq(5).attr('x') == '150', 'The sixth rect should have an x value of 150.');" - }, - { - "text": "The seventh rect should have an x value of 180.", - "testString": "assert($('rect').eq(6).attr('x') == '180', 'The seventh rect should have an x value of 180.');" - }, - { - "text": "The eighth rect should have an x value of 210.", - "testString": "assert($('rect').eq(7).attr('x') == '210', 'The eighth rect should have an x value of 210.');" - }, - { - "text": "The ninth rect should have an x value of 240.", - "testString": "assert($('rect').eq(8).attr('x') == '240', 'The ninth rect should have an x value of 240.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa9367417b2b2512bcf", - "title": "Dynamically Change the Height of Each Bar", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The height of each bar can be set to the value of the data point in the array, similar to how the x value was set dynamically.", - "
selection.attr(\"property\", (d, i) => {
  /*
  * d is the data point value
  * i is the index of the data point in the array
  */
})
", - "
", - "Change the callback function for the height attribute to return the data value times 3.", - "Note
Remember that multiplying all data points by the same constant scales the data (like zooming in). It helps to see the differences between bar values in this example." - ], - "tests": [ - { - "text": "The first rect should have a height of 36.", - "testString": "assert($('rect').eq(0).attr('height') == '36', 'The first rect should have a height of 36.');" - }, - { - "text": "The second rect should have a height of 93.", - "testString": "assert($('rect').eq(1).attr('height') == '93', 'The second rect should have a height of 93.');" - }, - { - "text": "The third rect should have a height of 66.", - "testString": "assert($('rect').eq(2).attr('height') == '66', 'The third rect should have a height of 66.');" - }, - { - "text": "The fourth rect should have a height of 51.", - "testString": "assert($('rect').eq(3).attr('height') == '51', 'The fourth rect should have a height of 51.');" - }, - { - "text": "The fifth rect should have a height of 75.", - "testString": "assert($('rect').eq(4).attr('height') == '75', 'The fifth rect should have a height of 75.');" - }, - { - "text": "The sixth rect should have a height of 54.", - "testString": "assert($('rect').eq(5).attr('height') == '54', 'The sixth rect should have a height of 54.');" - }, - { - "text": "The seventh rect should have a height of 87.", - "testString": "assert($('rect').eq(6).attr('height') == '87', 'The seventh rect should have a height of 87.');" - }, - { - "text": "The eighth rect should have a height of 42.", - "testString": "assert($('rect').eq(7).attr('height') == '42', 'The eighth rect should have a height of 42.');" - }, - { - "text": "The ninth rect should have a height of 27.", - "testString": "assert($('rect').eq(8).attr('height') == '27', 'The ninth rect should have a height of 27.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa9367417b2b2512bd0", - "title": "Invert SVG Elements", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "You may have noticed the bar chart looked like it's upside-down, or inverted. This is because of how SVG uses (x, y) coordinates.", - "In SVG, the origin point for the coordinates is in the upper-left corner. An x coordinate of 0 places a shape on the left edge of the SVG area. A y coordinate of 0 places a shape on the top edge of the SVG area. Higher x values push the rectangle to the right. Higher y values push the rectangle down.", - "To make the bars right-side-up, you need to change the way the y coordinate is calculated. It needs to account for both the height of the bar and the total height of the SVG area.", - "The height of the SVG area is 100. If you have a data point of 0 in the set, you would want the bar to start at the bottom of the SVG area (not the top). To do this, the y coordinate needs a value of 100. If the data point value were 1, you would start with a y coordinate of 100 to set the bar at the bottom. Then you need to account for the height of the bar of 1, so the final y coordinate would be 99.", - "The y coordinate that is y = heightOfSVG - heightOfBar would place the bars right-side-up.", - "
", - "Change the callback function for the y attribute to set the bars right-side-up. Remember that the height of the bar is 3 times the data value d.", - "Note
In general, the relationship is y = h - m * d, where m is the constant that scales the data points." - ], - "tests": [ - { - "text": "The first rect should have a y value of 64.", - "testString": "assert($('rect').eq(0).attr('y') == h - (dataset[0] * 3), 'The first rect should have a y value of 64.');" - }, - { - "text": "The second rect should have a y value of 7.", - "testString": "assert($('rect').eq(1).attr('y') == h - (dataset[1] * 3), 'The second rect should have a y value of 7.');" - }, - { - "text": "The third rect should have a y value of 34.", - "testString": "assert($('rect').eq(2).attr('y') == h - (dataset[2] * 3), 'The third rect should have a y value of 34.');" - }, - { - "text": "The fourth rect should have a y value of 49.", - "testString": "assert($('rect').eq(3).attr('y') == h - (dataset[3] * 3), 'The fourth rect should have a y value of 49.');" - }, - { - "text": "The fifth rect should have a y value of 25.", - "testString": "assert($('rect').eq(4).attr('y') == h - (dataset[4] * 3), 'The fifth rect should have a y value of 25.');" - }, - { - "text": "The sixth rect should have a y value of 46.", - "testString": "assert($('rect').eq(5).attr('y') == h - (dataset[5] * 3), 'The sixth rect should have a y value of 46.');" - }, - { - "text": "The seventh rect should have a y value of 13.", - "testString": "assert($('rect').eq(6).attr('y') == h - (dataset[6] * 3), 'The seventh rect should have a y value of 13.');" - }, - { - "text": "The eighth rect should have a y value of 58.", - "testString": "assert($('rect').eq(7).attr('y') == h - (dataset[7] * 3), 'The eighth rect should have a y value of 58.');" - }, - { - "text": "The ninth rect should have a y value of 73.", - "testString": "assert($('rect').eq(8).attr('y') == h - (dataset[8] * 3), 'The ninth rect should have a y value of 73.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fa9367417b2b2512bd1", - "title": "Change the Color of an SVG Element", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The bars are in the right position, but they are all the same black color. SVG has a way to change the color of the bars.", - "In SVG, a rect shape is colored with the fill attribute. It supports hex codes, color names, and rgb values, as well as more complex options like gradients and transparency.", - "
", - "Add an attr() method to set the \"fill\" of all the bars to the color \"navy\"." - ], - "tests": [ - { - "text": "The bars should all have a fill color of navy.", - "testString": "assert($('rect').css('fill') == \"rgb(0, 0, 128)\", 'The bars should all have a fill color of navy.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7faa367417b2b2512bd2", - "title": "Add Labels to D3 Elements", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 lets you label a graph element, such as a bar, using the SVG text element.", - "Like the rect element, a text element needs to have x and y attributes, to place it on the SVG canvas. It also needs to access the data to display those values.", - "D3 gives you a high level of control over how you label your bars.", - "
", - "The code in the editor already binds the data to each new text element. First, append text nodes to the svg. Next, add attributes for the x and y coordinates. They should be calculated the same way as the rect ones, except the y value for the text should make the label sit 3 units higher than the bar. Finally, use the D3 text() method to set the label equal to the data point value.", - "Note
For the label to sit higher than the bar, decide if the y value for the text should be 3 greater or 3 less than the y value for the bar." - ], - "tests": [ - { - "text": "The first text element should have a label of 12 and a y value of 61.", - "testString": "assert($('text').eq(0).text() == '12' && $('text').eq(0).attr('y') == '61', 'The first text element should have a label of 12 and a y value of 61.');" - }, - { - "text": "The second text element should have a label of 31 and a y value of 4.", - "testString": "assert($('text').eq(1).text() == '31' && $('text').eq(1).attr('y') == '4', 'The second text element should have a label of 31 and a y value of 4.');" - }, - { - "text": "The third text element should have a label of 22 and a y value of 31.", - "testString": "assert($('text').eq(2).text() == '22' && $('text').eq(2).attr('y') == '31', 'The third text element should have a label of 22 and a y value of 31.');" - }, - { - "text": "The fourth text element should have a label of 17 and a y value of 46.", - "testString": "assert($('text').eq(3).text() == '17' && $('text').eq(3).attr('y') == '46', 'The fourth text element should have a label of 17 and a y value of 46.');" - }, - { - "text": "The fifth text element should have a label of 25 and a y value of 22.", - "testString": "assert($('text').eq(4).text() == '25' && $('text').eq(4).attr('y') == '22', 'The fifth text element should have a label of 25 and a y value of 22.');" - }, - { - "text": "The sixth text element should have a label of 18 and a y value of 43.", - "testString": "assert($('text').eq(5).text() == '18' && $('text').eq(5).attr('y') == '43', 'The sixth text element should have a label of 18 and a y value of 43.');" - }, - { - "text": "The seventh text element should have a label of 29 and a y value of 10.", - "testString": "assert($('text').eq(6).text() == '29' && $('text').eq(6).attr('y') == '10', 'The seventh text element should have a label of 29 and a y value of 10.');" - }, - { - "text": "The eighth text element should have a label of 14 and a y value of 55.", - "testString": "assert($('text').eq(7).text() == '14' && $('text').eq(7).attr('y') == '55', 'The eighth text element should have a label of 14 and a y value of 55.');" - }, - { - "text": "The ninth text element should have a label of 9 and a y value of 70.", - "testString": "assert($('text').eq(8).text() == '9' && $('text').eq(8).attr('y') == '70', 'The ninth text element should have a label of 9 and a y value of 70.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7faa367417b2b2512bd3", - "title": "Style D3 Labels", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "D3 methods can add styles to the bar labels. The fill attribute sets the color of the text for a text node. The style() method sets CSS rules for other styles, such as \"font-family\" or \"font-size\".", - "
", - "Set the font-size of the text elements to 25px, and the color of the text to red." - ], - "tests": [ - { - "text": "The labels should all have a fill color of red.", - "testString": "assert($('text').css('fill') == 'rgb(255, 0, 0)', 'The labels should all have a fill color of red.');" - }, - { - "text": "The labels should all have a font-size of 25 pixels.", - "testString": "assert($('text').css('font-size') == '25px', 'The labels should all have a font-size of 25 pixels.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7faa367417b2b2512bd4", - "title": "Add a Hover Effect to a D3 Element", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "It's possible to add effects that highlight a bar when the user hovers over it with the mouse. So far, the styling for the rectangles is applied with the built-in D3 and SVG methods, but you can use CSS as well.", - "You set the CSS class on the SVG elements with the attr() method. Then the :hover pseudo-class for your new class holds the style rules for any hover effects.", - "
", - "Use the attr() method to add a class of bar to all the rect elements. This changes the fill color of the bar to brown when you mouse over it." - ], - "tests": [ - { - "text": "Your rect elements should have a class of bar.", - "testString": "assert($('rect').attr('class') == \"bar\", 'Your rect elements should have a class of bar.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7faa367417b2b2512bd6", - "title": "Add a Tooltip to a D3 Element", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "A tooltip shows more information about an item on a page when the user hovers over that item. There are several ways to add a tooltip to a visualization, this challenge uses the SVG title element.", - "title pairs with the text() method to dynamically add data to the bars.", - "
", - "Append a title element under each rect node. Then call the text() method with a callback function so the text displays the data value." - ], - "tests": [ - { - "text": "Your code should have 9 title elements.", - "testString": "assert($('title').length == 9, 'Your code should have 9 title elements.');" - }, - { - "text": "The first title element should have tooltip text of 12.", - "testString": "assert($('title').eq(0).text() == '12', 'The first title element should have tooltip text of 12.');" - }, - { - "text": "The second title element should have tooltip text of 31.", - "testString": "assert($('title').eq(1).text() == '31', 'The second title element should have tooltip text of 31.');" - }, - { - "text": "The third title element should have tooltip text of 22.", - "testString": "assert($('title').eq(2).text() == '22', 'The third title element should have tooltip text of 22.');" - }, - { - "text": "The fourth title element should have tooltip text of 17.", - "testString": "assert($('title').eq(3).text() == '17', 'The fourth title element should have tooltip text of 17.');" - }, - { - "text": "The fifth title element should have tooltip text of 25.", - "testString": "assert($('title').eq(4).text() == '25', 'The fifth title element should have tooltip text of 25.');" - }, - { - "text": "The sixth title element should have tooltip text of 18.", - "testString": "assert($('title').eq(5).text() == '18', 'The sixth title element should have tooltip text of 18.');" - }, - { - "text": "The seventh title element should have tooltip text of 29.", - "testString": "assert($('title').eq(6).text() == '29', 'The seventh title element should have tooltip text of 29.');" - }, - { - "text": "The eighth title element should have tooltip text of 14.", - "testString": "assert($('title').eq(7).text() == '14', 'The eighth title element should have tooltip text of 14.');" - }, - { - "text": "The ninth title element should have tooltip text of 9.", - "testString": "assert($('title').eq(8).text() == '9', 'The ninth title element should have tooltip text of 9.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fab367417b2b2512bd7", - "title": "Create a Scatterplot with SVG Circles", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "A scatter plot is another type of visualization. It usually uses circles to map data points, which have two values each. These values tie to the x and y axes, and are used to position the circle in the visualization.", - "SVG has a circle tag to create the circle shape. It works a lot like the rect elements you used for the bar chart.", - "
", - "Use the data(), enter(), and append() methods to bind dataset to new circle elements that are appended to the SVG canvas." - ], - "tests": [ - { - "text": "Your code should have 10 circle elements.", - "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fab367417b2b2512bd8", - "title": "Add Attributes to the Circle Elements", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The last challenge created the circle elements for each point in the dataset, and appended them to the SVG canvas. But D3 needs more information about the position and size of each circle to display them correctly.", - "A circle in SVG has three main attributes. The cx and cy attributes are the coordinates. They tell D3 where to position the center of the shape on the SVG canvas. The radius (r attribute) gives the size of the circle.", - "Just like the rect y coordinate, the cy attribute for a circle is measured from the top of the SVG canvas, not from the bottom.", - "All three attributes can use a callback function to set their values dynamically. Remember that all methods chained after data(dataset) run once per item in dataset. The d parameter in the callback function refers to the current item in dataset, which is an array for each point. You use bracket notation, like d[0], to access the values in that array.", - "
", - "Add cx, cy, and r attributes to the circle elements. The cx value should be the first number in the array for each item in dataset. The cy value should be based off the second number in the array, but make sure to show the chart right-side-up and not inverted. The r value should be 5 for all circles." - ], - "tests": [ - { - "text": "Your code should have 10 circle elements.", - "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" - }, - { - "text": "The first circle element should have a cx value of 34, a cy value of 422, and an r value of 5.", - "testString": "assert($('circle').eq(0).attr('cx') == '34' && $('circle').eq(0).attr('cy') == '422' && $('circle').eq(0).attr('r') == '5', 'The first circle element should have a cx value of 34, a cy value of 422, and an r value of 5.');" - }, - { - "text": "The second circle element should have a cx value of 109, a cy value of 220, and an r value of 5.", - "testString": "assert($('circle').eq(1).attr('cx') == '109' && $('circle').eq(1).attr('cy') == '220' && $('circle').eq(1).attr('r') == '5', 'The second circle element should have a cx value of 109, a cy value of 220, and an r value of 5.');" - }, - { - "text": "The third circle element should have a cx value of 310, a cy value of 380, and an r value of 5.", - "testString": "assert($('circle').eq(2).attr('cx') == '310' && $('circle').eq(2).attr('cy') == '380' && $('circle').eq(2).attr('r') == '5', 'The third circle element should have a cx value of 310, a cy value of 380, and an r value of 5.');" - }, - { - "text": "The fourth circle element should have a cx value of 79, a cy value of 89, and an r value of 5.", - "testString": "assert($('circle').eq(3).attr('cx') == '79' && $('circle').eq(3).attr('cy') == '89' && $('circle').eq(3).attr('r') == '5', 'The fourth circle element should have a cx value of 79, a cy value of 89, and an r value of 5.');" - }, - { - "text": "The fifth circle element should have a cx value of 420, a cy value of 280, and an r value of 5.", - "testString": "assert($('circle').eq(4).attr('cx') == '420' && $('circle').eq(4).attr('cy') == '280' && $('circle').eq(4).attr('r') == '5', 'The fifth circle element should have a cx value of 420, a cy value of 280, and an r value of 5.');" - }, - { - "text": "The sixth circle element should have a cx value of 233, a cy value of 355, and an r value of 5.", - "testString": "assert($('circle').eq(5).attr('cx') == '233' && $('circle').eq(5).attr('cy') == '355' && $('circle').eq(5).attr('r') == '5', 'The sixth circle element should have a cx value of 233, a cy value of 355, and an r value of 5.');" - }, - { - "text": "The seventh circle element should have a cx value of 333, a cy value of 404, and an r value of 5.", - "testString": "assert($('circle').eq(6).attr('cx') == '333' && $('circle').eq(6).attr('cy') == '404' && $('circle').eq(6).attr('r') == '5', 'The seventh circle element should have a cx value of 333, a cy value of 404, and an r value of 5.');" - }, - { - "text": "The eighth circle element should have a cx value of 222, a cy value of 167, and an r value of 5.", - "testString": "assert($('circle').eq(7).attr('cx') == '222' && $('circle').eq(7).attr('cy') == '167' && $('circle').eq(7).attr('r') == '5', 'The eighth circle element should have a cx value of 222, a cy value of 167, and an r value of 5.');" - }, - { - "text": "The ninth circle element should have a cx value of 78, a cy value of 180, and an r value of 5.", - "testString": "assert($('circle').eq(8).attr('cx') == '78' && $('circle').eq(8).attr('cy') == '180' && $('circle').eq(8).attr('r') == '5', 'The ninth circle element should have a cx value of 78, a cy value of 180, and an r value of 5.');" - }, - { - "text": "The tenth circle element should have a cx value of 21, a cy value of 377, and an r value of 5.", - "testString": "assert($('circle').eq(9).attr('cx') == '21' && $('circle').eq(9).attr('cy') == '377' && $('circle').eq(9).attr('r') == '5', 'The tenth circle element should have a cx value of 21, a cy value of 377, and an r value of 5.');" - } - ], - "solutions": [], - "hints": [ - "The cy attribute should be the second number of the data point array subtracted from the height of the SVG." - ], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fab367417b2b2512bd9", - "title": "Add Labels to Scatter Plot Circles", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "You can add text to create labels for the points in a scatter plot.", - "The goal is to display the comma-separated values for the first (x) and second (y) fields of each item in dataset.", - "The text nodes need x and y attributes to position it on the SVG canvas. In this challenge, the y value (which determines height) can use the same value that the circle uses for its cy attribute. The x value can be slightly larger than the cx value of the circle, so the label is visible. This will push the label to the right of the plotted point.", - "
", - "Label each point on the scatter plot using the text elements. The text of the label should be the two values separated by a comma and a space. For example, the label for the first point is \"34, 78\". Set the x attribute so it's 5 units more than the value you used for the cx attribute on the circle. Set the y attribute the same way that's used for the cy value on the circle." - ], - "tests": [ - { - "text": "Your code should have 10 text elements.", - "testString": "assert($('text').length == 10, 'Your code should have 10 text elements.');" - }, - { - "text": "The first label should have text of \"34, 78\", an x value of 39, and a y value of 422.", - "testString": "assert($('text').eq(0).text() == '34, 78' && $('text').eq(0).attr('x') == '39' && $('text').eq(0).attr('y') == '422', 'The first label should have text of \"34, 78\", an x value of 39, and a y value of 422.');" - }, - { - "text": "The second label should have text of \"109, 280\", an x value of 114, and a y value of 220.", - "testString": "assert($('text').eq(1).text() == '109, 280' && $('text').eq(1).attr('x') == '114' && $('text').eq(1).attr('y') == '220', 'The second label should have text of \"109, 280\", an x value of 114, and a y value of 220.');" - }, - { - "text": "The third label should have text of \"310, 120\", an x value of 315, and a y value of 380.", - "testString": "assert($('text').eq(2).text() == '310, 120' && $('text').eq(2).attr('x') == '315' && $('text').eq(2).attr('y') == '380', 'The third label should have text of \"310, 120\", an x value of 315, and a y value of 380.');" - }, - { - "text": "The fourth label should have text of \"79, 411\", an x value of 84, and a y value of 89.", - "testString": "assert($('text').eq(3).text() == '79, 411' && $('text').eq(3).attr('x') == '84' && $('text').eq(3).attr('y') == '89', 'The fourth label should have text of \"79, 411\", an x value of 84, and a y value of 89.');" - }, - { - "text": "The fifth label should have text of \"420, 220\", an x value of 425, and a y value of 280.", - "testString": "assert($('text').eq(4).text() == '420, 220' && $('text').eq(4).attr('x') == '425' && $('text').eq(4).attr('y') == '280', 'The fifth label should have text of \"420, 220\", an x value of 425, and a y value of 280.');" - }, - { - "text": "The sixth label should have text of \"233, 145\", an x value of 238, and a y value of 355.", - "testString": "assert($('text').eq(5).text() == '233, 145' && $('text').eq(5).attr('x') == '238' && $('text').eq(5).attr('y') == '355', 'The sixth label should have text of \"233, 145\", an x value of 238, and a y value of 355.');" - }, - { - "text": "The seventh label should have text of \"333, 96\", an x value of 338, and a y value of 404.", - "testString": "assert($('text').eq(6).text() == '333, 96' && $('text').eq(6).attr('x') == '338' && $('text').eq(6).attr('y') == '404', 'The seventh label should have text of \"333, 96\", an x value of 338, and a y value of 404.');" - }, - { - "text": "The eighth label should have text of \"222, 333\", an x value of 227, and a y value of 167.", - "testString": "assert($('text').eq(7).text() == '222, 333' && $('text').eq(7).attr('x') == '227' && $('text').eq(7).attr('y') == '167', 'The eighth label should have text of \"222, 333\", an x value of 227, and a y value of 167.');" - }, - { - "text": "The ninth label should have text of \"78, 320\", an x value of 83, and a y value of 180.", - "testString": "assert($('text').eq(8).text() == '78, 320' && $('text').eq(8).attr('x') == '83' && $('text').eq(8).attr('y') == '180', 'The ninth label should have text of \"78, 320\", an x value of 83, and a y value of 180.');" - }, - { - "text": "The tenth label should have text of \"21, 123\", an x value of 26, and a y value of 377.", - "testString": "assert($('text').eq(9).text() == '21, 123' && $('text').eq(9).attr('x') == '26' && $('text').eq(9).attr('y') == '377', 'The tenth label should have text of \"21, 123\", an x value of 26, and a y value of 377.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fab367417b2b2512bda", - "title": "Create a Linear Scale with D3", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The bar and scatter plot charts both plotted data directly onto the SVG canvas. However, if the height of a bar or one of the data points were larger than the SVG height or width values, it would go outside the SVG area.", - "In D3, there are scales to help plot data. Scales are functions that tell the program how to map a set of raw data points onto the pixels of the SVG canvas.", - "For example, say you have a 100x500-sized SVG canvas and you want to plot Gross Domestic Product (GDP) for a number of countries. The set of numbers would be in the billion or trillion-dollar range. You provide D3 a type of scale to tell it how to place the large GDP values into that 100x500-sized area.", - "It's unlikely you would plot raw data as-is. Before plotting it, you set the scale for your entire data set, so that the x and y values fit your canvas width and height.", - "D3 has several scale types. For a linear scale (usually used with quantitative data), there is the D3 method scaleLinear():", - " const scale = d3.scaleLinear()", - "By default, a scale uses the identity relationship. The value of the input is the same as the value of the output. A separate challenge covers how to change this.", - "
", - "Change the scale variable to create a linear scale. Then set the output variable to the scale called with an input argument of 50." - ], - "tests": [ - { - "text": "The text in the h2 should be 50.", - "testString": "assert($('h2').text() == '50', 'The text in the h2 should be 50.');" - }, - { - "text": "Your code should use the scaleLinear() method.", - "testString": "assert(code.match(/\\.scaleLinear/g), 'Your code should use the scaleLinear() method.');" - }, - { - "text": "The output variable should call scale with an argument of 50.", - "testString": "assert(output == 50 && code.match(/scale\\(\\s*?50\\s*?\\)/g), 'The output variable should call scale with an argument of 50.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fac367417b2b2512bdb", - "title": "Set a Domain and a Range on a Scale", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "By default, scales use the identity relationship - the input value maps to the output value. But scales can be much more flexible and interesting.", - "Say a data set has values ranging from 50 to 480. This is the input information for a scale, and is also known as the domain.", - "You want to map those points along the x axis on the SVG canvas, between 10 units and 500 units. This is the output information, which is also known as the range.", - "The domain() and range() methods set these values for the scale. Both methods take an array of at least two elements as an argument. Here's an example:", - "
// Set a domain
// The domain covers the set of input values
scale.domain([50, 480]);
// Set a range
// The range covers the set of output values
scale.range([10, 500]);
scale(50) // Returns 10
scale(480) // Returns 500
scale(325) // Returns 323.37
scale(750) // Returns 807.67
d3.scaleLinear()
", - "Notice that the scale uses the linear relationship between the domain and range values to figure out what the output should be for a given number. The minimum value in the domain (50) maps to the minimum value (10) in the range.", - "
", - "Create a scale and set its domain to [250, 500] and range to [10, 150].", - "Note
You can chain the domain() and range() methods onto the scale variable." - ], - "tests": [ - { - "text": "Your code should use the domain() method.", - "testString": "assert(code.match(/\\.domain/g), 'Your code should use the domain() method.');" - }, - { - "text": "The domain() of the scale should be set to [250, 500].", - "testString": "assert(JSON.stringify(scale.domain()) == JSON.stringify([250, 500]), 'The domain() of the scale should be set to [250, 500].');" - }, - { - "text": "Your code should use the range() method.", - "testString": "assert(code.match(/\\.range/g), 'Your code should use the range() method.');" - }, - { - "text": "The range() of the scale should be set to [10, 150].", - "testString": "assert(JSON.stringify(scale.range()) == JSON.stringify([10, 150]), 'The range() of the scale should be set to [10, 150].');" - }, - { - "text": "The text in the h2 should be -102.", - "testString": "assert($('h2').text() == '-102', 'The text in the h2 should be -102.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fac367417b2b2512bdc", - "title": "Use the d3.max and d3.min Functions to Find Minimum and Maximum Values in a Dataset", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The D3 methods domain() and range() set that information for your scale based on the data. There are a couple methods to make that easier.", - "Often when you set the domain, you'll want to use the minimum and maximum values within the data set. Trying to find these values manually, especially in a large data set, may cause errors.", - "D3 has two methods - min() and max() to return this information. Here's an example:", - "
const exampleData = [34, 234, 73, 90, 6, 52];
d3.min(exampleData) // Returns 6
d3.max(exampleData) // Returns 234
", - "A dataset may have nested arrays, like the [x, y] coordinate pairs that were in the scatter plot example. In that case, you need to tell D3 how to calculate the maximum and minimum.", - "Fortunately, both the min() and max() methods take a callback function.", - "In this example, the callback function's argument d is for the current inner array. The callback needs to return the element from the inner array (the x or y value) over which you want to compute the maximum or minimum. Here's an example for how to find the min and max values with an array of arrays:", - "
const locationData = [[1, 7],[6, 3],[8, 3]];
// Returns the smallest number out of the first elements
const minX = d3.min(locationData, (d) => d[0]);
// minX compared 1, 6, and 8 and is set to 1
", - "
", - "The positionData variable holds a 3-dimensional (3D) array. Use a D3 method to find the maximum value of the z coordinate (the third value) from the arrays and save it in the output variable.", - "Note
Fun fact - D3 can plot 3D arrays." - ], - "tests": [ - { - "text": "The text in the h2 should be 8.", - "testString": "assert(output == 8 && $('h2').text() == '8', 'The text in the h2 should be 8.');" - }, - { - "text": "Your code should use the max() method.", - "testString": "assert(code.match(/\\.max/g), 'Your code should use the max() method.')" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fac367417b2b2512bdd", - "title": "Use Dynamic Scales", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "The D3 min() and max() methods are useful to help set the scale.", - "Given a complex data set, one priority is to set the scale so the visualization fits the SVG container's width and height. You want all the data plotted inside the SVG canvas so it's visible on the web page.", - "The example below sets the x-axis scale for scatter plot data. The domain() method passes information to the scale about the raw data values for the plot. The range() method gives it information about the actual space on the web page for the visualization.", - "In the example, the domain goes from 0 to the maximum in the set. It uses the max() method with a callback function based on the x values in the arrays. The range uses the SVG canvas' width (w), but it includes some padding, too. This puts space between the scatter plot dots and the edge of the SVG canvas.", - "
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 between the SVG canvas boundary and the plot
const padding = 30;
const xScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, (d) => d[0])])
  .range([padding, w - padding]);
", - "The padding may be confusing at first. Picture the x-axis as a horizontal line from 0 to 500 (the width value for the SVG canvas). Including the padding in the range() method forces the plot to start at 30 along that line (instead of 0), and end at 470 (instead of 500).", - "
", - "Use the yScale variable to create a linear y-axis scale. The domain should start at zero and go to the maximum y value in the set. The range should use the SVG height (h) and include padding.", - "Note
Remember to keep the plot right-side-up. When you set the range for the y coordinates, the higher value (height minus padding) is the first argument, and the lower value is the second argument." - ], - "tests": [ - { - "text": "The text in the h2 should be 30.", - "testString": "assert(output == 30 && $('h2').text() == '30', 'The text in the h2 should be 30.');" - }, - { - "text": "The domain() of yScale should be equivalent to [0, 411].", - "testString": "assert(JSON.stringify(yScale.domain()) == JSON.stringify([0, 411]), 'The domain() of yScale should be equivalent to [0, 411].');" - }, - { - "text": "The range() of yScale should be equivalent to [470, 30].", - "testString": "assert(JSON.stringify(yScale.range()) == JSON.stringify([470, 30]), 'The range() of yScale should be equivalent to [470, 30].');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fac367417b2b2512bde", - "title": "Use a Pre-Defined Scale to Place Elements", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "With the scales set up, it's time to map the scatter plot again. The scales are like processing functions that turn the x and y raw data into values that fit and render correctly on the SVG canvas. They keep the data within the screen's plotting area.", - "You set the coordinate attribute values for an SVG shape with the scaling function. This includes x and y attributes for rect or text elements, or cx and cy for circles. Here's an example:", - "
shape
  .attr(\"x\", (d) => xScale(d[0]))
", - "Scales set shape coordinate attributes to place the data points onto the SVG canvas. You don't need to apply scales when you display the actual data value, for example, in the text() method for a tooltip or label.", - "
", - "Use xScale and yScale to position both the circle and text shapes onto the SVG canvas. For the circles, apply the scales to set the cx and cy attributes. Give them a radius of 5 units, too.", - "For the text elements, apply the scales to set the x and y attributes. The labels should be offset to the right of the dots. To do this, add 10 units to the x data value before passing it to the xScale." - ], - "tests": [ - { - "text": "Your code should have 10 circle elements.", - "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" - }, - { - "text": "The first circle element should have a cx value of approximately 91 and a cy value of approximately 368 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(0).attr('cx')) == '91' && Math.round($('circle').eq(0).attr('cy')) == '368' && $('circle').eq(0).attr('r') == '5', 'The first circle element should have a cx value of approximately 91 and a cy value of approximately 368 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The second circle element should have a cx value of approximately 159 and a cy value of approximately 181 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(1).attr('cx')) == '159' && Math.round($('circle').eq(1).attr('cy')) == '181' && $('circle').eq(1).attr('r') == '5', 'The second circle element should have a cx value of approximately 159 and a cy value of approximately 181 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The third circle element should have a cx value of approximately 340 and a cy value of approximately 329 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(2).attr('cx')) == '340' && Math.round($('circle').eq(2).attr('cy')) == '329' && $('circle').eq(2).attr('r') == '5', 'The third circle element should have a cx value of approximately 340 and a cy value of approximately 329 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The fourth circle element should have a cx value of approximately 131 and a cy value of approximately 60 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(3).attr('cx')) == '131' && Math.round($('circle').eq(3).attr('cy')) == '60' && $('circle').eq(3).attr('r') == '5', 'The fourth circle element should have a cx value of approximately 131 and a cy value of approximately 60 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The fifth circle element should have a cx value of approximately 440 and a cy value of approximately 237 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(4).attr('cx')) == '440' && Math.round($('circle').eq(4).attr('cy')) == '237' && $('circle').eq(4).attr('r') == '5', 'The fifth circle element should have a cx value of approximately 440 and a cy value of approximately 237 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The sixth circle element should have a cx value of approximately 271 and a cy value of approximately 306 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(5).attr('cx')) == '271' && Math.round($('circle').eq(5).attr('cy')) == '306' && $('circle').eq(5).attr('r') == '5', 'The sixth circle element should have a cx value of approximately 271 and a cy value of approximately 306 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The seventh circle element should have a cx value of approximately 361 and a cy value of approximately 351 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(6).attr('cx')) == '361' && Math.round($('circle').eq(6).attr('cy')) == '351' && $('circle').eq(6).attr('r') == '5', 'The seventh circle element should have a cx value of approximately 361 and a cy value of approximately 351 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The eighth circle element should have a cx value of approximately 261 and a cy value of approximately 132 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(7).attr('cx')) == '261' && Math.round($('circle').eq(7).attr('cy')) == '132' && $('circle').eq(7).attr('r') == '5', 'The eighth circle element should have a cx value of approximately 261 and a cy value of approximately 132 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The ninth circle element should have a cx value of approximately 131 and a cy value of approximately 144 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(8).attr('cx')) == '131' && Math.round($('circle').eq(8).attr('cy')) == '144' && $('circle').eq(8).attr('r') == '5', 'The ninth circle element should have a cx value of approximately 131 and a cy value of approximately 144 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "The tenth circle element should have a cx value of approximately 79 and a cy value of approximately 326 after applying the scales. It should also have an r value of 5.", - "testString": "assert(Math.round($('circle').eq(9).attr('cx')) == '79' && Math.round($('circle').eq(9).attr('cy')) == '326' && $('circle').eq(9).attr('r') == '5', 'The tenth circle element should have a cx value of approximately 79 and a cy value of approximately 326 after applying the scales. It should also have an r value of 5.');" - }, - { - "text": "Your code should have 10 text elements.", - "testString": "assert($('text').length == 10, 'Your code should have 10 text elements.');" - }, - { - "text": "The first label should have an x value of approximately 100 and a y value of approximately 368 after applying the scales.", - "testString": "assert(Math.round($('text').eq(0).attr('x')) == '100' && Math.round($('text').eq(0).attr('y')) == '368', 'The first label should have an x value of approximately 100 and a y value of approximately 368 after applying the scales.');" - }, - { - "text": "The second label should have an x value of approximately 168 and a y value of approximately 181 after applying the scales.", - "testString": "assert(Math.round($('text').eq(1).attr('x')) == '168' && Math.round($('text').eq(1).attr('y')) == '181', 'The second label should have an x value of approximately 168 and a y value of approximately 181 after applying the scales.');" - }, - { - "text": "The third label should have an x value of approximately 350 and a y value of approximately 329 after applying the scales.", - "testString": "assert(Math.round($('text').eq(2).attr('x')) == '350' && Math.round($('text').eq(2).attr('y')) == '329', 'The third label should have an x value of approximately 350 and a y value of approximately 329 after applying the scales.');" - }, - { - "text": "The fourth label should have an x value of approximately 141 and a y value of approximately 60 after applying the scales.", - "testString": "assert(Math.round($('text').eq(3).attr('x')) == '141' && Math.round($('text').eq(3).attr('y')) == '60', 'The fourth label should have an x value of approximately 141 and a y value of approximately 60 after applying the scales.');" - }, - { - "text": "The fifth label should have an x value of approximately 449 and a y value of approximately 237 after applying the scales.", - "testString": "assert(Math.round($('text').eq(4).attr('x')) == '449' && Math.round($('text').eq(4).attr('y')) == '237', 'The fifth label should have an x value of approximately 449 and a y value of approximately 237 after applying the scales.');" - }, - { - "text": "The sixth label should have an x value of approximately 280 and a y value of approximately 306 after applying the scales.", - "testString": "assert(Math.round($('text').eq(5).attr('x')) == '280' && Math.round($('text').eq(5).attr('y')) == '306', 'The sixth label should have an x value of approximately 280 and a y value of approximately 306 after applying the scales.');" - }, - { - "text": "The seventh label should have an x value of approximately 370 and a y value of approximately 351 after applying the scales.", - "testString": "assert(Math.round($('text').eq(6).attr('x')) == '370' && Math.round($('text').eq(6).attr('y')) == '351', 'The seventh label should have an x value of approximately 370 and a y value of approximately 351 after applying the scales.');" - }, - { - "text": "The eighth label should have an x value of approximately 270 and a y value of approximately 132 after applying the scales.", - "testString": "assert(Math.round($('text').eq(7).attr('x')) == '270' && Math.round($('text').eq(7).attr('y')) == '132', 'The eighth label should have an x value of approximately 270 and a y value of approximately 132 after applying the scales.');" - }, - { - "text": "The ninth label should have an x value of approximately 140 and a y value of approximately 144 after applying the scales.", - "testString": "assert(Math.round($('text').eq(8).attr('x')) == '140' && Math.round($('text').eq(8).attr('y')) == '144', 'The ninth label should have an x value of approximately 140 and a y value of approximately 144 after applying the scales.');" - }, - { - "text": "The tenth label should have an x value of approximately 88 and a y value of approximately 326 after applying the scales.", - "testString": "assert(Math.round($('text').eq(9).attr('x')) == '88' && Math.round($('text').eq(9).attr('y')) == '326', 'The tenth label should have an x value of approximately 88 and a y value of approximately 326 after applying the scales.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - }, - { - "id": "587d7fad367417b2b2512bdf", - "title": "Add Axes to a Visualization", - "required": [ - { - "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" - } - ], - "description": [ - "Another way to improve the scatter plot is to add an x-axis and a y-axis.", - "D3 has two methods axisLeft() and axisBottom() to render the y and x axes, respectively. (Axes is the plural form of axis). Here's an example to create the x-axis based on the xScale in the previous challenges:", - "const xAxis = d3.axisBottom(xScale);", - "The next step is to render the axis on the SVG canvas. To do so, you can use a general SVG component, the g element. The g stands for group.", - "Unlike rect, circle, and text, an axis is just a straight line when it's rendered. Because it is a simple shape, using g works.", - "The last step is to apply a transform attribute to position the axis on the SVG canvas in the right place. Otherwise, the line would render along the border of SVG canvas and wouldn't be visible.", - "SVG supports different types of transforms, but positioning an axis needs translate. When it's applied to the g element, it moves the whole group over and down by the given amounts. Here's an example:", - "
const xAxis = d3.axisBottom(xScale);

svg.append(\"g\")
   .attr(\"transform\", \"translate(0, \" + (h - padding) + \")\")
   .call(xAxis);
", - "The above code places the x-axis at the bottom of the SVG canvas. Then it's passed as an argument to the call() method.", - "The y-axis works is the same way, except the translate argument is in the form (x, 0). Because translate is a string in the attr() method above, you can use concatenation to include variable values for its arguments.", - "
", - "The scatter plot now has an x-axis. Create a y-axis in a variable named yAxis using the axisLeft() method. Then render the axis using a g element. Make sure to use a transform attribute to translate the axis by the amount of padding units right, and 0 units down. Remember to call() the axis." - ], - "tests": [ - { - "text": "Your code should use the axisLeft() method with yScale passed as the argument.", - "testString": "assert(code.match(/\\.axisLeft\\(yScale\\)/g), 'Your code should use the axisLeft() method with yScale passed as the argument.');" - }, - { - "text": "The y-axis g element should have a transform attribute to translate the axis by (60, 0).", - "testString": "assert($('g').eq(1).attr('transform').match(/translate\\(60\\s*?,\\s*?0\\)/g), 'The y-axis g element should have a transform attribute to translate the axis by (60, 0).');" - }, - { - "text": "Your code should call the yAxis.", - "testString": "assert(code.match(/\\.call\\(yAxis\\)/g), 'Your code should call the yAxis.');" - } - ], - "solutions": [], - "hints": [], - "releasedOn": "Feb 17, 2017", - "challengeType": 0, - "files": { - "indexhtml": { - "key": "indexhtml", - "ext": "html", - "name": "index", - "contents": [ - "", - " ", - "" - ], - "head": [], - "tail": [] - } - } - } - ] + "tests": [ + { + "text": "Your document should have 1 svg element.", + "testString": "assert($('svg').length == 1, 'Your document should have 1 svg element.');" + }, + { + "text": "The svg element should have a width attribute set to 500.", + "testString": "assert($('svg').attr('width') == '500', 'The svg element should have a width attribute set to 500.');" + }, + { + "text": "The svg element should have a height attribute set to 100.", + "testString": "assert($('svg').attr('height') == '100', 'The svg element should have a height attribute set to 100.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa8367417b2b2512bcc", + "title": "Display Shapes with SVG", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The last challenge created an svg element with a given width and height, which was visible because it had a background-color applied to it in the style tag. The code made space for the given width and height.", + "The next step is to create a shape to put in the svg area. There are a number of supported shapes in SVG, such as rectangles and circles. They are used to display data. For example, a rectangle (<rect>) SVG shape could create a bar in a bar chart.", + "When you place a shape into the svg area, you can specify where it goes with x and y coordinates. The origin point of (0, 0) is in the upper-left corner. Positive vales for x push the shape to the right, and positive values for y push the shape down from the origin point.", + "To place a shape in the middle of the 500 (width) x 100 (height) svg from last challenge, the x coordinate would be 250 and the y coordinate would be 50.", + "An SVG rect has four attributes. There are the x and y coordinates for where it is placed in the svg area. It also has a height and width to specify the size.", + "
", + "Add a rect shape to the svg using append(), and give it a width attribute of 25 and height attribute of 100. Also, give the rect x and y attributes each set to 0." + ], + "tests": [ + { + "text": "Your document should have 1 rect element.", + "testString": "assert($('rect').length == 1, 'Your document should have 1 rect element.');" + }, + { + "text": "The rect element should have a width attribute set to 25.", + "testString": "assert($('rect').attr('width') == '25', 'The rect element should have a width attribute set to 25.');" + }, + { + "text": "The rect element should have a height attribute set to 100.", + "testString": "assert($('rect').attr('height') == '100', 'The rect element should have a height attribute set to 100.');" + }, + { + "text": "The rect element should have an x attribute set to 0.", + "testString": "assert($('rect').attr('x') == '0', 'The rect element should have an x attribute set to 0.');" + }, + { + "text": "The rect element should have a y attribute set to 0.", + "testString": "assert($('rect').attr('y') == '0', 'The rect element should have a y attribute set to 0.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa8367417b2b2512bcd", + "title": "Create a Bar for Each Data Point in the Set", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The last challenge added only one rectangle to the svg element to represent a bar. Here, you'll combine what you've learned so far about data(), enter(), and SVG shapes to create and append a rectangle for each data point in dataset.", + "A previous challenge showed the format for how to create and append a div for each item in dataset:", + "
d3.select(\"body\").selectAll(\"div\")
  .data(dataset)
  .enter()
  .append(\"div\")
", + "There are a few differences working with rect elements instead of divs. The rects must be appended to an svg element, not directly to the body. Also, you need to tell D3 where to place each rect within the svg area. The bar placement will be covered in the next challenge.", + "
", + "Use the data(), enter(), and append() methods to create and append a rect for each item in dataset. The bars should display all on top of each other, this will be fixed in the next challenge." + ], + "tests": [ + { + "text": "Your document should have 9 rect elements.", + "testString": "assert($('rect').length == 9, 'Your document should have 9 rect elements.');" + }, + { + "text": "Your code should use the data() method.", + "testString": "assert(code.match(/\\.data/g), 'Your code should use the data() method.');" + }, + { + "text": "Your code should use the enter() method.", + "testString": "assert(code.match(/\\.enter/g), 'Your code should use the enter() method.');" + }, + { + "text": "Your code should use the append() method.", + "testString": "assert(code.match(/\\.append/g), 'Your code should use the append() method.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa9367417b2b2512bce", + "title": "Dynamically Set the Coordinates for Each Bar", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The last challenge created and appended a rectangle to the svg element for each point in dataset to represent a bar. Unfortunately, they were all stacked on top of each other.", + "The placement of a rectangle is handled by the x and y attributes. They tell D3 where to start drawing the shape in the svg area. The last challenge set them each to 0, so every bar was placed in the upper-left corner.", + "For a bar chart, all of the bars should sit on the same vertical level, which means the y value stays the same (at 0) for all bars. The x value, however, needs to change as you add new bars. Remember that larger x values push items farther to the right. As you go through the array elements in dataset, the x value should increase.", + "The attr() method in D3 accepts a callback function to dynamically set that attribute. The callback function takes two arguments, one for the data point itself (usually d) and one for the index of the data point in the array. The second argument for the index is optional. Here's the format:", + "
selection.attr(\"property\", (d, i) => {
  /*
  * d is the data point value
  * i is the index of the data point in the array
  */
})
", + "It's important to note that you do NOT need to write a for loop or use forEach() to iterate over the items in the data set. Recall that the data() method parses the data set, and any method that's chained after data() is run once for each item in the data set.", + "
", + "Change the x attribute callback function so it returns the index times 30.", + "Note
Each bar has a width of 25, so increasing each x value by 30 adds some space between the bars. Any value greater than 25 would work in this example." + ], + "tests": [ + { + "text": "The first rect should have an x value of 0.", + "testString": "assert($('rect').eq(0).attr('x') == '0', 'The first rect should have an x value of 0.');" + }, + { + "text": "The second rect should have an x value of 30.", + "testString": "assert($('rect').eq(1).attr('x') == '30', 'The second rect should have an x value of 30.');" + }, + { + "text": "The third rect should have an x value of 60.", + "testString": "assert($('rect').eq(2).attr('x') == '60', 'The third rect should have an x value of 60.');" + }, + { + "text": "The fourth rect should have an x value of 90.", + "testString": "assert($('rect').eq(3).attr('x') == '90', 'The fourth rect should have an x value of 90.');" + }, + { + "text": "The fifth rect should have an x value of 120.", + "testString": "assert($('rect').eq(4).attr('x') == '120', 'The fifth rect should have an x value of 120.');" + }, + { + "text": "The sixth rect should have an x value of 150.", + "testString": "assert($('rect').eq(5).attr('x') == '150', 'The sixth rect should have an x value of 150.');" + }, + { + "text": "The seventh rect should have an x value of 180.", + "testString": "assert($('rect').eq(6).attr('x') == '180', 'The seventh rect should have an x value of 180.');" + }, + { + "text": "The eighth rect should have an x value of 210.", + "testString": "assert($('rect').eq(7).attr('x') == '210', 'The eighth rect should have an x value of 210.');" + }, + { + "text": "The ninth rect should have an x value of 240.", + "testString": "assert($('rect').eq(8).attr('x') == '240', 'The ninth rect should have an x value of 240.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa9367417b2b2512bcf", + "title": "Dynamically Change the Height of Each Bar", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The height of each bar can be set to the value of the data point in the array, similar to how the x value was set dynamically.", + "
selection.attr(\"property\", (d, i) => {
  /*
  * d is the data point value
  * i is the index of the data point in the array
  */
})
", + "
", + "Change the callback function for the height attribute to return the data value times 3.", + "Note
Remember that multiplying all data points by the same constant scales the data (like zooming in). It helps to see the differences between bar values in this example." + ], + "tests": [ + { + "text": "The first rect should have a height of 36.", + "testString": "assert($('rect').eq(0).attr('height') == '36', 'The first rect should have a height of 36.');" + }, + { + "text": "The second rect should have a height of 93.", + "testString": "assert($('rect').eq(1).attr('height') == '93', 'The second rect should have a height of 93.');" + }, + { + "text": "The third rect should have a height of 66.", + "testString": "assert($('rect').eq(2).attr('height') == '66', 'The third rect should have a height of 66.');" + }, + { + "text": "The fourth rect should have a height of 51.", + "testString": "assert($('rect').eq(3).attr('height') == '51', 'The fourth rect should have a height of 51.');" + }, + { + "text": "The fifth rect should have a height of 75.", + "testString": "assert($('rect').eq(4).attr('height') == '75', 'The fifth rect should have a height of 75.');" + }, + { + "text": "The sixth rect should have a height of 54.", + "testString": "assert($('rect').eq(5).attr('height') == '54', 'The sixth rect should have a height of 54.');" + }, + { + "text": "The seventh rect should have a height of 87.", + "testString": "assert($('rect').eq(6).attr('height') == '87', 'The seventh rect should have a height of 87.');" + }, + { + "text": "The eighth rect should have a height of 42.", + "testString": "assert($('rect').eq(7).attr('height') == '42', 'The eighth rect should have a height of 42.');" + }, + { + "text": "The ninth rect should have a height of 27.", + "testString": "assert($('rect').eq(8).attr('height') == '27', 'The ninth rect should have a height of 27.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa9367417b2b2512bd0", + "title": "Invert SVG Elements", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "You may have noticed the bar chart looked like it's upside-down, or inverted. This is because of how SVG uses (x, y) coordinates.", + "In SVG, the origin point for the coordinates is in the upper-left corner. An x coordinate of 0 places a shape on the left edge of the SVG area. A y coordinate of 0 places a shape on the top edge of the SVG area. Higher x values push the rectangle to the right. Higher y values push the rectangle down.", + "To make the bars right-side-up, you need to change the way the y coordinate is calculated. It needs to account for both the height of the bar and the total height of the SVG area.", + "The height of the SVG area is 100. If you have a data point of 0 in the set, you would want the bar to start at the bottom of the SVG area (not the top). To do this, the y coordinate needs a value of 100. If the data point value were 1, you would start with a y coordinate of 100 to set the bar at the bottom. Then you need to account for the height of the bar of 1, so the final y coordinate would be 99.", + "The y coordinate that is y = heightOfSVG - heightOfBar would place the bars right-side-up.", + "
", + "Change the callback function for the y attribute to set the bars right-side-up. Remember that the height of the bar is 3 times the data value d.", + "Note
In general, the relationship is y = h - m * d, where m is the constant that scales the data points." + ], + "tests": [ + { + "text": "The first rect should have a y value of 64.", + "testString": "assert($('rect').eq(0).attr('y') == h - (dataset[0] * 3), 'The first rect should have a y value of 64.');" + }, + { + "text": "The second rect should have a y value of 7.", + "testString": "assert($('rect').eq(1).attr('y') == h - (dataset[1] * 3), 'The second rect should have a y value of 7.');" + }, + { + "text": "The third rect should have a y value of 34.", + "testString": "assert($('rect').eq(2).attr('y') == h - (dataset[2] * 3), 'The third rect should have a y value of 34.');" + }, + { + "text": "The fourth rect should have a y value of 49.", + "testString": "assert($('rect').eq(3).attr('y') == h - (dataset[3] * 3), 'The fourth rect should have a y value of 49.');" + }, + { + "text": "The fifth rect should have a y value of 25.", + "testString": "assert($('rect').eq(4).attr('y') == h - (dataset[4] * 3), 'The fifth rect should have a y value of 25.');" + }, + { + "text": "The sixth rect should have a y value of 46.", + "testString": "assert($('rect').eq(5).attr('y') == h - (dataset[5] * 3), 'The sixth rect should have a y value of 46.');" + }, + { + "text": "The seventh rect should have a y value of 13.", + "testString": "assert($('rect').eq(6).attr('y') == h - (dataset[6] * 3), 'The seventh rect should have a y value of 13.');" + }, + { + "text": "The eighth rect should have a y value of 58.", + "testString": "assert($('rect').eq(7).attr('y') == h - (dataset[7] * 3), 'The eighth rect should have a y value of 58.');" + }, + { + "text": "The ninth rect should have a y value of 73.", + "testString": "assert($('rect').eq(8).attr('y') == h - (dataset[8] * 3), 'The ninth rect should have a y value of 73.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fa9367417b2b2512bd1", + "title": "Change the Color of an SVG Element", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The bars are in the right position, but they are all the same black color. SVG has a way to change the color of the bars.", + "In SVG, a rect shape is colored with the fill attribute. It supports hex codes, color names, and rgb values, as well as more complex options like gradients and transparency.", + "
", + "Add an attr() method to set the \"fill\" of all the bars to the color \"navy\"." + ], + "tests": [ + { + "text": "The bars should all have a fill color of navy.", + "testString": "assert($('rect').css('fill') == \"rgb(0, 0, 128)\", 'The bars should all have a fill color of navy.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7faa367417b2b2512bd2", + "title": "Add Labels to D3 Elements", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 lets you label a graph element, such as a bar, using the SVG text element.", + "Like the rect element, a text element needs to have x and y attributes, to place it on the SVG canvas. It also needs to access the data to display those values.", + "D3 gives you a high level of control over how you label your bars.", + "
", + "The code in the editor already binds the data to each new text element. First, append text nodes to the svg. Next, add attributes for the x and y coordinates. They should be calculated the same way as the rect ones, except the y value for the text should make the label sit 3 units higher than the bar. Finally, use the D3 text() method to set the label equal to the data point value.", + "Note
For the label to sit higher than the bar, decide if the y value for the text should be 3 greater or 3 less than the y value for the bar." + ], + "tests": [ + { + "text": "The first text element should have a label of 12 and a y value of 61.", + "testString": "assert($('text').eq(0).text() == '12' && $('text').eq(0).attr('y') == '61', 'The first text element should have a label of 12 and a y value of 61.');" + }, + { + "text": "The second text element should have a label of 31 and a y value of 4.", + "testString": "assert($('text').eq(1).text() == '31' && $('text').eq(1).attr('y') == '4', 'The second text element should have a label of 31 and a y value of 4.');" + }, + { + "text": "The third text element should have a label of 22 and a y value of 31.", + "testString": "assert($('text').eq(2).text() == '22' && $('text').eq(2).attr('y') == '31', 'The third text element should have a label of 22 and a y value of 31.');" + }, + { + "text": "The fourth text element should have a label of 17 and a y value of 46.", + "testString": "assert($('text').eq(3).text() == '17' && $('text').eq(3).attr('y') == '46', 'The fourth text element should have a label of 17 and a y value of 46.');" + }, + { + "text": "The fifth text element should have a label of 25 and a y value of 22.", + "testString": "assert($('text').eq(4).text() == '25' && $('text').eq(4).attr('y') == '22', 'The fifth text element should have a label of 25 and a y value of 22.');" + }, + { + "text": "The sixth text element should have a label of 18 and a y value of 43.", + "testString": "assert($('text').eq(5).text() == '18' && $('text').eq(5).attr('y') == '43', 'The sixth text element should have a label of 18 and a y value of 43.');" + }, + { + "text": "The seventh text element should have a label of 29 and a y value of 10.", + "testString": "assert($('text').eq(6).text() == '29' && $('text').eq(6).attr('y') == '10', 'The seventh text element should have a label of 29 and a y value of 10.');" + }, + { + "text": "The eighth text element should have a label of 14 and a y value of 55.", + "testString": "assert($('text').eq(7).text() == '14' && $('text').eq(7).attr('y') == '55', 'The eighth text element should have a label of 14 and a y value of 55.');" + }, + { + "text": "The ninth text element should have a label of 9 and a y value of 70.", + "testString": "assert($('text').eq(8).text() == '9' && $('text').eq(8).attr('y') == '70', 'The ninth text element should have a label of 9 and a y value of 70.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7faa367417b2b2512bd3", + "title": "Style D3 Labels", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "D3 methods can add styles to the bar labels. The fill attribute sets the color of the text for a text node. The style() method sets CSS rules for other styles, such as \"font-family\" or \"font-size\".", + "
", + "Set the font-size of the text elements to 25px, and the color of the text to red." + ], + "tests": [ + { + "text": "The labels should all have a fill color of red.", + "testString": "assert($('text').css('fill') == 'rgb(255, 0, 0)', 'The labels should all have a fill color of red.');" + }, + { + "text": "The labels should all have a font-size of 25 pixels.", + "testString": "assert($('text').css('font-size') == '25px', 'The labels should all have a font-size of 25 pixels.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7faa367417b2b2512bd4", + "title": "Add a Hover Effect to a D3 Element", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "It's possible to add effects that highlight a bar when the user hovers over it with the mouse. So far, the styling for the rectangles is applied with the built-in D3 and SVG methods, but you can use CSS as well.", + "You set the CSS class on the SVG elements with the attr() method. Then the :hover pseudo-class for your new class holds the style rules for any hover effects.", + "
", + "Use the attr() method to add a class of bar to all the rect elements. This changes the fill color of the bar to brown when you mouse over it." + ], + "tests": [ + { + "text": "Your rect elements should have a class of bar.", + "testString": "assert($('rect').attr('class') == \"bar\", 'Your rect elements should have a class of bar.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7faa367417b2b2512bd6", + "title": "Add a Tooltip to a D3 Element", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "A tooltip shows more information about an item on a page when the user hovers over that item. There are several ways to add a tooltip to a visualization, this challenge uses the SVG title element.", + "title pairs with the text() method to dynamically add data to the bars.", + "
", + "Append a title element under each rect node. Then call the text() method with a callback function so the text displays the data value." + ], + "tests": [ + { + "text": "Your code should have 9 title elements.", + "testString": "assert($('title').length == 9, 'Your code should have 9 title elements.');" + }, + { + "text": "The first title element should have tooltip text of 12.", + "testString": "assert($('title').eq(0).text() == '12', 'The first title element should have tooltip text of 12.');" + }, + { + "text": "The second title element should have tooltip text of 31.", + "testString": "assert($('title').eq(1).text() == '31', 'The second title element should have tooltip text of 31.');" + }, + { + "text": "The third title element should have tooltip text of 22.", + "testString": "assert($('title').eq(2).text() == '22', 'The third title element should have tooltip text of 22.');" + }, + { + "text": "The fourth title element should have tooltip text of 17.", + "testString": "assert($('title').eq(3).text() == '17', 'The fourth title element should have tooltip text of 17.');" + }, + { + "text": "The fifth title element should have tooltip text of 25.", + "testString": "assert($('title').eq(4).text() == '25', 'The fifth title element should have tooltip text of 25.');" + }, + { + "text": "The sixth title element should have tooltip text of 18.", + "testString": "assert($('title').eq(5).text() == '18', 'The sixth title element should have tooltip text of 18.');" + }, + { + "text": "The seventh title element should have tooltip text of 29.", + "testString": "assert($('title').eq(6).text() == '29', 'The seventh title element should have tooltip text of 29.');" + }, + { + "text": "The eighth title element should have tooltip text of 14.", + "testString": "assert($('title').eq(7).text() == '14', 'The eighth title element should have tooltip text of 14.');" + }, + { + "text": "The ninth title element should have tooltip text of 9.", + "testString": "assert($('title').eq(8).text() == '9', 'The ninth title element should have tooltip text of 9.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fab367417b2b2512bd7", + "title": "Create a Scatterplot with SVG Circles", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "A scatter plot is another type of visualization. It usually uses circles to map data points, which have two values each. These values tie to the x and y axes, and are used to position the circle in the visualization.", + "SVG has a circle tag to create the circle shape. It works a lot like the rect elements you used for the bar chart.", + "
", + "Use the data(), enter(), and append() methods to bind dataset to new circle elements that are appended to the SVG canvas." + ], + "tests": [ + { + "text": "Your code should have 10 circle elements.", + "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fab367417b2b2512bd8", + "title": "Add Attributes to the Circle Elements", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The last challenge created the circle elements for each point in the dataset, and appended them to the SVG canvas. But D3 needs more information about the position and size of each circle to display them correctly.", + "A circle in SVG has three main attributes. The cx and cy attributes are the coordinates. They tell D3 where to position the center of the shape on the SVG canvas. The radius (r attribute) gives the size of the circle.", + "Just like the rect y coordinate, the cy attribute for a circle is measured from the top of the SVG canvas, not from the bottom.", + "All three attributes can use a callback function to set their values dynamically. Remember that all methods chained after data(dataset) run once per item in dataset. The d parameter in the callback function refers to the current item in dataset, which is an array for each point. You use bracket notation, like d[0], to access the values in that array.", + "
", + "Add cx, cy, and r attributes to the circle elements. The cx value should be the first number in the array for each item in dataset. The cy value should be based off the second number in the array, but make sure to show the chart right-side-up and not inverted. The r value should be 5 for all circles." + ], + "tests": [ + { + "text": "Your code should have 10 circle elements.", + "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" + }, + { + "text": "The first circle element should have a cx value of 34, a cy value of 422, and an r value of 5.", + "testString": "assert($('circle').eq(0).attr('cx') == '34' && $('circle').eq(0).attr('cy') == '422' && $('circle').eq(0).attr('r') == '5', 'The first circle element should have a cx value of 34, a cy value of 422, and an r value of 5.');" + }, + { + "text": "The second circle element should have a cx value of 109, a cy value of 220, and an r value of 5.", + "testString": "assert($('circle').eq(1).attr('cx') == '109' && $('circle').eq(1).attr('cy') == '220' && $('circle').eq(1).attr('r') == '5', 'The second circle element should have a cx value of 109, a cy value of 220, and an r value of 5.');" + }, + { + "text": "The third circle element should have a cx value of 310, a cy value of 380, and an r value of 5.", + "testString": "assert($('circle').eq(2).attr('cx') == '310' && $('circle').eq(2).attr('cy') == '380' && $('circle').eq(2).attr('r') == '5', 'The third circle element should have a cx value of 310, a cy value of 380, and an r value of 5.');" + }, + { + "text": "The fourth circle element should have a cx value of 79, a cy value of 89, and an r value of 5.", + "testString": "assert($('circle').eq(3).attr('cx') == '79' && $('circle').eq(3).attr('cy') == '89' && $('circle').eq(3).attr('r') == '5', 'The fourth circle element should have a cx value of 79, a cy value of 89, and an r value of 5.');" + }, + { + "text": "The fifth circle element should have a cx value of 420, a cy value of 280, and an r value of 5.", + "testString": "assert($('circle').eq(4).attr('cx') == '420' && $('circle').eq(4).attr('cy') == '280' && $('circle').eq(4).attr('r') == '5', 'The fifth circle element should have a cx value of 420, a cy value of 280, and an r value of 5.');" + }, + { + "text": "The sixth circle element should have a cx value of 233, a cy value of 355, and an r value of 5.", + "testString": "assert($('circle').eq(5).attr('cx') == '233' && $('circle').eq(5).attr('cy') == '355' && $('circle').eq(5).attr('r') == '5', 'The sixth circle element should have a cx value of 233, a cy value of 355, and an r value of 5.');" + }, + { + "text": "The seventh circle element should have a cx value of 333, a cy value of 404, and an r value of 5.", + "testString": "assert($('circle').eq(6).attr('cx') == '333' && $('circle').eq(6).attr('cy') == '404' && $('circle').eq(6).attr('r') == '5', 'The seventh circle element should have a cx value of 333, a cy value of 404, and an r value of 5.');" + }, + { + "text": "The eighth circle element should have a cx value of 222, a cy value of 167, and an r value of 5.", + "testString": "assert($('circle').eq(7).attr('cx') == '222' && $('circle').eq(7).attr('cy') == '167' && $('circle').eq(7).attr('r') == '5', 'The eighth circle element should have a cx value of 222, a cy value of 167, and an r value of 5.');" + }, + { + "text": "The ninth circle element should have a cx value of 78, a cy value of 180, and an r value of 5.", + "testString": "assert($('circle').eq(8).attr('cx') == '78' && $('circle').eq(8).attr('cy') == '180' && $('circle').eq(8).attr('r') == '5', 'The ninth circle element should have a cx value of 78, a cy value of 180, and an r value of 5.');" + }, + { + "text": "The tenth circle element should have a cx value of 21, a cy value of 377, and an r value of 5.", + "testString": "assert($('circle').eq(9).attr('cx') == '21' && $('circle').eq(9).attr('cy') == '377' && $('circle').eq(9).attr('r') == '5', 'The tenth circle element should have a cx value of 21, a cy value of 377, and an r value of 5.');" + } + ], + "solutions": [], + "hints": [ + "The cy attribute should be the second number of the data point array subtracted from the height of the SVG." + ], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fab367417b2b2512bd9", + "title": "Add Labels to Scatter Plot Circles", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "You can add text to create labels for the points in a scatter plot.", + "The goal is to display the comma-separated values for the first (x) and second (y) fields of each item in dataset.", + "The text nodes need x and y attributes to position it on the SVG canvas. In this challenge, the y value (which determines height) can use the same value that the circle uses for its cy attribute. The x value can be slightly larger than the cx value of the circle, so the label is visible. This will push the label to the right of the plotted point.", + "
", + "Label each point on the scatter plot using the text elements. The text of the label should be the two values separated by a comma and a space. For example, the label for the first point is \"34, 78\". Set the x attribute so it's 5 units more than the value you used for the cx attribute on the circle. Set the y attribute the same way that's used for the cy value on the circle." + ], + "tests": [ + { + "text": "Your code should have 10 text elements.", + "testString": "assert($('text').length == 10, 'Your code should have 10 text elements.');" + }, + { + "text": "The first label should have text of \"34, 78\", an x value of 39, and a y value of 422.", + "testString": "assert($('text').eq(0).text() == '34, 78' && $('text').eq(0).attr('x') == '39' && $('text').eq(0).attr('y') == '422', 'The first label should have text of \"34, 78\", an x value of 39, and a y value of 422.');" + }, + { + "text": "The second label should have text of \"109, 280\", an x value of 114, and a y value of 220.", + "testString": "assert($('text').eq(1).text() == '109, 280' && $('text').eq(1).attr('x') == '114' && $('text').eq(1).attr('y') == '220', 'The second label should have text of \"109, 280\", an x value of 114, and a y value of 220.');" + }, + { + "text": "The third label should have text of \"310, 120\", an x value of 315, and a y value of 380.", + "testString": "assert($('text').eq(2).text() == '310, 120' && $('text').eq(2).attr('x') == '315' && $('text').eq(2).attr('y') == '380', 'The third label should have text of \"310, 120\", an x value of 315, and a y value of 380.');" + }, + { + "text": "The fourth label should have text of \"79, 411\", an x value of 84, and a y value of 89.", + "testString": "assert($('text').eq(3).text() == '79, 411' && $('text').eq(3).attr('x') == '84' && $('text').eq(3).attr('y') == '89', 'The fourth label should have text of \"79, 411\", an x value of 84, and a y value of 89.');" + }, + { + "text": "The fifth label should have text of \"420, 220\", an x value of 425, and a y value of 280.", + "testString": "assert($('text').eq(4).text() == '420, 220' && $('text').eq(4).attr('x') == '425' && $('text').eq(4).attr('y') == '280', 'The fifth label should have text of \"420, 220\", an x value of 425, and a y value of 280.');" + }, + { + "text": "The sixth label should have text of \"233, 145\", an x value of 238, and a y value of 355.", + "testString": "assert($('text').eq(5).text() == '233, 145' && $('text').eq(5).attr('x') == '238' && $('text').eq(5).attr('y') == '355', 'The sixth label should have text of \"233, 145\", an x value of 238, and a y value of 355.');" + }, + { + "text": "The seventh label should have text of \"333, 96\", an x value of 338, and a y value of 404.", + "testString": "assert($('text').eq(6).text() == '333, 96' && $('text').eq(6).attr('x') == '338' && $('text').eq(6).attr('y') == '404', 'The seventh label should have text of \"333, 96\", an x value of 338, and a y value of 404.');" + }, + { + "text": "The eighth label should have text of \"222, 333\", an x value of 227, and a y value of 167.", + "testString": "assert($('text').eq(7).text() == '222, 333' && $('text').eq(7).attr('x') == '227' && $('text').eq(7).attr('y') == '167', 'The eighth label should have text of \"222, 333\", an x value of 227, and a y value of 167.');" + }, + { + "text": "The ninth label should have text of \"78, 320\", an x value of 83, and a y value of 180.", + "testString": "assert($('text').eq(8).text() == '78, 320' && $('text').eq(8).attr('x') == '83' && $('text').eq(8).attr('y') == '180', 'The ninth label should have text of \"78, 320\", an x value of 83, and a y value of 180.');" + }, + { + "text": "The tenth label should have text of \"21, 123\", an x value of 26, and a y value of 377.", + "testString": "assert($('text').eq(9).text() == '21, 123' && $('text').eq(9).attr('x') == '26' && $('text').eq(9).attr('y') == '377', 'The tenth label should have text of \"21, 123\", an x value of 26, and a y value of 377.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fab367417b2b2512bda", + "title": "Create a Linear Scale with D3", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The bar and scatter plot charts both plotted data directly onto the SVG canvas. However, if the height of a bar or one of the data points were larger than the SVG height or width values, it would go outside the SVG area.", + "In D3, there are scales to help plot data. Scales are functions that tell the program how to map a set of raw data points onto the pixels of the SVG canvas.", + "For example, say you have a 100x500-sized SVG canvas and you want to plot Gross Domestic Product (GDP) for a number of countries. The set of numbers would be in the billion or trillion-dollar range. You provide D3 a type of scale to tell it how to place the large GDP values into that 100x500-sized area.", + "It's unlikely you would plot raw data as-is. Before plotting it, you set the scale for your entire data set, so that the x and y values fit your canvas width and height.", + "D3 has several scale types. For a linear scale (usually used with quantitative data), there is the D3 method scaleLinear():", + " const scale = d3.scaleLinear()", + "By default, a scale uses the identity relationship. The value of the input is the same as the value of the output. A separate challenge covers how to change this.", + "
", + "Change the scale variable to create a linear scale. Then set the output variable to the scale called with an input argument of 50." + ], + "tests": [ + { + "text": "The text in the h2 should be 50.", + "testString": "assert($('h2').text() == '50', 'The text in the h2 should be 50.');" + }, + { + "text": "Your code should use the scaleLinear() method.", + "testString": "assert(code.match(/\\.scaleLinear/g), 'Your code should use the scaleLinear() method.');" + }, + { + "text": "The output variable should call scale with an argument of 50.", + "testString": "assert(output == 50 && code.match(/scale\\(\\s*?50\\s*?\\)/g), 'The output variable should call scale with an argument of 50.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fac367417b2b2512bdb", + "title": "Set a Domain and a Range on a Scale", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "By default, scales use the identity relationship - the input value maps to the output value. But scales can be much more flexible and interesting.", + "Say a data set has values ranging from 50 to 480. This is the input information for a scale, and is also known as the domain.", + "You want to map those points along the x axis on the SVG canvas, between 10 units and 500 units. This is the output information, which is also known as the range.", + "The domain() and range() methods set these values for the scale. Both methods take an array of at least two elements as an argument. Here's an example:", + "
// Set a domain
// The domain covers the set of input values
scale.domain([50, 480]);
// Set a range
// The range covers the set of output values
scale.range([10, 500]);
scale(50) // Returns 10
scale(480) // Returns 500
scale(325) // Returns 323.37
scale(750) // Returns 807.67
d3.scaleLinear()
", + "Notice that the scale uses the linear relationship between the domain and range values to figure out what the output should be for a given number. The minimum value in the domain (50) maps to the minimum value (10) in the range.", + "
", + "Create a scale and set its domain to [250, 500] and range to [10, 150].", + "Note
You can chain the domain() and range() methods onto the scale variable." + ], + "tests": [ + { + "text": "Your code should use the domain() method.", + "testString": "assert(code.match(/\\.domain/g), 'Your code should use the domain() method.');" + }, + { + "text": "The domain() of the scale should be set to [250, 500].", + "testString": "assert(JSON.stringify(scale.domain()) == JSON.stringify([250, 500]), 'The domain() of the scale should be set to [250, 500].');" + }, + { + "text": "Your code should use the range() method.", + "testString": "assert(code.match(/\\.range/g), 'Your code should use the range() method.');" + }, + { + "text": "The range() of the scale should be set to [10, 150].", + "testString": "assert(JSON.stringify(scale.range()) == JSON.stringify([10, 150]), 'The range() of the scale should be set to [10, 150].');" + }, + { + "text": "The text in the h2 should be -102.", + "testString": "assert($('h2').text() == '-102', 'The text in the h2 should be -102.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fac367417b2b2512bdc", + "title": "Use the d3.max and d3.min Functions to Find Minimum and Maximum Values in a Dataset", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The D3 methods domain() and range() set that information for your scale based on the data. There are a couple methods to make that easier.", + "Often when you set the domain, you'll want to use the minimum and maximum values within the data set. Trying to find these values manually, especially in a large data set, may cause errors.", + "D3 has two methods - min() and max() to return this information. Here's an example:", + "
const exampleData = [34, 234, 73, 90, 6, 52];
d3.min(exampleData) // Returns 6
d3.max(exampleData) // Returns 234
", + "A dataset may have nested arrays, like the [x, y] coordinate pairs that were in the scatter plot example. In that case, you need to tell D3 how to calculate the maximum and minimum.", + "Fortunately, both the min() and max() methods take a callback function.", + "In this example, the callback function's argument d is for the current inner array. The callback needs to return the element from the inner array (the x or y value) over which you want to compute the maximum or minimum. Here's an example for how to find the min and max values with an array of arrays:", + "
const locationData = [[1, 7],[6, 3],[8, 3]];
// Returns the smallest number out of the first elements
const minX = d3.min(locationData, (d) => d[0]);
// minX compared 1, 6, and 8 and is set to 1
", + "
", + "The positionData variable holds a 3-dimensional (3D) array. Use a D3 method to find the maximum value of the z coordinate (the third value) from the arrays and save it in the output variable.", + "Note
Fun fact - D3 can plot 3D arrays." + ], + "tests": [ + { + "text": "The text in the h2 should be 8.", + "testString": "assert(output == 8 && $('h2').text() == '8', 'The text in the h2 should be 8.');" + }, + { + "text": "Your code should use the max() method.", + "testString": "assert(code.match(/\\.max/g), 'Your code should use the max() method.')" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fac367417b2b2512bdd", + "title": "Use Dynamic Scales", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "The D3 min() and max() methods are useful to help set the scale.", + "Given a complex data set, one priority is to set the scale so the visualization fits the SVG container's width and height. You want all the data plotted inside the SVG canvas so it's visible on the web page.", + "The example below sets the x-axis scale for scatter plot data. The domain() method passes information to the scale about the raw data values for the plot. The range() method gives it information about the actual space on the web page for the visualization.", + "In the example, the domain goes from 0 to the maximum in the set. It uses the max() method with a callback function based on the x values in the arrays. The range uses the SVG canvas' width (w), but it includes some padding, too. This puts space between the scatter plot dots and the edge of the SVG canvas.", + "
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 between the SVG canvas boundary and the plot
const padding = 30;
const xScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, (d) => d[0])])
  .range([padding, w - padding]);
", + "The padding may be confusing at first. Picture the x-axis as a horizontal line from 0 to 500 (the width value for the SVG canvas). Including the padding in the range() method forces the plot to start at 30 along that line (instead of 0), and end at 470 (instead of 500).", + "
", + "Use the yScale variable to create a linear y-axis scale. The domain should start at zero and go to the maximum y value in the set. The range should use the SVG height (h) and include padding.", + "Note
Remember to keep the plot right-side-up. When you set the range for the y coordinates, the higher value (height minus padding) is the first argument, and the lower value is the second argument." + ], + "tests": [ + { + "text": "The text in the h2 should be 30.", + "testString": "assert(output == 30 && $('h2').text() == '30', 'The text in the h2 should be 30.');" + }, + { + "text": "The domain() of yScale should be equivalent to [0, 411].", + "testString": "assert(JSON.stringify(yScale.domain()) == JSON.stringify([0, 411]), 'The domain() of yScale should be equivalent to [0, 411].');" + }, + { + "text": "The range() of yScale should be equivalent to [470, 30].", + "testString": "assert(JSON.stringify(yScale.range()) == JSON.stringify([470, 30]), 'The range() of yScale should be equivalent to [470, 30].');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fac367417b2b2512bde", + "title": "Use a Pre-Defined Scale to Place Elements", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "With the scales set up, it's time to map the scatter plot again. The scales are like processing functions that turn the x and y raw data into values that fit and render correctly on the SVG canvas. They keep the data within the screen's plotting area.", + "You set the coordinate attribute values for an SVG shape with the scaling function. This includes x and y attributes for rect or text elements, or cx and cy for circles. Here's an example:", + "
shape
  .attr(\"x\", (d) => xScale(d[0]))
", + "Scales set shape coordinate attributes to place the data points onto the SVG canvas. You don't need to apply scales when you display the actual data value, for example, in the text() method for a tooltip or label.", + "
", + "Use xScale and yScale to position both the circle and text shapes onto the SVG canvas. For the circles, apply the scales to set the cx and cy attributes. Give them a radius of 5 units, too.", + "For the text elements, apply the scales to set the x and y attributes. The labels should be offset to the right of the dots. To do this, add 10 units to the x data value before passing it to the xScale." + ], + "tests": [ + { + "text": "Your code should have 10 circle elements.", + "testString": "assert($('circle').length == 10, 'Your code should have 10 circle elements.');" + }, + { + "text": "The first circle element should have a cx value of approximately 91 and a cy value of approximately 368 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(0).attr('cx')) == '91' && Math.round($('circle').eq(0).attr('cy')) == '368' && $('circle').eq(0).attr('r') == '5', 'The first circle element should have a cx value of approximately 91 and a cy value of approximately 368 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The second circle element should have a cx value of approximately 159 and a cy value of approximately 181 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(1).attr('cx')) == '159' && Math.round($('circle').eq(1).attr('cy')) == '181' && $('circle').eq(1).attr('r') == '5', 'The second circle element should have a cx value of approximately 159 and a cy value of approximately 181 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The third circle element should have a cx value of approximately 340 and a cy value of approximately 329 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(2).attr('cx')) == '340' && Math.round($('circle').eq(2).attr('cy')) == '329' && $('circle').eq(2).attr('r') == '5', 'The third circle element should have a cx value of approximately 340 and a cy value of approximately 329 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The fourth circle element should have a cx value of approximately 131 and a cy value of approximately 60 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(3).attr('cx')) == '131' && Math.round($('circle').eq(3).attr('cy')) == '60' && $('circle').eq(3).attr('r') == '5', 'The fourth circle element should have a cx value of approximately 131 and a cy value of approximately 60 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The fifth circle element should have a cx value of approximately 440 and a cy value of approximately 237 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(4).attr('cx')) == '440' && Math.round($('circle').eq(4).attr('cy')) == '237' && $('circle').eq(4).attr('r') == '5', 'The fifth circle element should have a cx value of approximately 440 and a cy value of approximately 237 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The sixth circle element should have a cx value of approximately 271 and a cy value of approximately 306 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(5).attr('cx')) == '271' && Math.round($('circle').eq(5).attr('cy')) == '306' && $('circle').eq(5).attr('r') == '5', 'The sixth circle element should have a cx value of approximately 271 and a cy value of approximately 306 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The seventh circle element should have a cx value of approximately 361 and a cy value of approximately 351 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(6).attr('cx')) == '361' && Math.round($('circle').eq(6).attr('cy')) == '351' && $('circle').eq(6).attr('r') == '5', 'The seventh circle element should have a cx value of approximately 361 and a cy value of approximately 351 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The eighth circle element should have a cx value of approximately 261 and a cy value of approximately 132 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(7).attr('cx')) == '261' && Math.round($('circle').eq(7).attr('cy')) == '132' && $('circle').eq(7).attr('r') == '5', 'The eighth circle element should have a cx value of approximately 261 and a cy value of approximately 132 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The ninth circle element should have a cx value of approximately 131 and a cy value of approximately 144 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(8).attr('cx')) == '131' && Math.round($('circle').eq(8).attr('cy')) == '144' && $('circle').eq(8).attr('r') == '5', 'The ninth circle element should have a cx value of approximately 131 and a cy value of approximately 144 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "The tenth circle element should have a cx value of approximately 79 and a cy value of approximately 326 after applying the scales. It should also have an r value of 5.", + "testString": "assert(Math.round($('circle').eq(9).attr('cx')) == '79' && Math.round($('circle').eq(9).attr('cy')) == '326' && $('circle').eq(9).attr('r') == '5', 'The tenth circle element should have a cx value of approximately 79 and a cy value of approximately 326 after applying the scales. It should also have an r value of 5.');" + }, + { + "text": "Your code should have 10 text elements.", + "testString": "assert($('text').length == 10, 'Your code should have 10 text elements.');" + }, + { + "text": "The first label should have an x value of approximately 100 and a y value of approximately 368 after applying the scales.", + "testString": "assert(Math.round($('text').eq(0).attr('x')) == '100' && Math.round($('text').eq(0).attr('y')) == '368', 'The first label should have an x value of approximately 100 and a y value of approximately 368 after applying the scales.');" + }, + { + "text": "The second label should have an x value of approximately 168 and a y value of approximately 181 after applying the scales.", + "testString": "assert(Math.round($('text').eq(1).attr('x')) == '168' && Math.round($('text').eq(1).attr('y')) == '181', 'The second label should have an x value of approximately 168 and a y value of approximately 181 after applying the scales.');" + }, + { + "text": "The third label should have an x value of approximately 350 and a y value of approximately 329 after applying the scales.", + "testString": "assert(Math.round($('text').eq(2).attr('x')) == '350' && Math.round($('text').eq(2).attr('y')) == '329', 'The third label should have an x value of approximately 350 and a y value of approximately 329 after applying the scales.');" + }, + { + "text": "The fourth label should have an x value of approximately 141 and a y value of approximately 60 after applying the scales.", + "testString": "assert(Math.round($('text').eq(3).attr('x')) == '141' && Math.round($('text').eq(3).attr('y')) == '60', 'The fourth label should have an x value of approximately 141 and a y value of approximately 60 after applying the scales.');" + }, + { + "text": "The fifth label should have an x value of approximately 449 and a y value of approximately 237 after applying the scales.", + "testString": "assert(Math.round($('text').eq(4).attr('x')) == '449' && Math.round($('text').eq(4).attr('y')) == '237', 'The fifth label should have an x value of approximately 449 and a y value of approximately 237 after applying the scales.');" + }, + { + "text": "The sixth label should have an x value of approximately 280 and a y value of approximately 306 after applying the scales.", + "testString": "assert(Math.round($('text').eq(5).attr('x')) == '280' && Math.round($('text').eq(5).attr('y')) == '306', 'The sixth label should have an x value of approximately 280 and a y value of approximately 306 after applying the scales.');" + }, + { + "text": "The seventh label should have an x value of approximately 370 and a y value of approximately 351 after applying the scales.", + "testString": "assert(Math.round($('text').eq(6).attr('x')) == '370' && Math.round($('text').eq(6).attr('y')) == '351', 'The seventh label should have an x value of approximately 370 and a y value of approximately 351 after applying the scales.');" + }, + { + "text": "The eighth label should have an x value of approximately 270 and a y value of approximately 132 after applying the scales.", + "testString": "assert(Math.round($('text').eq(7).attr('x')) == '270' && Math.round($('text').eq(7).attr('y')) == '132', 'The eighth label should have an x value of approximately 270 and a y value of approximately 132 after applying the scales.');" + }, + { + "text": "The ninth label should have an x value of approximately 140 and a y value of approximately 144 after applying the scales.", + "testString": "assert(Math.round($('text').eq(8).attr('x')) == '140' && Math.round($('text').eq(8).attr('y')) == '144', 'The ninth label should have an x value of approximately 140 and a y value of approximately 144 after applying the scales.');" + }, + { + "text": "The tenth label should have an x value of approximately 88 and a y value of approximately 326 after applying the scales.", + "testString": "assert(Math.round($('text').eq(9).attr('x')) == '88' && Math.round($('text').eq(9).attr('y')) == '326', 'The tenth label should have an x value of approximately 88 and a y value of approximately 326 after applying the scales.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + }, + { + "id": "587d7fad367417b2b2512bdf", + "title": "Add Axes to a Visualization", + "required": [ + { + "src": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js" + } + ], + "description": [ + "Another way to improve the scatter plot is to add an x-axis and a y-axis.", + "D3 has two methods axisLeft() and axisBottom() to render the y and x axes, respectively. (Axes is the plural form of axis). Here's an example to create the x-axis based on the xScale in the previous challenges:", + "const xAxis = d3.axisBottom(xScale);", + "The next step is to render the axis on the SVG canvas. To do so, you can use a general SVG component, the g element. The g stands for group.", + "Unlike rect, circle, and text, an axis is just a straight line when it's rendered. Because it is a simple shape, using g works.", + "The last step is to apply a transform attribute to position the axis on the SVG canvas in the right place. Otherwise, the line would render along the border of SVG canvas and wouldn't be visible.", + "SVG supports different types of transforms, but positioning an axis needs translate. When it's applied to the g element, it moves the whole group over and down by the given amounts. Here's an example:", + "
const xAxis = d3.axisBottom(xScale);

svg.append(\"g\")
   .attr(\"transform\", \"translate(0, \" + (h - padding) + \")\")
   .call(xAxis);
", + "The above code places the x-axis at the bottom of the SVG canvas. Then it's passed as an argument to the call() method.", + "The y-axis works is the same way, except the translate argument is in the form (x, 0). Because translate is a string in the attr() method above, you can use concatenation to include variable values for its arguments.", + "
", + "The scatter plot now has an x-axis. Create a y-axis in a variable named yAxis using the axisLeft() method. Then render the axis using a g element. Make sure to use a transform attribute to translate the axis by the amount of padding units right, and 0 units down. Remember to call() the axis." + ], + "tests": [ + { + "text": "Your code should use the axisLeft() method with yScale passed as the argument.", + "testString": "assert(code.match(/\\.axisLeft\\(yScale\\)/g), 'Your code should use the axisLeft() method with yScale passed as the argument.');" + }, + { + "text": "The y-axis g element should have a transform attribute to translate the axis by (60, 0).", + "testString": "assert($('g').eq(1).attr('transform').match(/translate\\(60\\s*?,\\s*?0\\)/g), 'The y-axis g element should have a transform attribute to translate the axis by (60, 0).');" + }, + { + "text": "Your code should call the yAxis.", + "testString": "assert(code.match(/\\.call\\(yAxis\\)/g), 'Your code should call the yAxis.');" + } + ], + "solutions": [], + "hints": [], + "releasedOn": "Feb 17, 2017", + "challengeType": 0, + "files": { + "indexhtml": { + "key": "indexhtml", + "ext": "html", + "name": "index", + "contents": [ + "", + " ", + "" + ], + "head": [], + "tail": [] + } + } + } + ] } \ No newline at end of file