تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Project Setup Overview0:00

Let's continue with our exercises and work on the obligatory to-do app. You see to-do apps being implemented across several libraries and frameworks because it's a great example of how to perform common tasks like adding, removing, or updating DOM elements. So I'm going to start with the CSS and layout already complete as you see in the browser as I want to focus on the JavaScript, but we'll make sure to go over the HTML. So if we go into my code here, you'll see I have three files, the index.html, as you can see, not too much markup here, and we'll go over this in the browser DevTools. We have a reset.css, which is being imported right here, just a standard CSS reset, and we have all of the styles for the to-do app. And actually, I'm going to duplicate this HTML.

we have all of the styles for the to-do app. And actually, I'm going to duplicate this HTML. So if you want to follow along, you can start from the starting point. So let's go ahead and duplicate this. Let's name this one index-complete.html, and this is the one we'll work on. So let me open this with Live Server. Let's close this one, and let's take a look at the markup in DevTools. So we have our root element. I named it to-do app container. On the right, you can see we have a few styles on it, mainly the light gray background color.

Handling Form Submit2:43

So yeah, let's go ahead and get started. So let's start with what we have to select, and we'll start with the form, and we also need the input. So let's go into our code here, take a look at our form, and let's add an ID on the form. Let's name it to-do form, and the input already has a class of to-do input. So let's go ahead and select those two. Again, I'll just put it in a script tag down here, but you're welcome to add it in an external file if you like. So script, and let's select the to-do form, so const to-do form = document.querySelector(...). And this one is named to-do form with an ID.

So script, and let's select the toDoForm, so const toDoForm equals document.querySelector. And this one is named toDoForm with an ID. And then we have our toDoInput, so const toDoFormInput. That has a class of .toDoInput. So let's start with adding to-dos, we'll listen for the submit event on the toDoForm. So we'll say toDoForm.addEventListener, it's the submit event. Let's grab the event, and let's prevent the default, so event.preventDefault. And then how about we just console.log submitting, or let's say adding to-do, adding a to-do. Let's try that out in the browser, maybe just remove the query string here. Let's go to our console, let's type something in here, hello there, and it does work.

Let's try that out in the browser, maybe just remove the query string here. Let's go to our console, let's type something in here, hello there, and it does work. So to grab what the user types in, we can make use of event.target.value, so let's go ahead and add that here. Let's change this to backticks, adding a to-do, and let's just put event.target.value in here to make sure it's correct. And let's also clear the to-do input after it's done. So we have our to-do form input, we can get the value of that and set it to an empty string. Let's save that, check it out in the browser, let's add a to-do, okay, adding a to-do undefined. Actually we don't need event.target.value, we just need to-do form input value.

Let's save that, check it out in the browser, let's add a to-do, okay, adding a to-do undefined. Actually, we don't need event.targetValue, we just need to-do form input value. to-do form input value, let's try that one more time, adding, and that works, cool. Okay, so what do we actually need to do when we add a new to-do? So if we go back to our elements tab and highlight the list item here, right here, so what we want to do here is have another list item, but the label is whatever the user types in. So we can actually duplicate this in DevTools, duplicate element, and you see now we have two elements here, we have this one and the duplicated one. And now all we have to do is update the label, so this label right here changed, or whatever

Cloning and Appending Items5:36

two elements here, we have this one and the duplicated one. And now all we have to do is update the label, so this label right here changed, or whatever user added. Okay, so if we go back to our code here, we can create an element and build it up like I showed you in the creating elements video, but the easiest way would be to just clone what we already have. So this assumes that there's at least one to-do item when we start, and in this case we already have that, so let's stick with that for now. So the to-do item is right here, and it's a list item with a class of to-do item container. How about we select that, let's name it to-do item, let me just duplicate this, to-do item

So the to-do item is right here, and it's a list item with a class of to-do item container. How about we select that, let's name it to-do item, let me just duplicate this, to-do item query selector is to-do item container, okay, and we can make use of the cloneNode method to clone this element. So right in here, instead of console logging, let's make a new element here, so say const newToDoItem equals to-do item, we can make use of the cloneNode method right here, and we can set true for the parameter because we want the children to be cloned as well, okay. And how about we console log that to see what we get, newToDoItem, okay, save that back to the browser, let's go to the console here, let's add something in here, and you can see

