{"id":903,"date":"2024-02-22T10:39:19","date_gmt":"2024-02-22T16:39:19","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=903"},"modified":"2024-02-22T14:25:03","modified_gmt":"2024-02-22T20:25:03","slug":"building-a-todo-app-from-scratch-step-3-basic-javascript-functionality","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-3-basic-javascript-functionality\/","title":{"rendered":"Building a TODO App from Scratch \u2014 Step 3 \u2014 Basic JavaScript Functionality"},"content":{"rendered":"\n<p>We left off with a wireframe-y looking HTML-ized version of our design. Personally, I&#8217;m a little tempted to get some CSS on there, but let&#8217;s hold off and do the very basic JavaScript functionality first. That will make it, ya know, actually functional, and having some of the interactive stuff in place might inform some of our styling choices anyway.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-1-planning-design\/\">Building a TODO App from Scratch \u2014 Step 1 \u2014 Planning &#038; Design<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-2-html\/\">Building a TODO App from Scratch \u2014 Step 2 \u2014 HTML<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-3-basic-javascript-functionality\/\">Building a TODO App from Scratch \u2014 Step 3 \u2014 Basic JavaScript Functionality<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-4-styling-interactive-choices\/\">Building a TODO App from Scratch \u2014 Step 4 \u2014 Styling &#038; Interactive Choices<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-5-extra-functionality\/\">Building a TODO App from Scratch \u2014 Step 5 \u2014 Extra Functionality<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Where is the data going to go?<\/h2>\n\n\n\n<p>There are more ways and places to save data on the web than you can shake a stick at. Much like we slowed down at the beginning and did some design thinking at first, we might do well do do a little data thinking and consider the experience we&#8217;re trying to build.<\/p>\n\n\n\n<p>For example, we might want to offer our TODO app for multiple users who log in to the website. If that&#8217;s the case, we&#8217;ll need to store the data in such a way that each user has their own set of to-dos associated with them. That would be fun! In that case we&#8217;d have to think about security and ensuring correct auth around data access. Maybe a bit much for a first crack though, so let&#8217;s table that for now.<\/p>\n\n\n\n<p>Circling back to the front-end, it would be nice to get our data there ultimately as JSON. JSON is just so browser-friendly, as JavaScript can read it and loop over it and do stuff with it so easily. Not to mention <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/JSON\">built-in APIs<\/a>. <\/p>\n\n\n\n<p>That doesn&#8217;t mean we <em>have to store data<\/em> as JSON, but it&#8217;s not a terrible idea. We could use a more traditional database like MySQL, but then we&#8217;d need some kind of tool to help us return our data as JSON, like any decent ORM should be able to help with. <a href=\"https:\/\/www.sqlite.org\/json1.html#jmini\">SQLlite might be even better<\/a>.<\/p>\n\n\n\n<p>Or if we went with Postgres, it supports a better native <code>json<\/code> field type, and built-in utility functions like <code>ROW_TO_JSON()<\/code>. <\/p>\n\n\n\n<p>Still, just keeping the data as JSON sounds appealing. I&#8217;m not a huge data expert, but as I understand it there are JSON-first databases like <a href=\"https:\/\/www.mongodb.com\/\">MongoDB<\/a> or <a href=\"https:\/\/couchdb.apache.org\/\">CouchDB<\/a> that might make perfect sense here. I&#8217;ve used Firebase a number of times before and <a href=\"https:\/\/firebase.google.com\/products\/firestore?hl=en\">their data storage<\/a> is very JSON-like. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"500\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/Thumbs.jpg?resize=1000%2C500&#038;ssl=1\" alt=\"\" class=\"wp-image-1000\" style=\"width:413px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/Thumbs.jpg?w=1000&amp;ssl=1 1000w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/Thumbs.jpg?resize=300%2C150&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/Thumbs.jpg?resize=768%2C384&amp;ssl=1 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">There are a million data storage options, and every data storage company wants your data. Just the way it is, kid.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Overwhelmed? Sorry. Let&#8217;s make like Liz Lemon <a href=\"https:\/\/www.youtube.com\/watch?v=i_VahrHVX8Y\">and not overthink it<\/a>. Let&#8217;s assume we&#8217;re going to use JSON data, and dunk that data as a string in <code>localStorage<\/code>. While this isn&#8217;t exactly a database, we can still nicely format our data, make it extensible, and pretend like we&#8217;re interfacing with a fancier database system. <\/p>\n\n\n\n<p>What we&#8217;re giving up with <code>localStorage<\/code> is a user being able to access their data from anywhere. If they so much as open our TODO app in another browser, their data isn&#8217;t there. Let alone open the site on their phone or the like. Or do something blasphemous like clear their browser data. So it&#8217;s rudimentary, but it&#8217;s still &#8220;real&#8221; enough data storage for now. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is the data going to look like?<\/h2>\n\n\n\n<p>JSON can be an <code>{}<\/code> Object or <code>[]<\/code> Array. I&#8217;m thinking Array here, because arrays have this natural sense of order. We talked about being able to re-arrange to-do items at some point, and the position in the array could be all we need for that. <\/p>\n\n\n\n<p>As best I know, as of ES2020, I believe Objects maintain order when you iterate over them in all the various ways. But I don&#8217;t think there is any reasonable way to change that order easily, without creating a brand new Object and re-inserting things in the newly desired order.  Or, we&#8217;d have to keep an <code>order<\/code> value for each item, then update that as needed and always iterate based on that value. Sounds like too much work.<\/p>\n\n\n\n<p>So&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">&#91;\n  { <span class=\"hljs-comment\">\/* to-do item *\/<\/span> },\n  { <span class=\"hljs-comment\">\/* to-do item *\/<\/span> }\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>(Astute readers will note that JSON doesn&#8217;t actually have comments, unless we <a href=\"https:\/\/json5.org\/\">fix it somehow<\/a>.)<\/p>\n\n\n\n<p>If we <em>were<\/em> supporting a multi-user system, perhaps all the data would be an object with each user ID being a key and the data being an array like above. One way or another, one use per Array.<\/p>\n\n\n\n<p>Now what does each item look like? Based on our design, we really only need a few things:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Title (text of the to-do)<\/li>\n\n\n\n<li>Complete (whether it is done or not)<\/li>\n<\/ol>\n\n\n\n<p>We could decide that we don&#8217;t need &#8220;complete&#8221; because we&#8217;ll just delete it. But any to-do app worth it&#8217;s salt will be able to show you a list of completed items, and you can always delete them from there. <\/p>\n\n\n\n<p>We talked about using the array order for the visual order of the to-dos. I think I&#8217;m fine with that for now, knowing we can always add ordering properties if we really feel like it. In fact, we can add whatever. We could add dates like a &#8220;created at&#8221;, &#8220;modified at&#8221;, &#8220;due date&#8221;, or &#8220;completed at&#8221; if we felt it would be useful. We could add tags. We could add a description. But these kind of things should be design and UX driven, as we well know by now. Don&#8217;t just go adding data speculatively, that tends to not end well.<\/p>\n\n\n\n<p>When it comes to updating\/deleting existing to-dos, we&#8217;re going to need a way to update just that item in the data. That&#8217;s why I brought up the array order again, because theoretically we could know which item we&#8217;re dealing with by the DOM order, then match that to the array order to find the right item. But something about that feels janky to me. I&#8217;d rather have a more solid-feeling one-to-one connection between the UI and the data. Maybe it&#8217;s just me, but it feels better. So let&#8217;s add a unique identifier to each todo.<\/p>\n\n\n\n<p>So our now-list-of-three will look like this in the data:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">&#91;\n  {\n     title: <span class=\"hljs-string\">\"Walk the dog\"<\/span>, <span class=\"hljs-comment\">\/\/ string<\/span>\n     completed: <span class=\"hljs-literal\">false<\/span>,      <span class=\"hljs-comment\">\/\/ boolean<\/span>\n     id: <span class=\"hljs-string\">\"something-unique\"<\/span> <span class=\"hljs-comment\">\/\/ string<\/span>\n  },\n  {\n    <span class=\"hljs-comment\">\/\/ more! <\/span>\n  }\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Seems workable and extensible to me! <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Writing data<\/h2>\n\n\n\n<p>There is one way to add a new item on our site: submitting the form. So if we get ahold of that form in JavaScript and watch for the <code>submit<\/code> event, we&#8217;re in business.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> form = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#todo-form\"<\/span>);\n\nform.addEventListener(<span class=\"hljs-string\">\"submit\"<\/span>, (event) =&gt; {\n  event.preventDefault();\n\n  <span class=\"hljs-comment\">\/\/ Add to-do to data and render UI<\/span>\n\n  form.reset();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Two little tricks there. <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The <code>preventDefault<\/code> is because we&#8217;re handling the submission in JavaScript so we&#8217;re preventing the browser from trying to perform the default action of going to a new page. <\/li>\n\n\n\n<li>The <code>reset<\/code> bit is a nice little built-in UI for resetting the fields, so we don&#8217;t have to manually clear them ourselves. After we add a to-do, we don&#8217;t want that same text just sitting there in the input, it should go back to blank.<\/li>\n<\/ol>\n\n\n\n<p>We talked about all our data being one big JSON-able Array. So:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> TODOs = &#91;];<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then we can <code>push<\/code> into that array with the new data. We know the <code>complete<\/code> value will be false (we just added it!) and the title will be from the input. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">TODOs<\/span><span class=\"hljs-selector-class\">.push<\/span>({\n  <span class=\"hljs-attribute\">title<\/span>: event.target&#91;<span class=\"hljs-number\">0<\/span>].value,\n  complete: false,\n  id: self.crypto.<span class=\"hljs-built_in\">randomUUID<\/span>()\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That last bit is the browser giving us a unique identifier for free! We could used <a href=\"https:\/\/www.npmjs.com\/package\/uuid\">the package<\/a> for them, but we just don&#8217;t need to anymore. UUID&#8217;s are cool. There is a practically-zero chance of ever getting a duplicate <em>ever<\/em>. <a href=\"https:\/\/en.wikipedia.org\/w\/index.php?title=Universally_unique_identifier&amp;oldid=755882275#Random_UUID_probability_of_duplicates\">Wikipedia<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8230; only after generating 1 billion UUIDs every second for approximately 100 years would the probability of creating a single duplicate reach 50%.<\/p>\n<\/blockquote>\n\n\n\n<p>We&#8217;ve decided we&#8217;re just going to keep the data in <code>localStorage<\/code> for now, so after we&#8217;ve updated our TODOs Array, let&#8217;s dump it there. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">localStorage&#91;<span class=\"hljs-string\">\"data\"<\/span>] = <span class=\"hljs-built_in\">JSON<\/span>.stringify(TODOs);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Uh, that was easy.<\/p>\n\n\n\n<p>Now that we&#8217;ve added the data, we know we&#8217;ll need to re-render the UI. But we&#8217;ll make a function for that in the next section, as obviously we&#8217;ll need to render the UI when we read the data when the page loads as well. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reading data<\/h2>\n\n\n\n<p>Getting the data out of <code>localStorage<\/code> is just as easy as writing to it: <code>localStorage[\"data\"]<\/code>. That&#8217;ll have our JSON data in it. Probably. If we&#8217;ve written to it before. Just to be sure, let&#8217;s check before we parse out the data. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> TODOs = &#91;];\n\n<span class=\"hljs-keyword\">if<\/span> (localStorage&#91;<span class=\"hljs-string\">\"data\"<\/span>] !== <span class=\"hljs-literal\">null<\/span> &amp;&amp; localStorage&#91;<span class=\"hljs-string\">\"data\"<\/span>] !== <span class=\"hljs-literal\">undefined<\/span>) {\n  TODOs = <span class=\"hljs-built_in\">JSON<\/span>.parse(localStorage&#91;<span class=\"hljs-string\">\"data\"<\/span>]);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Just doing that once when the page is loaded will ensure our <code>TODOs<\/code> variable is loaded with what we got. <\/p>\n\n\n\n<p>Now we need to render the UI. We already figured out we need to do this in several situations:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>When the page loads<\/li>\n\n\n\n<li>When we add a new to-do<\/li>\n\n\n\n<li>When we complete a to-do<\/li>\n<\/ol>\n\n\n\n<p>So let&#8217;s write it as a function so we can call it in all those situations. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> list = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#todo-list\"<\/span>);\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildUI<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">let<\/span> HTML = <span class=\"hljs-string\">``<\/span>;\n  TODOs.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">todo<\/span>) =&gt;<\/span> {\n    HTML += <span class=\"hljs-string\">`\n      &lt;li id=\"<span class=\"hljs-subst\">${todo.id}<\/span>\"&gt;\n       <span class=\"hljs-subst\">${todo.title}<\/span>\n       &lt;button aria-label=\"Complete\" class=\"button-complete\"&gt;\n         &lt;svg class=\"svg-check\"&gt;&lt;path d=\"...\" \/&gt;&lt;\/svg&gt;\n       &lt;\/button&gt;\n      &lt;\/li&gt;`<\/span>;\n  });\n  list.innerHTML = HTML;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We knows <code>TODOs<\/code> is an Array, so we loop over it, creating one big string of HTML with all the <code>&lt;li&gt;<\/code>s we&#8217;ll populate the <code>&lt;ol&gt;<\/code> with. (We&#8217;ll monkey with that SVG later.)<\/p>\n\n\n\n<p>I feel like the native JavaScript Template Literal is a good fit here. That&#8217;s the string within backticks (<code>`<\/code>). This allows us to write multi-line strings and interpolate variables inside. This is a place where there is lots of choice though! We could have used a native HTML <code>&lt;template&gt;<\/code> here, and perhaps we will in the future. We could have used <a href=\"https:\/\/handlebarsjs.com\/\">a Handlebars template<\/a> or the like. If you&#8217;re used to using a JavaScript framework, this is essentially a component and essentially the heart of whatever framework it is. Like the JSX of React.<\/p>\n\n\n\n<p>The downsides of a Template Literal is that you don&#8217;t get syntax highlighting usually. It&#8217;s not going to be linted or checked like your other HTML. Still, I like how simple the Template Literal is here, let&#8217;s keep it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Completing a to-do<\/h2>\n\n\n\n<p>In the HTML for each of our to-dos, remember we have a <code>&lt;button&gt;<\/code> designed for clicking to complete a to-do. But they don&#8217;t have click event handlers on them yet. We could put an <code>onclick<\/code> handler as an attribute right on them. That&#8217;s not the world&#8217;s worst idea, since they would automatically have interactivity applied to them the second they hit the DOM. <\/p>\n\n\n\n<p>Just go another way though: event delegation. We can just watch for clicks on the whole document, and if the event originated on that kind of button, then we can do our work. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">document<\/span>.documentElement.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, (event) =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (event.target.classList.contains(<span class=\"hljs-string\">\"button-complete\"<\/span>)) {\n    <span class=\"hljs-comment\">\/\/ Click happened on a Complete button<\/span>\n  }\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>There is a little gotcha here though! We have an <code>&lt;svg&gt;<\/code> in our button, and it&#8217;s possible\/likely the user clicks directly on that, so <code>event.target<\/code> will be that and not the <code>&lt;button&gt;<\/code>. So a smidge of CSS will help us:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.svg-check<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: block; <span class=\"hljs-comment\">\/* prevent weird line-height issue *\/<\/span>\n  <span class=\"hljs-attribute\">pointer-events<\/span>: none; <span class=\"hljs-comment\">\/* stop click events from being target *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we need to do the actual work. We can figure out exactly which to-do this is by the unique ID that we gave the <code>&lt;li&gt;<\/code> element. That will match the ID in our data. So we look through our data and find that ID, mark it as complete, and put the data back. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">document<\/span>.documentElement.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, (event) =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (event.target.classList.contains(<span class=\"hljs-string\">\"button-complete\"<\/span>)) {\n    TODOs = TODOs.filter(<span class=\"hljs-function\">(<span class=\"hljs-params\">todo<\/span>) =&gt;<\/span> todo.id !== event.target.parentElement.id);\n    localStorage&#91;<span class=\"hljs-string\">\"data\"<\/span>] = <span class=\"hljs-built_in\">JSON<\/span>.stringify(TODOs);\n    buildUI();\n  }\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That <code>filter<\/code> function is <em>instantly<\/em> removing the to-do with a matching ID from the data. Ultimately our plan is to update the <code>complete<\/code> value in our data, so that we can show a list of completed to-dos. But we&#8217;ve done a lot today already, so let&#8217;s revisit that when we do more with JavaScript. We&#8217;ve still got editing to do and such.<\/p>\n\n\n\n<p>This is a little akwardness of the <code>localStorage<\/code> setup we have. Every time we touch our data, we rewrite the entire set of data each time. When you&#8217;re working with a &#8220;real&#8221; database, don&#8217;t download the entire database and replace the entire database when small changes are made, that would just be silly. But our data is so small\/light here, even if there were a few hundred to-dos, it&#8217;s not that big of a deal. But certainly a real database is a cleaner and more scalable approach. We could have also architected things differently, making each to-do a unique key in <code>localStorage<\/code>, but that didn&#8217;t have the Array characteristic we wanted, and just feels kinda sloppy to me. <\/p>\n\n\n\n<p>See that we&#8217;re calling our <code>buildUI()<\/code> function after making the data change as well, ensuring our UI is in sync with our data. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">So Far<\/h2>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XWGwxYV\" src=\"\/\/codepen.io\/anon\/embed\/XWGwxYV?height=450&amp;theme-id=47434&amp;slug-hash=XWGwxYV&amp;default-tab=js,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XWGwxYV\" title=\"CodePen Embed XWGwxYV\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-1-planning-design\/\">Building a TODO App from Scratch \u2014 Step 1 \u2014 Planning &#038; Design<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-2-html\/\">Building a TODO App from Scratch \u2014 Step 2 \u2014 HTML<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-3-basic-javascript-functionality\/\">Building a TODO App from Scratch \u2014 Step 3 \u2014 Basic JavaScript Functionality<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-4-styling-interactive-choices\/\">Building a TODO App from Scratch \u2014 Step 4 \u2014 Styling &#038; Interactive Choices<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/building-a-todo-app-from-scratch-step-5-extra-functionality\/\">Building a TODO App from Scratch \u2014 Step 5 \u2014 Extra Functionality<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>We left off with a wireframe-y looking HTML-ized version of our design. Personally, I&#8217;m a little tempted to get some CSS on there, but let&#8217;s hold off and do the very basic JavaScript functionality first. That will make it, ya know, actually functional, and having some of the interactive stuff in place might inform some [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1003,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[3,6],"class_list":["post-903","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-javascript","tag-todo"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/todo-thumb-1.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=903"}],"version-history":[{"count":19,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/903\/revisions"}],"predecessor-version":[{"id":1008,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/903\/revisions\/1008"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1003"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=903"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=903"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}