- 00:00:00 hi this is actually the third video of a
- 00:00:02 short mini series on the3js
- 00:00:05 in the first two videos we learned what
- 00:00:07 d3 is and we had a closer look at
- 00:00:10 data in d3js and how we joined that with
- 00:00:12 dom
- 00:00:13 elements now in this video we're going
- 00:00:15 to take what we learned there
- 00:00:17 we're going to build a simple project or
- 00:00:19 a simple demo
- 00:00:20 where we will utilize the 3.js to build
- 00:00:22 an interactive chart
- 00:00:24 and use what we learned in the last two
- 00:00:28 videos
- 00:00:33 so let's get starting building this
- 00:00:35 little demo
- 00:00:36 project or little demo chart dashboard
- 00:00:39 whatever you want to call it
- 00:00:40 with d3.js for that below the video
- 00:00:43 you'll find a link to a github
- 00:00:44 repository with this
- 00:00:45 starting setup which is a html file with
- 00:00:48 a very
- 00:00:49 rough html structure in there some divs
- 00:00:52 a svg element
- 00:00:53 unordered list some css styles which are
- 00:00:56 defined
- 00:00:57 here in this file and in app.js you find
- 00:01:00 some dummy data which i prepared in
- 00:01:01 advance
- 00:01:02 now that's where we are going to pick up
- 00:01:04 on my goal here
- 00:01:06 is to render a nice little bar chart
- 00:01:10 where we render that data and next to
- 00:01:12 that bar
- 00:01:13 chart all those um items
- 00:01:17 so all those controls for usa india
- 00:01:20 china germany
- 00:01:21 where we can basically toggle those
- 00:01:23 items so that we can add or remove them
- 00:01:25 dynamically
- 00:01:26 to and from the bar chart that's the
- 00:01:28 goal here
- 00:01:29 so therefore it's of course up to us
- 00:01:31 with what we want to start
- 00:01:32 i will start with the chart so i will
- 00:01:35 make sure that we actually have a nice
- 00:01:37 chart
- 00:01:37 before we then later make sure we can
- 00:01:39 add and remove stuff
- 00:01:40 now for that chart i'm going to set up
- 00:01:43 some
- 00:01:44 constants here for example a chart width
- 00:01:47 of let's say 600 and that will be
- 00:01:50 interpreted as pixels later
- 00:01:53 and a chart height of let's say
- 00:01:56 400 and this will be our chart
- 00:01:58 dimensions
- 00:01:59 for the chart we're going to render so
- 00:02:02 next we can render the chart
- 00:02:04 and for that i'll first of all define my
- 00:02:06 chart container
- 00:02:08 and get access to my svg element here
- 00:02:11 in the dom so to this element here
- 00:02:15 with d3 select that will be the
- 00:02:18 container that holds my chart in the end
- 00:02:20 i will render all the chart
- 00:02:22 elements so all the bars for example in
- 00:02:25 that svg
- 00:02:27 now on that svg i therefore want to set
- 00:02:30 some attributes
- 00:02:31 i want to set the width attribute so
- 00:02:34 that's basically as if you manually
- 00:02:36 added with here on the svg but we're
- 00:02:39 going to do it in code
- 00:02:40 and i'll set that to my chart
- 00:02:44 width so to this constant defined up
- 00:02:46 there
- 00:02:48 and let's move that into a new line to
- 00:02:50 make it easier to read
- 00:02:51 and then also add the height here and
- 00:02:54 unsurprisingly set this to
- 00:02:56 chart height so with that we define the
- 00:02:59 chart
- 00:03:00 area and we can refer back to this chart
- 00:03:02 container which holds
- 00:03:04 a pointer at this selection to then well
- 00:03:07 render stuff into it and that's actually
- 00:03:10 what we're going to do next
- 00:03:12 cue this chart container i want to
- 00:03:14 append a new
- 00:03:16 element and that will be a g element now
- 00:03:18 this is a regular html element
- 00:03:20 it is a svg element to be precise and it
- 00:03:23 creates a so-called
- 00:03:24 group in svg and we can use this to
- 00:03:27 group together
- 00:03:28 other svg elements and i simply want to
- 00:03:30 group all my bars together later
- 00:03:32 so that's why i create this new group
- 00:03:33 here and i will store this in my
- 00:03:36 chart constant because that group will
- 00:03:39 hold the actual
- 00:03:40 chart the actual chart elements so to
- 00:03:42 say
- 00:03:43 so that's just a little helper constant
- 00:03:45 the more interesting part happens now
- 00:03:48 on this chart i now want to select all
- 00:03:51 elements with a bar class and such
- 00:03:54 elements don't exist yet but we're going
- 00:03:55 to add them
- 00:03:56 and that's what i mentioned in the first
- 00:03:58 two videos of this mini series
- 00:04:00 especially in the data join video i
- 00:04:02 explained this again
- 00:04:04 here we select elements that might not
- 00:04:06 exist yet
- 00:04:07 to then join data with them so that d3js
- 00:04:10 is able to figure out
- 00:04:11 which elements are missing for the
- 00:04:13 provided data so that's the next step
- 00:04:15 then i selected those elements
- 00:04:17 now as a next step we can call data on
- 00:04:21 those selected elements to join them
- 00:04:23 with some data and the data i wanted to
- 00:04:25 join them with
- 00:04:26 is my dummy data here of course so this
- 00:04:28 data here
- 00:04:31 now that allows the3js to find out which
- 00:04:34 data is missing or
- 00:04:35 redundant and we get access to the
- 00:04:37 missing data by calling
- 00:04:38 enter so this now basically gives us the
- 00:04:41 information
- 00:04:42 d3j has derived for which elements are
- 00:04:45 missing
- 00:04:46 and therefore now on enter we can call
- 00:04:48 append and append
- 00:04:50 new elements and i want to append rect
- 00:04:53 elements that's also a regular html or
- 00:04:56 to be precise
- 00:04:57 svg element for creating a rectangle
- 00:05:01 now on those rectangles i also want to
- 00:05:03 add a class
- 00:05:05 and that should be the bar class of
- 00:05:07 course because we're using
- 00:05:08 the bar class here for selecting the
- 00:05:10 elements so we should also append
- 00:05:12 elements with such a class here
- 00:05:15 so this will now render or add new
- 00:05:17 rectangles with that class
- 00:05:19 for all the missing data points now with
- 00:05:22 that
- 00:05:23 if i reload here we see the chart area
- 00:05:26 but we don't see any bars in there now
- 00:05:28 if i select this
- 00:05:30 we do actually see though that
- 00:05:31 rectangles are being rendered in there
- 00:05:34 they're just not visible which is of
- 00:05:36 course not ideal now they are not
- 00:05:38 visible even though they have a fill
- 00:05:40 color
- 00:05:41 so the reason why they're not visible of
- 00:05:43 course is that they're basically having
- 00:05:45 a size of zero by
- 00:05:46 zero so they have no width and no height
- 00:05:50 now we need to change that and we can
- 00:05:52 change this on the rendered bars here
- 00:05:54 by adding some attributes for example a
- 00:05:56 width attribute
- 00:05:58 but also of course a height attribute
- 00:06:01 now the question is which values should
- 00:06:03 be entered here
- 00:06:04 and i showed this in the very first
- 00:06:05 video of this mini series already
- 00:06:08 we can hard code some values here but we
- 00:06:10 actually want to derive the width and
- 00:06:12 the height
- 00:06:13 based on the number of data points we
- 00:06:14 have and their concrete data values
- 00:06:18 and we don't just want to take those
- 00:06:19 values and hard code the number of data
- 00:06:22 points or anything like that
- 00:06:24 instead we want to let d3js provide a
- 00:06:27 scale for us
- 00:06:28 we want to let d3js translate that
- 00:06:30 number of data points
- 00:06:32 and the values to concrete widths and
- 00:06:35 heights
- 00:06:35 that fit into our available space here
- 00:06:39 and for that we can create the freejs
- 00:06:42 scales
- 00:06:43 now we already have the right import for
- 00:06:45 that in the starting setup
- 00:06:47 so now all we need to do maybe
- 00:06:51 here after setting up those constants is
- 00:06:53 we can create a
- 00:06:54 x scale or maybe just x
- 00:06:58 constant which holds a function
- 00:07:01 created with the help of d3 scale
- 00:07:04 and then here for the x-axis i want to
- 00:07:06 create a band scale
- 00:07:08 which basically means we'll have some
- 00:07:10 equally sized items which are
- 00:07:12 distributed along the x-axis
- 00:07:15 and for the y axis
- 00:07:18 i'll create a linear scale so that we
- 00:07:21 basically are able to
- 00:07:23 well reflect those values with
- 00:07:26 appropriate relations to each other
- 00:07:28 along the y axis
- 00:07:31 now on scale band i'll call range round
- 00:07:35 to define the available range and range
- 00:07:38 round
- 00:07:38 simply rounds the actual calculated
- 00:07:41 numbers then
- 00:07:43 and i'll define the range here to go
- 00:07:45 from zero so all the way on the left
- 00:07:48 on the x-axis all the way up to chart
- 00:07:51 with because we well do have that
- 00:07:54 maximum width available
- 00:07:57 and i also want to have some padding and
- 00:07:58 with the padding function or the padding
- 00:08:00 method here
- 00:08:01 we can define that we overall want to
- 00:08:03 have 10
- 00:08:04 of padding not between each item but
- 00:08:06 instead in total 10
- 00:08:08 of the space basically is reserved for
- 00:08:10 padding and d3js with this scale band
- 00:08:14 method and so on
- 00:08:15 will automatically figure out how much
- 00:08:17 space should be between the items
- 00:08:19 it will count the available items and do
- 00:08:21 all of that for us essentially
- 00:08:23 now for a scale linear i also define a
- 00:08:26 range
- 00:08:26 not a rounded one but here i want exact
- 00:08:29 values to be as exact as possible on the
- 00:08:31 y-axis
- 00:08:33 and here the range has to start with the
- 00:08:35 highest value
- 00:08:36 because as you learned in the earlier
- 00:08:38 videos the coordinate
- 00:08:40 system that d3js uses behind the scenes
- 00:08:43 starts in the top left corner
- 00:08:45 so therefore here we start at chart
- 00:08:47 height and go all the way to
- 00:08:49 zero like this so these are our
- 00:08:51 available ranges here
- 00:08:54 and with those ranges defined let me
- 00:08:57 close that so that we have more space
- 00:08:59 with those ranges to find
- 00:09:00 we also need to do one other thing which
- 00:09:03 we could he
- 00:09:04 do here by chaining but which i'll do in
- 00:09:06 a separate line we also
- 00:09:09 want to define our domains
- 00:09:12 so on the x object which we have here in
- 00:09:16 the end
- 00:09:16 i will call domain this allows us to
- 00:09:20 specify
- 00:09:21 how many items should be positioned here
- 00:09:23 or which data should be
- 00:09:25 scaled that's in the end what domain
- 00:09:26 does and here
- 00:09:28 i'm basically pointing at my dummy data
- 00:09:31 and i want to
- 00:09:33 transform this array of objects into an
- 00:09:35 array of
- 00:09:36 strings so that we have some unique
- 00:09:38 identifiers that the 3.js can use here
- 00:09:42 and for that i'll simply extract my
- 00:09:44 region here from every data point
- 00:09:46 and that basically tells d3js that for
- 00:09:49 this
- 00:09:50 x scale it should take this array and it
- 00:09:53 will see that there are four items in
- 00:09:54 there
- 00:09:55 and it will then have all the
- 00:09:56 information it needs to create the
- 00:09:58 different positions along the x-axis for
- 00:10:01 that
- 00:10:01 data now for y
- 00:10:04 i'll do something similar there i also
- 00:10:07 specify a domain
- 00:10:08 but here i want to have a domain that
- 00:10:10 goes from zero
- 00:10:12 to some value that is a bit bigger than
- 00:10:15 our biggest value here
- 00:10:16 so that this is the full available
- 00:10:18 y-axis that
- 00:10:20 would support a lowest value of 0 and
- 00:10:23 let's say a highest value of
- 00:10:25 15 or to be a bit more dynamic and
- 00:10:28 flexible
- 00:10:29 we can use a built in helper method on
- 00:10:31 the d3
- 00:10:32 object the max method to which we can
- 00:10:35 pass our
- 00:10:36 data and then as a second argument we
- 00:10:39 provide a function
- 00:10:40 that the freejs should execute on every
- 00:10:43 data point
- 00:10:44 to tell the fridges which property in
- 00:10:47 our data
- 00:10:48 is the one it should use for calculating
- 00:10:50 the max value
- 00:10:51 so here we can then for example reach
- 00:10:55 out
- 00:10:55 to the value and with this d3js we'll
- 00:10:58 have a look at all the values
- 00:10:59 in all our data points and basically use
- 00:11:02 the biggest
- 00:11:03 value here and extract that and use that
- 00:11:06 here therefore as a upper bound
- 00:11:08 now since i want to have some empty
- 00:11:09 space above my tallest bar
- 00:11:12 i will add plus three here so that
- 00:11:15 simply there is some extra headroom
- 00:11:17 above my well biggest value which was
- 00:11:19 derived from my data
- 00:11:21 so with that we defined the domains
- 00:11:23 which will be mapped into the actual
- 00:11:25 available widths and heights
- 00:11:27 and x and y are now functions which we
- 00:11:30 can call
- 00:11:31 to get the concrete positions along the
- 00:11:33 x and y
- 00:11:34 axis and with that we also can get the
- 00:11:37 width and the height
- 00:11:38 for the width so for the x-axis we can
- 00:11:42 call
- 00:11:42 x bandwidth this method basically gives
- 00:11:45 us an equal width for all bars
- 00:11:48 and it simply takes the total number of
- 00:11:49 data points the available width
- 00:11:51 and calculates the width of every bar
- 00:11:54 taking into account the padding
- 00:11:55 that's what the bandwidth method in the
- 00:11:57 end does and gives us
- 00:11:59 now for the height since we start the
- 00:12:02 top left corner
- 00:12:03 we'll take the chart height and deduct
- 00:12:07 the calculated y position from it so
- 00:12:10 2y i then want to pass my concrete
- 00:12:14 value for the different data points and
- 00:12:16 therefore here for the height i actually
- 00:12:17 pass in a function
- 00:12:19 an anonymous function that gets the data
- 00:12:22 for this data point
- 00:12:23 for which this react rectangle was
- 00:12:25 rendered and we can then feed data dot
- 00:12:28 value here to y
- 00:12:29 to calculate the position along the y
- 00:12:31 axis for this element
- 00:12:33 and deduct this from the chart height
- 00:12:36 because we start in that
- 00:12:37 top left corner and now we also want to
- 00:12:40 set the x
- 00:12:41 and y positions of course so that we
- 00:12:44 don't just have to with the width and
- 00:12:45 the height but also where the item
- 00:12:47 should be
- 00:12:48 and for this in both cases we get access
- 00:12:50 to our data point
- 00:12:52 and then call x data.region
- 00:12:55 for the x axis we use the region because
- 00:12:58 that is what we used here
- 00:13:00 for the domain and for the y
- 00:13:04 axis we always refer to the value so
- 00:13:06 here we call
- 00:13:07 y for data.value and now with that all
- 00:13:11 out of the way with this code added if
- 00:13:13 we now reload here
- 00:13:14 we see our nice bar chart here
- 00:13:17 now this is not too bad but i think some
- 00:13:20 labels would be nice
- 00:13:21 and with that i mean labels on the
- 00:13:23 x-axis to see which country belongs to
- 00:13:25 which bar
- 00:13:26 or which bar belongs to which country i
- 00:13:28 guess and also labels above the
- 00:13:32 bars so that we can see which value is
- 00:13:34 being charted here
- 00:13:35 now let's start with the labels above
- 00:13:38 the bars
- 00:13:40 for that we can reach out to our chart
- 00:13:42 again
- 00:13:43 and here select all elements with a
- 00:13:45 label class
- 00:13:46 again we have no such elements yet but
- 00:13:48 that will change
- 00:13:50 because with the data method we're going
- 00:13:51 to join our labels with the dummy data
- 00:13:56 and now we want to find out which data
- 00:13:58 is missing with enter
- 00:14:00 and for every missing element i append
- 00:14:03 some
- 00:14:04 text now text here is simply
- 00:14:07 another html or svg element for
- 00:14:10 rendering text inside of svg so this is
- 00:14:12 not the text which will be
- 00:14:14 output this adds a text element instead
- 00:14:17 now on that text element we can call the
- 00:14:19 text method to set
- 00:14:20 the actual text that should be displayed
- 00:14:23 and that text of course depends on my
- 00:14:24 data point
- 00:14:25 and there i want to retrieve the value
- 00:14:29 like this now if we save that and reload
- 00:14:34 we don't see the labels though now the
- 00:14:37 labels are there
- 00:14:40 you can see them here very small here at
- 00:14:44 the top
- 00:14:44 but they're not visible because they sit
- 00:14:46 in the top left corner
- 00:14:48 i mentioned that the coordinate system
- 00:14:50 starts there and we have to move them to
- 00:14:52 the right positions therefore
- 00:14:54 now again we do this with adder for a
- 00:14:57 setting
- 00:14:57 the x and y attribute and therefore
- 00:15:00 their position in that coordinate system
- 00:15:03 so the x position in the end depends on
- 00:15:05 our data point
- 00:15:07 and here i want to call x for
- 00:15:09 data.region
- 00:15:10 because generally i want to have the
- 00:15:12 same coordinate as my bar
- 00:15:14 has but the problem with that alone if i
- 00:15:17 do that for x and y
- 00:15:19 is that for x
- 00:15:22 especially the data is at the very
- 00:15:24 beginning and for why it's also a bit
- 00:15:26 too close to the bar
- 00:15:28 now to change this for why it's simple
- 00:15:31 we just
- 00:15:32 deduct let's say 20 pixels we have to
- 00:15:35 deduct it and not
- 00:15:36 add it because we're calculating from
- 00:15:38 the top so if i deduct 20
- 00:15:40 you see now there is some space on the
- 00:15:42 y-axis but we also want to position this
- 00:15:45 in the middle of the bar and to do that
- 00:15:48 on x we can simply add something to the
- 00:15:51 calculated position to move it further
- 00:15:53 to the right
- 00:15:54 and get the width of our bar
- 00:15:57 with x bandwidth and divide this by two
- 00:16:01 and if we do that you see now it's
- 00:16:02 further in the middle now it's not
- 00:16:04 entirely in the middle because now
- 00:16:06 actually it starts in the middle and
- 00:16:08 because the text then is laid out to the
- 00:16:10 right this is not entirely what we want
- 00:16:12 here
- 00:16:14 but that's something which we can easily
- 00:16:16 change
- 00:16:17 by adding another attribute where we set
- 00:16:20 the text anchor
- 00:16:22 and that is our attribute we can set for
- 00:16:24 the svg text element
- 00:16:26 to middle
- 00:16:30 in addition i'll now also add my label
- 00:16:33 class here and set this to true
- 00:16:37 with that if we reload the text is in
- 00:16:39 the middle and it is
- 00:16:41 purple as well now that's the label
- 00:16:44 above the bars
- 00:16:46 now for the label below the bars where
- 00:16:49 we say
- 00:16:49 india china germany and so on for that
- 00:16:52 we want to add a
- 00:16:53 axis and just as with the scaling there
- 00:16:56 is a separate package
- 00:16:58 i mentioned that the 3js would be quite
- 00:17:00 modular
- 00:17:01 that helps us with rendering access we
- 00:17:04 simply have to add this
- 00:17:05 import so let's do that let's go to the
- 00:17:09 html file and add this import here
- 00:17:13 and once we added this we can start
- 00:17:15 creating
- 00:17:16 an axis and add this to our chart
- 00:17:20 now i'll do that here before i render my
- 00:17:24 bars
- 00:17:24 though the exact position shouldn't
- 00:17:26 matter
- 00:17:27 and append a new group which will hold
- 00:17:30 my axis
- 00:17:31 where i then will call a function call
- 00:17:35 as a default
- 00:17:36 javascript method which you can call on
- 00:17:38 function
- 00:17:39 objects and in the end here i want to
- 00:17:42 call
- 00:17:44 a function exposed by d3
- 00:17:48 and now thanks to this axis import we
- 00:17:50 can call axis
- 00:17:52 bottom here and this will create a
- 00:17:54 bottom axis
- 00:17:57 for my x scale so i pass my
- 00:18:01 x constant which holds a function itself
- 00:18:04 to axis bottom
- 00:18:06 and this function object the x function
- 00:18:09 object
- 00:18:09 holds all the information about our
- 00:18:11 x-axis scale
- 00:18:13 for example here thanks to our domain
- 00:18:15 mapping it holds the
- 00:18:16 the regions and axis bottom extracts
- 00:18:20 that data basically
- 00:18:21 so that it knows what to render at which
- 00:18:23 position for which item
- 00:18:24 so all the heavy lifting of knowing
- 00:18:27 where to render
- 00:18:28 what is taken care of by this axis
- 00:18:30 bottom function and by the
- 00:18:32 axis package now also call
- 00:18:36 etcher on that and set the color of this
- 00:18:38 to
- 00:18:40 a hex code to 4f009e
- 00:18:44 that's of course optional but should
- 00:18:45 make it look a bit nicer and if i save
- 00:18:48 that you'll see something strange
- 00:18:50 you'll see the axis is there but it's at
- 00:18:52 the top
- 00:18:53 even though it's called axis bottom now
- 00:18:56 there is a
- 00:18:57 axis top as well let's call that and see
- 00:18:59 what the difference is
- 00:19:01 now we have no labels because the
- 00:19:03 difference actually is
- 00:19:05 axis top and bottom does not refer to
- 00:19:08 where it's being rendered in your
- 00:19:10 coordinate system but where the labels
- 00:19:12 are
- 00:19:13 with axis top the labels are above the
- 00:19:16 axis
- 00:19:16 and therefore they're cut off here with
- 00:19:19 axis
- 00:19:20 bottom the labels are below the axis and
- 00:19:22 therefore visible
- 00:19:24 but of course we want to move the entire
- 00:19:26 axis down
- 00:19:27 and that's something we simply have to
- 00:19:28 do manually by adding another attribute
- 00:19:31 here
- 00:19:32 the transform attribute which allows us
- 00:19:35 to move that down
- 00:19:38 here i'll then pass a
- 00:19:41 string literal with backticks instead of
- 00:19:44 single quotes
- 00:19:46 and call translate here as a css
- 00:19:50 function so to say
- 00:19:51 to translate along the x and y axis
- 00:19:54 and on the x axis i don't want to move
- 00:19:56 it but on the y
- 00:19:57 axis i'll set this
- 00:20:00 to chart height and this will simply
- 00:20:03 move the axis down
- 00:20:04 by the height of the chart now if we do
- 00:20:07 that and reload
- 00:20:09 you see the axis is gone and that's also
- 00:20:11 not what we want
- 00:20:12 now it's gone for the same reason why we
- 00:20:14 didn't see the labels on the axis top
- 00:20:16 case
- 00:20:17 the axis is simply cut off i moved it
- 00:20:19 down by the entire height
- 00:20:21 of the chart and that of course means
- 00:20:23 that it's now outside of the chart
- 00:20:26 now that's not what i want so we could
- 00:20:28 of course
- 00:20:29 deduct 20 from that for example but now
- 00:20:32 the axis is in my bars
- 00:20:34 so that's also not what i want instead
- 00:20:36 what i'll do here what makes sense
- 00:20:39 is here where i define the heart the
- 00:20:42 height and the width and so on
- 00:20:43 i'll add a new margins constant which is
- 00:20:46 an object
- 00:20:47 with let's say a top margin of 20 and a
- 00:20:49 bottom margin
- 00:20:50 of 10 and here for calculating the
- 00:20:53 height i will now take the 400
- 00:20:56 and deduct my top margin and deduct
- 00:21:00 my bottom margin
- 00:21:04 and i'm doing this because now when i
- 00:21:08 set up the height of my chart container
- 00:21:10 i will re-add those margins so here i
- 00:21:13 add them again
- 00:21:14 but when i then later refer to the
- 00:21:16 height i'll have to hide without the
- 00:21:18 margins so there simply will be some
- 00:21:20 space
- 00:21:21 for for example the axis
- 00:21:24 and now with that if we push down the
- 00:21:26 axis by just height
- 00:21:29 you see here it is now you also see
- 00:21:31 there is a little tick at the beginning
- 00:21:33 you might or might not want that if you
- 00:21:35 don't want it
- 00:21:37 and i do not want it you can call
- 00:21:39 another method on access bottom
- 00:21:41 you can call tick
- 00:21:45 size outer written like this and set
- 00:21:47 this to zero
- 00:21:48 and with that the outer ticks so the
- 00:21:50 beginning take here for example is
- 00:21:52 removed
- 00:21:53 and with that we got this finished chart
- 00:21:55 with an x-axis
- 00:21:57 with the labels above the bars now for
- 00:22:00 the second part of this demo here i want
- 00:22:03 to make sure that we have some controls
- 00:22:05 that allow us to dynamically add and
- 00:22:07 remove these bars
- 00:22:10 and for that i'll scroll down further in
- 00:22:12 my code file here
- 00:22:14 and actually create a new constant list
- 00:22:17 items
- 00:22:18 where i use d3 to select the element
- 00:22:21 with the id
- 00:22:22 data and that simply is
- 00:22:25 this div here i select that
- 00:22:29 and in there i will actually select the
- 00:22:31 unordered list
- 00:22:33 and now in there i will select all list
- 00:22:35 items again we don't have those items
- 00:22:37 yet but you already know in the meantime
- 00:22:39 that we can join some data to change
- 00:22:41 this
- 00:22:42 now i will join my dummy data here of
- 00:22:44 course and i'm interested in the data
- 00:22:46 that's missing here
- 00:22:48 with enter i get that data and we can
- 00:22:50 then
- 00:22:51 append new list items like this
- 00:22:55 now on my list items i will then call
- 00:22:58 append again
- 00:22:59 and append a span where i set the text
- 00:23:02 to data which i extract from the data
- 00:23:04 point by using this function form
- 00:23:06 and i'm interested in the region with
- 00:23:09 that if i reload and
- 00:23:10 shrink this a bit here you see
- 00:23:14 these items on the right here now i want
- 00:23:17 to have a check box which allows me to
- 00:23:19 add or remove the items and therefore on
- 00:23:22 list items i will append
- 00:23:25 a input on every list item where i set
- 00:23:29 the attribute
- 00:23:30 type to checkbox
- 00:23:33 and this will add a regular html input
- 00:23:35 element
- 00:23:36 where the type is checkbox and i'll
- 00:23:39 initially also set it to checked by
- 00:23:41 adding the checked attribute and setting
- 00:23:43 this to true
- 00:23:44 and with that we got this nice checkbox
- 00:23:47 next to the items
- 00:23:51 let me zoom in a bit more again so that
- 00:23:53 this is a bit easier to see
- 00:23:55 now currently clicking the checkbox
- 00:23:57 doesn't do anything
- 00:23:58 so therefore on those rendered check
- 00:24:00 boxes i will add an event listener
- 00:24:03 and d3js has got us covered here we can
- 00:24:06 use the
- 00:24:06 on method to register event listeners
- 00:24:09 for example to the change event
- 00:24:11 which will fire whenever that is clicked
- 00:24:15 now the cool thing is here we of course
- 00:24:16 have a function then
- 00:24:18 that is then executed if that change
- 00:24:21 event triggers
- 00:24:22 and here we get access to the data point
- 00:24:24 for which it triggered
- 00:24:25 so we don't get access to the event
- 00:24:27 object or 2d
- 00:24:29 checkbox but instead really to the data
- 00:24:31 point that was rendered here and that's
- 00:24:33 really cool because that helps us a lot
- 00:24:35 with working with that data
- 00:24:36 we don't have to manually extract it
- 00:24:38 somehow instead we immediately get the
- 00:24:40 correct data here
- 00:24:42 and we can simply console.log the data
- 00:24:44 here to have a look at what we get
- 00:24:46 exactly save that and go to the console
- 00:24:49 here
- 00:24:49 reload and if i click on the checkbox
- 00:24:51 for india you now see here is our
- 00:24:54 india data point and for china i get the
- 00:24:56 china data point
- 00:24:57 and so on now i want to keep track
- 00:25:01 of my unchecked
- 00:25:04 elements and for that here i'll add a
- 00:25:07 new
- 00:25:09 variable which i'll name unselected
- 00:25:14 ids which is an array that initially is
- 00:25:17 empty because initially all items are
- 00:25:19 selected
- 00:25:21 and then here whenever a checkbox is
- 00:25:23 clicked we can check if unselected ids
- 00:25:25 already has that data point with index
- 00:25:28 off for example we can check for the
- 00:25:30 data id
- 00:25:33 every item has a id field so we can
- 00:25:35 check if that id which is just a string
- 00:25:37 is already part of unselected ids and if
- 00:25:40 that
- 00:25:41 actually returns minus one if index off
- 00:25:44 returns minus one
- 00:25:45 we know this id is not part of
- 00:25:47 unselected ids yet
- 00:25:48 so it wasn't unselected before hence we
- 00:25:51 want to add it
- 00:25:52 by pushing data.id so that id string
- 00:25:56 two unselected ids else it was
- 00:25:59 unselected before
- 00:26:00 and in that case i want to set
- 00:26:02 unselected ids equal to unselected ids
- 00:26:05 filter so i want to set it equal to a
- 00:26:08 new array which is an array i derived by
- 00:26:10 filtering
- 00:26:11 by filtering for
- 00:26:16 unequal ids so i'm basically going
- 00:26:19 through all ids that are in unselected
- 00:26:21 id
- 00:26:22 and i check are you not equal to the id
- 00:26:25 we clicked on if yes i want to keep you
- 00:26:27 because then you were not removed
- 00:26:30 and otherwise if you are the id we
- 00:26:32 clicked on then you are no longer
- 00:26:34 unselected
- 00:26:35 so you will not be part of the new
- 00:26:37 unselected ids array which is generated
- 00:26:39 here
- 00:26:39 and which is then stored here so with
- 00:26:42 that we're either adding or removing an
- 00:26:44 id
- 00:26:45 to or from that unselected ids array
- 00:26:48 here
- 00:26:49 and now the interesting part comes now i
- 00:26:52 want to re-render
- 00:26:53 my chart i want to update the data in
- 00:26:56 the chart
- 00:26:57 with my new data that should of course
- 00:27:00 include or not
- 00:27:01 include data points based on whether
- 00:27:04 their id
- 00:27:04 is unchecked or not and to make that
- 00:27:08 happen we have to
- 00:27:10 refactor our code a bit to be precise we
- 00:27:13 have to put
- 00:27:14 some code here into a function
- 00:27:17 all the code where we render our bars
- 00:27:20 and our
- 00:27:21 labels that has to go into a function
- 00:27:24 now
- 00:27:26 i'll name that function render chart
- 00:27:30 and i'm going to take this code where we
- 00:27:31 create the bars the labels
- 00:27:34 so that code here will go
- 00:27:38 into render chart with that
- 00:27:42 we can call a render chart whenever we
- 00:27:44 checked or unchecked a new item
- 00:27:47 but we'll still have another problem
- 00:27:48 it's good that we manage the unselected
- 00:27:50 ids here
- 00:27:51 but in the chart we're using the dummy
- 00:27:53 data and
- 00:27:54 that data never changed so
- 00:27:57 in order to make sure that we actually
- 00:28:01 reflect our changes i will go to the
- 00:28:04 beginning of the code file
- 00:28:06 maybe here below those constants and add
- 00:28:08 a new variable
- 00:28:10 selected data and initially that is
- 00:28:13 equal to dummy data
- 00:28:15 and it's now the selected data that
- 00:28:17 should be used inside of the render
- 00:28:18 chart function
- 00:28:19 which will rerun when we change the data
- 00:28:22 now
- 00:28:23 the domain and so on there we can still
- 00:28:25 use the dummy data because i don't want
- 00:28:27 to change the scaling or the
- 00:28:28 axis you could do this as well but i'm
- 00:28:30 not going to do it here
- 00:28:32 so therefore i only care about the data
- 00:28:33 inside of render chart
- 00:28:35 and here i use selected data instead of
- 00:28:37 dummy data
- 00:28:38 and the same here
- 00:28:43 now of course we have to change selected
- 00:28:45 data i want to keep it here by the way i
- 00:28:47 really only want to change it in render
- 00:28:49 chart but now we have to change selected
- 00:28:51 data here before we call render chart
- 00:28:53 otherwise our unselected ids are still
- 00:28:56 not being reflected there so
- 00:28:58 selected data is then dummy data but
- 00:29:00 filtered
- 00:29:01 so a new array derived from dummy data
- 00:29:04 and my filter criteria is that i check
- 00:29:08 for every data point in dummy data if
- 00:29:11 unselected ids
- 00:29:13 holds the id of that data point
- 00:29:16 if that is equal to -1 here then i know
- 00:29:20 the id
- 00:29:20 of this data point is not in unselected
- 00:29:23 ids and therefore i want to keep the
- 00:29:24 data point
- 00:29:26 if this is anything but -1 i know the id
- 00:29:30 of this data point
- 00:29:31 is part of unselected ids and therefore
- 00:29:33 that data point should be removed and it
- 00:29:35 therefore is not part of the new
- 00:29:37 selected data array that is derived
- 00:29:39 and thereafter i call render chart
- 00:29:42 now with that it will not work though if
- 00:29:44 i reload
- 00:29:46 you see now nothing is being rendered
- 00:29:47 here and the reason for that
- 00:29:50 simply is that our entire chart
- 00:29:52 rendering logic is now in rendered chart
- 00:29:54 but we never call this function sure we
- 00:29:57 call it if we click a checkbox
- 00:29:59 which is why then the chart appears once
- 00:30:01 we uncheck something and re-add it
- 00:30:04 but i also want to call it initially
- 00:30:06 when our app starts
- 00:30:07 so here before i render my list items i
- 00:30:10 will call render
- 00:30:11 chart actually with that if i reload the
- 00:30:13 data is there initially
- 00:30:15 if we uncheck the usa though you see
- 00:30:17 nothing changes
- 00:30:18 if i recheck it also nothing changes so
- 00:30:21 we're not really doing anything
- 00:30:24 well it makes sense that nothing changes
- 00:30:26 because we don't have any logic to
- 00:30:28 remove bars for example and we need that
- 00:30:30 if we uncheck something
- 00:30:31 and we remove it in our data which we're
- 00:30:34 doing here
- 00:30:35 then we also want to be able to remove
- 00:30:36 the dom node so for that in rendered
- 00:30:38 chart it's nice that we have logic to
- 00:30:40 render new bars
- 00:30:42 but we also need logic to remove bars
- 00:30:44 and for that i'll select
- 00:30:46 all my bars here again and
- 00:30:49 join them again with selected data
- 00:30:54 but now here i will call exit to get
- 00:30:56 information about the data that should
- 00:30:58 be removed
- 00:30:58 and i'll call remove on that data and
- 00:31:01 the same for the labels of course i also
- 00:31:04 want to remove labels for data that's
- 00:31:05 not there anymore so on the chart i'll
- 00:31:08 select
- 00:31:08 all for all the labels
- 00:31:14 join that with my selected data call
- 00:31:17 exit
- 00:31:18 and then remove to remove the labels as
- 00:31:20 well
- 00:31:22 if we save then reload and uncheck the
- 00:31:24 us
- 00:31:25 we see everything is gone that also
- 00:31:27 doesn't look right
- 00:31:28 now maybe our logic here for updating
- 00:31:31 selected data is wrong so let's console
- 00:31:34 log
- 00:31:34 selected data and let's see how selected
- 00:31:37 data looks like
- 00:31:38 after we unchecked something so if i
- 00:31:40 reload
- 00:31:41 and remove the us you indeed see it's an
- 00:31:43 empty array
- 00:31:45 so we have some error here selected data
- 00:31:48 should be based on dummy data
- 00:31:51 and we want a filter there but yeah this
- 00:31:54 should not be data id but
- 00:31:56 d.id because that's the input argument
- 00:31:58 we're getting here
- 00:31:59 so that was a tiny mistake on my side i
- 00:32:01 of course want to use the actual data
- 00:32:02 points the actual elements of dummy data
- 00:32:05 so with that fix here if i now reload
- 00:32:09 you see now only one item is removed but
- 00:32:11 it's the wrong one it's always germany
- 00:32:14 and this is a an error we also kind of
- 00:32:16 had in the second video of this mini
- 00:32:18 series if you remember
- 00:32:20 there we also had the problem that the
- 00:32:22 last item was changed or
- 00:32:24 removed even though we actually removed
- 00:32:26 the first item
- 00:32:28 the problem here is simply that d3js is
- 00:32:30 not able to
- 00:32:31 correctly identify values it has an
- 00:32:34 array full of objects and
- 00:32:36 that makes it hard for the 3.js to find
- 00:32:38 out which item actually changed
- 00:32:40 it just sees that the overall length of
- 00:32:42 the array changed
- 00:32:43 but it doesn't know exactly which item
- 00:32:45 changed
- 00:32:47 to help the 3js with this identification
- 00:32:50 we actually want to let it know which
- 00:32:51 key which unique identification criteria
- 00:32:55 it can use
- 00:32:56 when it binds data and we do this by
- 00:32:59 passing a second argument to data
- 00:33:01 which is a function that gets this data
- 00:33:04 point so that runs on every data point
- 00:33:06 which should return the value that
- 00:33:09 should be used as a unique identifier
- 00:33:11 and that therefore should be the id
- 00:33:13 field here
- 00:33:14 so now whenever i call data i will
- 00:33:17 actually pass the second argument
- 00:33:19 both for entering and for exiting and
- 00:33:21 this helps the 3.js correctly
- 00:33:24 identify our values and the dom nodes
- 00:33:27 that belong to them
- 00:33:28 and with that change made now you see
- 00:33:30 indeed
- 00:33:31 we do remove and re-add the correct
- 00:33:35 value the correct bar
- 00:33:39 and that is actually it that's it for
- 00:33:42 this video that's the little
- 00:33:43 demo here which kind of sums up what i
- 00:33:46 mentioned what i explained in the last
- 00:33:48 two videos of this mini series and this
- 00:33:50 hopefully gets you started with
- 00:33:52 d3js therefore it can be difficult to
- 00:33:55 wrap its head around d3js
- 00:33:57 but it can be quite powerful once you
- 00:33:59 get the hang of it
- 00:34:00 now this series hopefully got you
- 00:34:02 started with getting the hang of it
- 00:34:04 and i strongly recommend that you dive
- 00:34:07 in the official documentation
- 00:34:08 of d3js and the different packages like
- 00:34:12 d3-axis
- 00:34:13 to learn all about it and then simply
- 00:34:16 play around with it
- 00:34:17 that really is the best thing you can do
- 00:34:19 if you really want to master it
- 00:34:20 play around with d3.js find out what you
- 00:34:23 can do with it
- 00:34:24 run into problems solve them that is how
- 00:34:27 you're going to grow