And how about we console.log that to see what we get, new to-do item, okay, save that back to the browser, let's go to the console here, let's add something in here, and you can see we do get the element here. Now it's not in the DOM yet, but it does have the children, okay. So let's go ahead and insert this into the DOM. So we need to select the parent element first, so in this case, the to-do list. So let's select that first, const to-do list = querySelector('.to-do-list'), the name is to-do-list as a class, to-do list, okay, and we can make use of append to append the new to-do item. So let's get rid of this, let's actually put it down here, let's say to-do list.append

item. So let's get rid of this, let's actually put it down here, let's say toDoList.append(newToDoItem), okay. Let's save that, let's see if it actually shows up here, adding, and it does, cool. So now we just have to update the actual label of the to-do. So back to our code here, so essentially we have a listItem here, and we want to update this text here. So we can use querySelector to look for the toDoItemLabel, and look for this element, and update the textContent. So let's go ahead and do that.

and update the text content. So let's go ahead and do that. So right here, newToDoItem.querySelector, we're looking for the toDoItemLabel, and we're setting the text content on that to whatever the user typed in. So that's toDoFormInput.value, okay. Let's get rid of this, so we're cloning the node, we're updating the label, and then we're adding it to the DOM. So let's try that out, and then we're clearing the toDoFormInput. Save that, back to the browser, let's add, adding, and it does work with whatever we typed in here.

Deleting with Delegation9:32

You can actually remove elements in DevTools as well, right click, delete element, and now it's gone. So if you remember in the event delegation video, we can add an event listener on the actual delete button, so this button right here. But as we add new items, we won't have an event listener on that new item. So the better approach here would be to add the event listener on a nearby parent, in this case we'll add it on the ul, and then we can use event.target to listen for clicks on the X button, or the delete button. So let's go ahead and do that. So back to our code, let's add another event listener here.

So let's go ahead and do that. So back to our code, let's add another event listener here. So it's going to be on the to-do list. So to-do list.addEventListener, click, and then when that happens, we want to listen for the clicks on the delete button. So again, the delete button has a class of X button right here. So we can say if event.target, so if the thing we actually clicked on, let's see if it has a class, so classList.contains of X button. So if that's the case, then we clicked on the delete button. So let's just console.log deleting to-do.

So if that's the case, then we clicked on the delete button. So let's just console.log deleting to-do. Let's try that out, back to the browser. Let's go to the console here, let's add a few, add another to-do, okay. Not sure what this error here is, but you can just ignore it for now. And let's see if we can delete these to-dos, and we do get the console.log, okay. So now what we have to do is, again, let me open up the elements tab. Let's go to one of these list items, or one of these to-dos. So right now, event.target is the button, so we have access to this, and what we want to delete is the closest parent element that has the class of to-do-item-container.

So right now, event.target is the button, so we have access to this, and what we want to delete is the closest parent element that has the class of to-do-item-container. So that's right here, if we clicked this button here. So again, we can make use of the closest method to grab that, so we're moving up the tree and looking for this element here. So let's go ahead and do that, back to the code. Let's remove this, let's make a new element. So const to-do-item-to-delete equals event.target, and in this case, that's the button, or the delete button that we clicked. We can say .closest, and the actual list item has a class of to-do-item-container.

delete button that we clicked. We can say .closest, and the actual list item has a class of to-do-item-container. So to-do-item-container, okay. And to remove it, we can say to-do-item.to-delete.remove, that removes it from the DOM. Let's see if we did this correctly. Save that. Back to the browser, let's add a few, add a few, let's delete, add, okay, that's the way it seems to work. Let's delete the first one, that works as well, and let's delete the last one. That works as well.

Toggling Completion State12:25

Let's delete the first one, that works as well, and let's delete the last one. That works as well. Cool. Okay, now let's work on checking to-dos. So let me just refresh here, and when we check on this, we want to complete this to-do. So I do have a class already defined, named line-through, which will put a line through this label. So let me just show you here, line-through, and you can see it crosses it out, and you can see it's just text decoration line-through. So it's going to be very similar to the close button.

can see it's just text decoration line-through. So it's going to be very similar to the close button. So we want to use event delegation and listen for clicks on this checkbox here, and this checkbox has a class of input[type=checkbox]. Actually I'm going to put a class on that just to make it a bit easier. So let's do that. Let's look for the checkbox, let's put a class on that, class="to-do-check". Okay, we can make use of the code we already have for event delegation for the delete button. So right here, we're listening on the unordered list, but now we want to listen for clicks.

