Coding

Exploring D3.js Data Binding/ Joins

  • 00:00:00 hi this is actually the second video of
  • 00:00:02 a short mini-series
  • 00:00:03 on the free j/s in the first video we
  • 00:00:06 had a look at what the free chase
  • 00:00:08 generally is and how it works now in
  • 00:00:10 this video we're going to dive a bit
  • 00:00:12 deeper into that data joining part of D
  • 00:00:14 free will learn how that exactly works
  • 00:00:16 and what it means when you join data
  • 00:00:18 with Dom elements
  • 00:00:26 so it's really important that you
  • 00:00:28 understand how data binding or data
  • 00:00:31 joins as it's actually called works in
  • 00:00:34 d3.js now generally whenever you see
  • 00:00:37 code like this we're talking about data
  • 00:00:40 binding or data joins and I'll use these
  • 00:00:43 terms interchangeably here so what we're
  • 00:00:45 doing here and what I showed you in the
  • 00:00:47 first video of this little mini series
  • 00:00:49 which you find linked below this video
  • 00:00:51 is where selecting all elements with a
  • 00:00:55 bar CSS class on it and we're binding
  • 00:00:58 some data to those elements now as you
  • 00:01:01 learned in that last video in that first
  • 00:01:03 video those elements don't need to exist
  • 00:01:06 yet we can also bind data to elements
  • 00:01:08 that aren't rendered yet so that we
  • 00:01:10 basically then tell the free-jazz hey
  • 00:01:13 please find the difference between
  • 00:01:15 what's currently rendered may be nothing
  • 00:01:18 and what should be rendered based on the
  • 00:01:20 data that's the whole idea here so in
  • 00:01:22 the end what we do here is we join some
  • 00:01:25 data with the selected bar elements and
  • 00:01:28 the data that is attached to those
  • 00:01:30 elements and that data attachment thing
  • 00:01:33 is managed behind the scenes by d3.js
  • 00:01:36 and more importantly than that the free
  • 00:01:39 trace then also derives the required
  • 00:01:42 updates it finds out what should be
  • 00:01:45 added so which new data is available
  • 00:01:47 based on what was previously rendered
  • 00:01:49 and the day that we now joined what
  • 00:01:53 should be removed so what is redundant
  • 00:01:55 based on what was rendered and what we
  • 00:01:57 now joined and maybe what was there
  • 00:01:59 before but what simply changed now and
  • 00:02:02 it gives us the enter and exit methods
  • 00:02:05 on the selection generated by the data
  • 00:02:08 join to easily get access to the
  • 00:02:11 elements that are missing or that are
  • 00:02:14 redundant and it will automatically
  • 00:02:16 update elements that are already there
  • 00:02:18 where the data changed let me show this
  • 00:02:21 to you to make this really clear and for
  • 00:02:23 this I have an empty project here just a
  • 00:02:25 base HTML skeleton with some Styles here
  • 00:02:28 some basic styles with an empty
  • 00:02:30 unordered list and a script import and
  • 00:02:33 I'm importing d3 min-jae's I'm all the
  • 00:02:36 importing d3 scale but we're not going
  • 00:02:38 to use that here so if you
  • 00:02:39 to follow along you can just use the
  • 00:02:41 normal DV in port now here in app
  • 00:02:45 jeaious so in my script I now want to
  • 00:02:48 set up some data with which we can work
  • 00:02:51 and for that I'll actually create a new
  • 00:02:53 object country data which is a regular
  • 00:02:57 JavaScript object like this where I want
  • 00:02:59 to have some items and I'll keep the
  • 00:03:01 data set very simple here because I want
  • 00:03:03 to focus on how the free choice works so
  • 00:03:06 let's say our actual data which I store
  • 00:03:08 in items here is just a set of country
  • 00:03:11 names so something like China India and
  • 00:03:16 the US and of course you can add any
  • 00:03:19 countries you want now I'll also add
  • 00:03:22 some methods I'll add an add item method
  • 00:03:24 here which takes a new item that should
  • 00:03:27 be added where I simply reach out to
  • 00:03:29 this items and I push my item onto this
  • 00:03:32 array then I want a remove item method
  • 00:03:36 where I get the index of the element
  • 00:03:38 that should be removed and I remove it
  • 00:03:40 by using this Islands splice not slice
  • 00:03:43 but splice to remove item and I remove
  • 00:03:47 the item at index index and only that
  • 00:03:51 item by passing one as a second argument
  • 00:03:53 I make it clear that I only want to
  • 00:03:54 remove the item at this index and last
  • 00:03:57 but not least I'll add a update item
  • 00:04:00 method this takes the index of the item
  • 00:04:02 that should be updated and the new item
  • 00:04:05 that should be inserted in place of that
  • 00:04:07 existing item and we simply select the
  • 00:04:10 item by index and replace it with new
  • 00:04:13 item therefore so it's a very simple
  • 00:04:15 object with some simple methods but this
  • 00:04:18 allows us to interact with this data
  • 00:04:20 here now let's use d3.js and let's start
  • 00:04:23 simple let's say we want to output the
  • 00:04:25 data we have here in our Dom so that we
  • 00:04:30 can see it on our page here now for that
  • 00:04:33 we use the d3 object which is available
  • 00:04:36 thanks to the d3 import and here we can
  • 00:04:39 first of all select the node DT element
  • 00:04:42 where we want to render our data in and
  • 00:04:44 that's the unordered list in my case so
  • 00:04:46 I select this with the Select method and
  • 00:04:48 the Select method selects one single
  • 00:04:51 element the first unordered list
  • 00:04:53 fines now in that unordered list
  • 00:04:56 I now want you add my data and for every
  • 00:05:00 data piece I want to render a list item
  • 00:05:03 therefore here we do select all Li and
  • 00:05:07 even though we have no list items
  • 00:05:10 initially we can select them of course
  • 00:05:13 this selection will simply be empty the
  • 00:05:15 first time this runs but this is what
  • 00:05:18 allows the free J's to later do this
  • 00:05:20 diff this comparison between what is
  • 00:05:23 rendered and what should be rendered
  • 00:05:25 based on the joint data because that's
  • 00:05:27 going to be the next step we call data
  • 00:05:30 on that selection and that joins all
  • 00:05:33 list items in that unordered list with
  • 00:05:36 the data we're going to pass in here and
  • 00:05:39 there I want to pass in country data
  • 00:05:41 items because my items are a that's the
  • 00:05:44 data I want to render now behind the
  • 00:05:46 scenes d3.js will therefore then go
  • 00:05:48 ahead have a look at the list items it
  • 00:05:50 found and initially it didn't find any
  • 00:05:53 have a look at the data and basically
  • 00:05:55 check based on the data I got here so
  • 00:05:58 based on the number of elements in the
  • 00:06:00 end here in this array how many list
  • 00:06:03 items do I need to add how many list
  • 00:06:05 items should I maybe remove and which
  • 00:06:08 data did simply change and the first
  • 00:06:11 time this runs when we have no list
  • 00:06:12 items there is nothing to remove there
  • 00:06:15 is nothing to change or to update but
  • 00:06:17 there are of course missing list items
  • 00:06:19 so data here in the end gives us back a
  • 00:06:22 new selection basically a selection full
  • 00:06:26 of the queue be rendered elements so
  • 00:06:29 data now basically holds information
  • 00:06:31 about what was rendered and which data
  • 00:06:34 we connected to this and now from this
  • 00:06:36 selection from that information we can
  • 00:06:39 now extract different slices of
  • 00:06:41 information so to say for example with
  • 00:06:43 the enter method we can find out which
  • 00:06:46 items are missing with the exit method
  • 00:06:50 we could find out which items are
  • 00:06:52 redundant and should be removed maybe
  • 00:06:54 well of course since we know that
  • 00:06:56 initially we have no list items we want
  • 00:06:58 to call enter and this gives us a new
  • 00:07:00 selection which now holds all missing
  • 00:07:03 list items so which basically holds the
  • 00:07:05 information about which
  • 00:07:06 items were expected based on the data we
  • 00:07:09 joined and which items were actually
  • 00:07:11 selected and therefore rendered before
  • 00:07:13 and initially that means that here we
  • 00:07:16 have a selection full of missing list
  • 00:07:17 items and we can then append those list
  • 00:07:20 items by calling append Li and this will
  • 00:07:23 now render a new list item for every
  • 00:07:25 missing data point now you could render
  • 00:07:28 anything here you could render a div
  • 00:07:30 here but that wouldn't make much sense
  • 00:07:32 because when you then later again select
  • 00:07:34 all list items in the unordered list to
  • 00:07:36 maybe join new data if you rendered divs
  • 00:07:39 for missing data before those list items
  • 00:07:42 will still be missing in the future so
  • 00:07:44 you should append here what you selected
  • 00:07:46 here and this will now render new list
  • 00:07:49 items for all the data points here and
  • 00:07:51 in order to see something I can now
  • 00:07:53 render some text here so with the text
  • 00:07:55 method we can append or add text to
  • 00:07:58 every rendered list item and there we
  • 00:08:00 could add some hard coded text if we
  • 00:08:02 wanted to but we can also reach out to
  • 00:08:05 the data off the data point of every
  • 00:08:08 specific list item so we have one list
  • 00:08:10 item being rendered per data point here
  • 00:08:13 and we can get access to the data in
  • 00:08:15 that respective data point now our data
  • 00:08:18 points are just strings here so
  • 00:08:20 therefore in this function which we can
  • 00:08:22 pass in here we get access to the data
  • 00:08:24 point and I can just return the data in
  • 00:08:26 there because that already is a string
  • 00:08:28 if it were an object you could of course
  • 00:08:30 access some property of it but here it's
  • 00:08:34 a string so we can just return it like
  • 00:08:35 this and if we now save that and reload
  • 00:08:38 we therefore see these three countries
  • 00:08:40 being rendered now that's what I showed
  • 00:08:43 you before now let's understand this
  • 00:08:45 data joining a bit better though let's
  • 00:08:48 say that our data changes maybe because
  • 00:08:50 the user clicked a button or maybe like
  • 00:08:53 I'll do it here for demo purposes
  • 00:08:55 because we set a timer so here I have a
  • 00:08:58 timer of two seconds let's say and after
  • 00:09:00 two seconds I want to add a new item so
  • 00:09:03 I can reach out to country data and call
  • 00:09:05 the add item method we added earlier and
  • 00:09:08 maybe add Germany or whichever country
  • 00:09:10 you want to add and now we can basically
  • 00:09:12 repeat this step here I can select all
  • 00:09:15 this code and execute it again
  • 00:09:19 and with this I again reach out to all
  • 00:09:22 the list items in the unordered list and
  • 00:09:24 join it with my new data since I call
  • 00:09:27 country data add item here country data
  • 00:09:30 items here will be different than it was
  • 00:09:32 here in line 16 because now we have well
  • 00:09:35 new items in there we add a Germany
  • 00:09:37 after all
  • 00:09:38 so now entry will hold the difference
  • 00:09:40 between the list items it found and the
  • 00:09:43 day that it was connected to those list
  • 00:09:45 items in the past and the new data now
  • 00:09:48 here of course unlike before when we
  • 00:09:50 first rendered this there will be list
  • 00:09:53 items but the fridges will see that we
  • 00:09:55 only have free list items but actually
  • 00:09:58 four countries now so enter should hold
  • 00:10:01 that extra new data piece that was added
  • 00:10:03 so we append a new list item for every
  • 00:10:06 missing data point and again output the
  • 00:10:09 name of the country so output the text
  • 00:10:12 here and to make it a bit easier to see
  • 00:10:14 which country was added or which new
  • 00:10:17 data point was rendered I'll actually
  • 00:10:19 add a class here with the class method
  • 00:10:21 and that's going to be the added class
  • 00:10:23 which we add like this that's a class I
  • 00:10:26 prepared in advance here it simply
  • 00:10:28 renders a green background color if I
  • 00:10:31 now set save this and reload initially
  • 00:10:34 we have three countries but then Germany
  • 00:10:36 is added and it is green as you can tell
  • 00:10:38 now interesting if you open the elements
  • 00:10:41 tab in your dev tools if you reload here
  • 00:10:44 you'll see initially free list items and
  • 00:10:46 you see that only the fourth one flashed
  • 00:10:48 here so that's also nice to know when
  • 00:10:51 the day that changes the free Jas is not
  • 00:10:54 going ahead and rear-ending
  • 00:10:55 everything it really only rear Enders
  • 00:10:57 what changed and that's of course better
  • 00:11:00 for performance than if it would
  • 00:11:02 rerender the entire list so that's an
  • 00:11:04 interesting side note so what did we
  • 00:11:07 added a new item
  • 00:11:08 let's now copy that whole timer and add
  • 00:11:11 a new timer which let's say fires after
  • 00:11:14 four seconds here
  • 00:11:17 and there I don't want to add item but I
  • 00:11:19 want to remove our item so I'll call
  • 00:11:21 remove item and remove the item at index
  • 00:11:24 zero so the first item in the list and
  • 00:11:27 that should be China now thereafter
  • 00:11:30 again I select all my list items and I
  • 00:11:33 join it with the new data and now I'm
  • 00:11:35 not interested in the data that needs to
  • 00:11:37 be added because that should be nothing
  • 00:11:39 because I only remove something instead
  • 00:11:42 with exit we now get access to the
  • 00:11:45 redundant data we get access to the
  • 00:11:47 information d3.js derived on which data
  • 00:11:51 was removed for which we still have a
  • 00:11:53 rendered list item and then we could
  • 00:11:55 call remove to remove such redundant Dom
  • 00:11:59 notes but here I want to do something
  • 00:12:01 else instead of removing it I will
  • 00:12:05 actually just change the CSS class or
  • 00:12:09 add a new CSS class of redundant and I'm
  • 00:12:14 doing this so that we can see on the
  • 00:12:17 screen which data changed if I remove it
  • 00:12:20 we also see what's missing but this
  • 00:12:22 since redundant adds a red where a
  • 00:12:24 reddish background should make it easier
  • 00:12:27 to see what d3.js identified as
  • 00:12:29 redundant we don't need to set a text
  • 00:12:32 here because we're working with Dom
  • 00:12:33 elements that already were rendered so
  • 00:12:36 if we now save this and I reload we see
  • 00:12:39 after two seconds Germany gets added but
  • 00:12:41 after four seconds huh that does make
  • 00:12:44 sense desert germany is getting removed
  • 00:12:47 but we removed the first item here well
  • 00:12:51 good news is d3.js did identify some
  • 00:12:56 change bad news is it identified the
  • 00:12:59 wrong change the reason for that is that
  • 00:13:01 currently I'm always joining my array of
  • 00:13:04 data here and the thing with that is if
  • 00:13:07 you do just that d3.js identifies
  • 00:13:11 elements by index now if I remove the
  • 00:13:14 first element in my array that does not
  • 00:13:17 mean that the first element is empty it
  • 00:13:19 means that all our elements move one
  • 00:13:22 position ahead so India becomes the new
  • 00:13:24 first element with index zero USA
  • 00:13:26 becomes the new second element with
  • 00:13:28 index one and Germany which I added
  • 00:13:30 before
  • 00:13:31 the new third element with index – now
  • 00:13:35 because that is what's happening what
  • 00:13:38 the fridge is of course sees is that we
  • 00:13:41 have four rendered list items because we
  • 00:13:44 added Germany so after this line or
  • 00:13:46 after this code block we have four
  • 00:13:48 rendered list items but after removing
  • 00:13:50 the first item there only should be free
  • 00:13:52 list items and d3.js by default is not
  • 00:13:56 going to look at the concrete values
  • 00:13:58 instead it just checks the length of the
  • 00:14:00 new data array and of the old data array
  • 00:14:02 basically and sees that there is a
  • 00:14:04 difference so it simply gets rid of the
  • 00:14:06 last element in the array or of the
  • 00:14:09 rendered elements to be precise so
  • 00:14:11 that's why it identifies Germany as the
  • 00:14:14 redundant data because the new data
  • 00:14:15 array after removing the first item
  • 00:14:18 simply has only three items instead of
  • 00:14:20 four now of course that's not the
  • 00:14:22 behavior we want here and thankfully we
  • 00:14:25 can change it we can tell d3.js how it
  • 00:14:29 should identify data as I said by
  • 00:14:31 default it just takes the index in the
  • 00:14:33 array and doesn't look at the concrete
  • 00:14:35 data value but we can change that when
  • 00:14:38 we call the data method we can pass in a
  • 00:14:41 second argument where we basically
  • 00:14:43 provide a function that returns the key
  • 00:14:46 that should be used for identifying data
  • 00:14:48 to d3.js here we get access to the
  • 00:14:52 concrete data points so this function
  • 00:14:54 gets executed for every data point and
  • 00:14:56 it should return some value which we
  • 00:14:58 extract from the data point that acts as
  • 00:15:01 a unique identifier of that data point
  • 00:15:03 now since our data points are just
  • 00:15:06 strings here I'm just going to return
  • 00:15:08 the string itself like this but with
  • 00:15:10 that I'm saying it's not the index that
  • 00:15:13 matters for identifying data instead
  • 00:15:15 it's the data itself the the string in
  • 00:15:18 this case and therefore D free trace now
  • 00:15:20 we'll make that comparison not by length
  • 00:15:23 of the array and by index but instead by
  • 00:15:26 looking at the string and it will
  • 00:15:27 compare strings and we have to do that
  • 00:15:30 for all data joins of course like this
  • 00:15:33 and if we do that and we now reload
  • 00:15:36 you'll see that Germany gets added
  • 00:15:38 but then China gets marked as redundant
  • 00:15:41 data and that is what should happen and
  • 00:15:43 that is what happens because the
  • 00:15:45 3js now does not just see that the data
  • 00:15:48 array length changed and basically
  • 00:15:50 compares that to the amount of list
  • 00:15:52 items that were rendered but it looks at
  • 00:15:54 the concrete values and it checks which
  • 00:15:56 value is missing now and that's of
  • 00:15:58 course the string China so with that
  • 00:16:01 let's copy that one last time here
  • 00:16:04 because here I now want to call country
  • 00:16:07 data dot update item and let's say I
  • 00:16:11 want to update the second item so the
  • 00:16:13 one with index one and change it to
  • 00:16:16 Russia currently the second item here is
  • 00:16:19 India though keep in mind that this
  • 00:16:22 timer here actually should run after six
  • 00:16:25 seconds and therefore after we removed
  • 00:16:28 the first item so the actual second item
  • 00:16:31 at this point is not going to be India
  • 00:16:33 but instead should be the USA because we
  • 00:16:36 removed the first item in the meantime
  • 00:16:38 so I'm changing that item to Russia then
  • 00:16:43 I have my same code as before I join my
  • 00:16:46 list items with that updated data now
  • 00:16:48 here in this case I am still interested
  • 00:16:51 in the day that had exited because when
  • 00:16:54 we update data when it replaced the
  • 00:16:57 second item with Russia
  • 00:16:58 technically the old second item India of
  • 00:17:01 course exits it's removed because it is
  • 00:17:05 replaced with new data of course we
  • 00:17:07 don't remove a real element in our data
  • 00:17:09 array but we do remove data in there we
  • 00:17:13 replace it with new data that's the
  • 00:17:14 difference to just removing it but still
  • 00:17:17 with exit we can get access to this
  • 00:17:19 changed data and just to be real clear
  • 00:17:22 here exit this exit method gives us
  • 00:17:26 information about the data which d3.js
  • 00:17:29 identified as being redundant now but
  • 00:17:32 here in this case we simply know that we
  • 00:17:36 replaced one item with rashia and
  • 00:17:39 therefore this item is not really
  • 00:17:41 redundant but just change but that's
  • 00:17:43 something we know here and why does the
  • 00:17:45 item we replaced end up in exit in the
  • 00:17:47 first place why is it identified as
  • 00:17:49 being redundant well because we're using
  • 00:17:52 our data or a string itself as a key
  • 00:17:55 that's what we changed a couple of
  • 00:17:56 seconds ago so if we replace
  • 00:17:59 USA with Russia well that you as a key
  • 00:18:01 is gone and that Russia key is new so
  • 00:18:05 therefore we would find Russia on enter
  • 00:18:08 and USA on exit but since we know
  • 00:18:11 exactly what we're doing here we can of
  • 00:18:12 course also just use the exit data and
  • 00:18:15 not remove it and then add Russia but
  • 00:18:18 instead just change the exit data but
  • 00:18:21 again we only do this here because we
  • 00:18:22 know exactly which change we're making
  • 00:18:24 here and to mark it as such let's add
  • 00:18:27 the updated class here to mark it as
  • 00:18:32 updated because that is a nice class I
  • 00:18:34 prepared that gives us some orange
  • 00:18:36 background with that if I reload here we
  • 00:18:40 see Germany China and then you see the
  • 00:18:45 US gets marked and China gets marked now
  • 00:18:49 the US makes a lot of sense because I'm
  • 00:18:51 updating the second item which at this
  • 00:18:53 point because we removed the first item
  • 00:18:56 of four which at this point is the USA
  • 00:18:59 as I explained so it makes sense that
  • 00:19:01 this gets marked but why is China
  • 00:19:03 getting marked well keep in mind we did
  • 00:19:05 remove China here right we do remove it
  • 00:19:08 here but we don't remove it in the Dom I
  • 00:19:10 just mark it as redundant but when we
  • 00:19:13 done here again select all list items
  • 00:19:15 and join that with our updated data in
  • 00:19:19 that data China is still missing we
  • 00:19:22 still have that same data set where we
  • 00:19:24 removed China so therefore of course the
  • 00:19:26 difference detected by the free j/s is
  • 00:19:29 that yes
  • 00:19:30 the u.s. changed to Russia but also
  • 00:19:33 China changed compared to what's
  • 00:19:36 rendered in the Dom China changed
  • 00:19:38 because we never removed it if we would
  • 00:19:40 have called remove here in the removal
  • 00:19:44 case to really remove the Dom node of
  • 00:19:46 course only the US will be marked so now
  • 00:19:49 you see Germany's added China is really
  • 00:19:52 being removed and then only the US is
  • 00:19:55 marked as updated so that's the only
  • 00:19:56 reason why China shows up again now I'll
  • 00:19:59 not remove it here because I like this
  • 00:20:01 red in fact where we mark what's going
  • 00:20:04 to be removed what is identified as
  • 00:20:06 redundant but that's the explanation why
  • 00:20:08 the same item later gets marked as
  • 00:20:11 updated
  • 00:20:14 now what you'll also notice of course
  • 00:20:16 though is if I reload again we have
  • 00:20:18 Germany we have China and the US gets
  • 00:20:22 marked and China for the reasons
  • 00:20:24 mentioned but the text here on USA does
  • 00:20:27 not change but we did change it here
  • 00:20:29 well that makes sense hopefully because
  • 00:20:32 d3.js is not going to magically change
  • 00:20:35 our Dom it only does what we instructed
  • 00:20:38 to do like for example add this class so
  • 00:20:40 if you want to change the text here you
  • 00:20:42 have to call text and set this to Russia
  • 00:20:47 for example you can also set it to data
  • 00:20:50 data so to this function that gets
  • 00:20:52 access to the data point and uses the
  • 00:20:54 data points value as a text but with dad
  • 00:20:59 you actually would get access to the
  • 00:21:00 data point that is marked as to be
  • 00:21:03 removed or as redundant so you would get
  • 00:21:05 access to USA as a text not to Russia
  • 00:21:07 and therefore here since we of course
  • 00:21:10 know that we're going to add Russia we
  • 00:21:12 should hard-coded here so now if I
  • 00:21:15 reload here you see Germany is being
  • 00:21:17 added China is marked for a removal and
  • 00:21:20 then we have Russia here and also here
  • 00:21:22 because well China is still sticking
  • 00:21:24 around right but this is now the update
  • 00:21:27 which we want to perform and that's how
  • 00:21:31 D free J's data binding or data joins
  • 00:21:34 work especially the exit and the enter
  • 00:21:37 case are super important to understand
  • 00:21:39 because these are the cases you're
  • 00:21:41 typically working with a lot this second
  • 00:21:44 argument you can pass to data to provide
  • 00:21:46 a unique identifier to D free J's for
  • 00:21:49 your data points that's also good to
  • 00:21:51 know and in general hopefully that video
  • 00:21:53 help with understanding how D free J s
  • 00:21:56 works and how you connect your Dom now
  • 00:21:59 it's to data with d3.js