Okay, we can make use of the code we already have for event delegation for the delete button. So right here, we're listening on the unordered list, but now we want to listen for clicks on the checkbox. So I'm going to grab the code we have for the delete button, let's paste that in, let's close the if, and instead of X button, it's now to-do check. So the checkbox, to-do check, okay. And how about we just console.log something in here to make sure it does work. So console.log, item, was, check, okay, save that, back to the browser. Let's go to the console here, let's add one more, let's check, and it does seem to work. Cool.

Let's go to the console here, let's add one more, let's check, and it does seem to work. Cool. Let me open up the elements tab again, and let's select a checkbox here. So this one here, as we check this one, we want to add that class on the actual label. So this is the sibling element, and we can make use of nextElementSibling to grab it, and then update the class. So let's go ahead and do that. So back to our code, let's say const, toDoItemToCheck, equals, or let's say toDoItemToComplete. And event.target is the checkbox, but the sibling, so nextElementSibling, is the element.

item, to complete. And event.target is the checkbox, but the sibling, so next element sibling, is the element we want to toggle the class on, okay. And we can say toDoItem, to complete, classList, we can use toggle here to toggle that lineThrough class, lineThrough, and let's see if this works. Save. Back to the browser, let's check this, and it does seem to work. Cool. Let's make sure it works on other elements as well, so add a few, let's check these ones as well, and it does seem to work.

Counting Items Remaining15:16

Let's make sure it works on other elements as well, so add a few, let's check these ones as well, and it does seem to work. Cool. Okay, let's finish off by implementing this remaining feature here. So we want to keep track of how many to-do items are remaining, so in this case, finished JavaScript series is not checked, so there should be one. But if I check it, it should be zero. So how do we calculate that? So remember our to-do items here have this class of line-through, if it's complete. So we want to count it if it doesn't have that class.

So remember our to-do items here have this class of line-through, if it's complete. So we want to count it if it doesn't have that class. So again, if I uncheck this, you can see the class is now gone, so this one is not complete, so there's one remaining. So we want to count all of the labels that don't have that class, so let's go ahead and do that. So back to our code, up here, let's create that function, actually, I'll put it right here. Let's name it calculateItemsRemaining, okay, and again, we need all of those labels, so in this case, the label is toDoItemLabel, and we want all of them, so we can make use

Let's name it calculateItemsRemaining, okay, and again, we need all of those labels, so in this case, the label is to-do item label, and we want all of them, so we can make use of querySelectorAll. So let's do that. So back to our code, let's say const to-do item labels equals document.querySelectorAll(' .to-do item label'), okay? So there should be a node list, and let me show you two ways we can do this. So we can forEach over this, so to-do item label.forEach, and for each iteration, we'll get a label or an element, so to-do item label, okay? And if this element doesn't have that line through class, then we want to count it, so

we'll get a label or an element, so to-do item label, okay? And if this element doesn't have that lineThrough class, then we want to count it, so we need a variable to keep track of the count. Let's put it here, let count equals 0 to start, and we want to do that check. So if the to-do item label does not have that class, so classList.contains lineThrough, and we want to make sure it does not have that class, so we'll put the ! in front for not, and if this is the case, we want to increase the count. So count++, or we'll just do count equals count plus 1. And after that's done, after this forEach is done, we want to update the actual label, so this label here.

And after that's done, after this forEach is done, we want to update the actual label, so this label here. So we need to grab that as well. Actually, I think I want to wrap a span around that number, so up here, where we have three items remaining, let me wrap this in a span as well, with an ID span with an ID of itemsRemaining, okay? Now we can grab that here, let's duplicate this, let's say itemsRemaining, and that has an ID of itemsRemaining, okay? And now we can update that, so itemsRemaining, after this loop, or after this forEach, let's say itemsRemaining.textContent equals count, okay?

And now we can update that, so itemsRemaining, after this loop, or after this forEach, let's say itemsRemaining.textContent equals count, okay? Hopefully I did that correctly, but we're not actually calling this function. So how about we call it right as the app initializes? So anywhere in here, so let's call it right after we select our elements. So calculateItemsRemaining, okay? And initially there's only one, and it's not checked, so if I did that correctly, we should have one. So let me save that, go back to the browser, and we do have one here. So let's go back to the code, and hard code line through for the to-do item we have in

So let me save that, go back to the browser, and we do have one here. So let's go back to the code, and hard code lineThrough for the to-do item we have in here, and let's see if it goes to zero. lineThrough, it does, so that seems to work, let's remove that again, okay? Back to one. But it's not going to update as we check or delete the to-dos, so if I check it, you can see it doesn't update. So we have to make sure to call it every time we check or delete our to-dos. So let's do that. calculateItemsRemaining, let me just copy this, actually we have to add it here as well.

So let's do that. Calculate itemsRemaining, let me just copy this, actually we have to add it here as well when we're adding a new to-do, when we're deleting a to-do, so right here, and also when we're checking a to-do, so right here, okay? Now it should update as we update our to-dos, so save that, let's add a few more. And you can see it's going up, let me just close this, another to-do, it's going up as we check, that should work as well, cool, two, one, zero, and if we delete, that should update as well, so if I delete this, there should be no more itemsRemaining, and it does seem to work. Let me show you another way you can do this, and that's to make use of the filter method,

does seem to work. Let me show you another way you can do this, and that's to make use of the filter method, so let's go back to our code, back to our calculate method, where is it, up here, and this is fine, but I'll show you how you can do it using filter. So let me comment this out, let's make a new variable here, let's say const itemsCount equals, and we want to make use of the filter method on arrays, but our toDoItemLabels is a node list, so we can convert that to an array using Array.from(toDoItemLabels), so now it's an array, and now we can make use of our filter, so .filter, and the parameter for this is a single toDoItemLabel, and it's a callback function, and itemsCount will only have the values that match this condition, so let's say return, and the condition

for this is a single toDo item label, and it's a callback function, and itemsCount will only have the values that match this condition, so let's say return, and the condition is the same as this if in here, so if that label contains the class of lineThrough, so let me just grab this, paste that in here, so this itemsCount will be an array of toDo item labels that do not have the class of lineThrough, so essentially the toDos that are remaining, but we just want the length of that, so we can say .length, since it's an array, length, okay, and now we can change this to itemsCount, and it's basically the same thing, so let's save that, let's make sure this still works, one is correct, it does update, let's add a few more, a few more, and it looks like we sort of have a bug here, you can see as I added those toDos, it was cloning this one, which was already complete,

does update, let's add a few more, a few more, and it looks like we sort of have a bug here, you can see as I added those to-dos, it was cloning this one, which was already complete, so these ones were complete as well, but just to show you the items remaining still works, you can see it does, let's quickly fix that bug and then call it a day, so again if we check this and add a new to-do, then it's checked as well, which we don't want, so we have to remove the class and also uncheck the box as we add new to-dos, so we already have this to-do item label, so again if you don't remember, to-do item label is where the line through goes, so we can remove that class, so let's do that, right here, let's duplicate this, instead of changing the text content, we want to remove the class, so classList.remove, and the class is line-through, line-through, okay, see if that worked, let's

duplicate this, instead of changing the text content, we want to remove the class, so classList.remove, and the class is lineThrough, lineThrough, okay, see if that worked, let's save that, let's try that one more time, so let's add here, or let's check this first, let's add, and you can see the lineThrough is now gone, but we also have to uncheck this box here, so again the checkbox is, or has a class of toDoCheck, so we can select it like that, so a new querySelector, toDoCheck, and we can use the checked property and set that, so checked equals false, and we're selecting the toDoItemLabel twice, which is fine, but we don't want to jump into the DOM twice and select the same thing, so let's grab this, and also this, let's replace it with toDoItemLabel, so toDoItemLabel, and let's paste that in here, so const toDoItemLabel equals this, and if I did that

grab this, and also this, let's replace it with toDoItemLabel, so toDoItemLabel, and let's paste that in here, so const toDoItemLabel equals this, and if I did that correctly, it should work, so again let's check this, let's add a new one, and now it's not checked, but if we do it the normal way, that should work as well, adding, and it does work, cool, so we built this to-do app, which has adding to-dos, removing to-dos, checking to-dos, and also has this remaining count here, all with vanilla JavaScript and what we learned so far in this course. In the next video, we'll take a look at one possible refactor we can do to give some structure and organization to our code.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