Exciting Web Development Journey – To Do List

Hi there! Let’s continue our Web Development Journey we started in the previous post when we learned HTML, CSS, and JavaScript concepts. We had the opportunity to build a proof-of-concept web page to work with Sharing is The Key Website light and dark themes. I will assume you know some of the conventions, tricks, concepts, and tools we taught in the previous post for better productivity.

SITK To Do List

Scenario

We will make a web app for a to-do list. We’ll be able to use this app to track whatever we want: cool programming tricks that you want to learn, places to go, songs to listen to or learn to play, or something as basic as things to pick up at the stores. It will be possible to add and remove to-do items from the list and save them so they reload for you later.

Learning goals

  1. Getting to know more HTML elements such as Form and input text fields
  2. Styling using global variables, Flexbox module, and many other CSS resources
  3. Creating your own JavaScript functions
  4. Listening for different user actions on a web page
  5. Using functions together to write code more professionally.
  6. Saving user information between visits to your web page

What you will need

  1. VS Code, the cross-platform code editor. Or one of your preference.
  2. Docker Desktop latest stable version. You can see how to set it up here.
  3. Familiarity with at least one programming language. You don’t need to be an expert.
  4. A computer running one of the following:
    • Windows: Windows 10 Home or Professional
    • Mac: macOS 10.9 or later
    • Linux: Ubuntu, Debian, Red Hat, Fedora, or SUSE

Project structure

Let’s clone the previous post project to save some time and learn more about git CLI.

In your terminal, type the following command and hit Enter.

git clone https://github.com/sitknewnormal/SITK-themes.git "SITK-ToDoList"

This command will pull SITK-themes source code from our Git Hub repository and copy it into the SITK-ToDoList new folder.

SITK-TodoList project structure

Move into the SITK-ToDoList project folder, remove git source control, and open the VS Code by running the following commands.

cd .\SITK-ToDoList\; Remove-Item -LiteralPath ".git" -Force -Recurse; code .

opening SITK-ToDoList in VS code

It will open SITK-ToDoList in VS Code. If you remember from the previous post, it will ask you to reopen the project into a Dev Container, but for now, let’s not do this because I want to introduce you to an exciting VS Code extension.

SITK-ToDoList in VS Code

Let’s open the devcontainer.json file, make some changes and save them:

  • In the extensions values, replace "tht13.html-preview-vscode" by "ritwickdey.liveserver" (Live Server). Live Server extension launches a local development server with live reload feature for static and dynamic pages;
  • Change the name to “SITK – ToDo List.”
making changes to dev container config file

Now that we have everything in place, close VS Code and reopen it, but at this time, choose to reopen the project into the Docker Dev Container when asked to do so.

Let’s code HTML

When the Dev Container is up and running, and the project is ready to be edited, let’s go to the index.html and make some changes.

First, click on Go Live in the status bar to start our Live Server.

opening Live Server

It will open the web page in your default browser. Let’s put the VS Code and the browser windows side-by-side to see the reflection of what we are coding in the browser as soon as we save the changes, and let’s close the VS Code Explorer to get more room to code.

VS Code and browser side by side

Let’s get rid of everything between the opening <body> and closing </body> body element except for the theme switcher button element and save it.

SITK-ToDoList clearing out everything in the body

Let’s give our app an important header To-do List using <h1> element at the beginning of the body and save it.

SITK-ToDoList adding H1

We will need a paragraph <p> right after the header to inform the user to mark an item as completed by double-clicking on it. Please save it to see it in the browser.

SITK-ToDoList adding a paragraph with some instruction

To add items to our To-do List, we will need a Form with an input text element and a button. An HTML Form is used to collect user input. The user input is most often sent to a server for processing, but this is not our case for this post. Therefore copy the code below to a new line right after the paragraph we’ve just added.

        <form name="todo-adder">
            <input type="text" id="todo-entry-box" />
        </form>
        <button id="add-btn" class="pretty-button">Add</button>

Notice that we  named our Form name="todo-adder", we’ve given an identification id="todo-entry-box" to our input text, we did it to the button id="add-btn" as well, and we also created a class class="pretty-button" for future CSS styling. It is essential to keep in mind that naming, giving identification to HTML elements is always a good practice and will help you style them uniquely. Classes are often used to style a group of elements. Furthermore, the naming convention for CSS attributes such as name, id, and class suggests using lowercase and the hyphen for composed names.

SITK-ToDoList adding a form input text and a button

Now let’s break a line to separate the Form from the items, and let’s create an ordered list container so the user can see the item they will add to the list. Please copy the code below to a new line after the Add button.


        <br />
        <ol id="todo-list">
        </ol>

We gave an ID id="todo-list" to the To-do List for future CSS styling.

SITK-ToDoList adding ordered list

Next, we will create three buttons and wrap them in a div element. When it comes to productivity, VS Code is one of the best editors. The Emmet feature can help you with repetitive and boring coding tasks, and no extension is needed. Take a look at the video below to have a taste of VS Code editor’s power.

Let’s give them some IDs and texts accordingly to the chunk of code below.


    <div id="control-wrapper">
        <button id="clear-completed-btn" class="pretty-button">Clear Completed</button>
        <button id="empty-btn" class="pretty-button">Empty List</button>
        <button id="save-btn" class="pretty-button">Save List</button>
    </div>
SITK-ToDoList adding three buttons

Let’s do one more cool thing! To make our To-do List look better, we will wrap the elements from line 12 (Header 1) to 25 (three buttons div) in a div to style it. We will select lines 12 to 25, type the shortcut Ctrl+Shift+P to access the command palette, type Emmet: Wrap with Abbreviation, when it opens to enter the abbreviation, type div.todo-wrapper and hit Enter.

The final HTML code should look like this.

SITK-ToDoList final HTML code

As we could notice, we didn’t touch the head section where we still can see the style sheet linked, and we didn’t change the script tag where we still keep our JavaScript also linked.

Styling HTML with CSS

We already have our CSS from the previous post set up, which already supports light and dark themes, and we will keep and extend it. By the way, spending some time styling an application is very important and allows a better user experience.

Let’s do some cleaning before we style the new HTML elements we added.

First, let’s get rid of li, ul and .list selectors since we don’t have them anymore in the HTML file. Plus, let’s add three more colour variables in the root element. Please copy the code below at the end of the root element and save it.


    --laser1-light: #e5d9b6;
    --light-gray: #3a3a3a;
    --completed: #666666
SITK-ToDoList adding variables to the root element

We will use --laser1-light and --light-gray to style the to-do wrapper div’s background colour when we switch between light and dark themes, respectively. Place each variable inside light and dark themes selectors.

SITK-ToDoList placing new variables in dark and light themes

Let’s change the theme switcher button absolute position to the web page’s right top corner by changing the top key to 0px, replacing left by right key and set it also to 0px, in the .btn selector.

SITK-ToDoList setting theme switcher button to top right corner

Next, we will style the to-do wrapper div setting the background colour, border-radius, top, left and right margin and padding. Please copy the code below to the end of the CSS file. Notice that we are already using the --todo-wrapper-bg variable so the background will assume --laser1-light or --light-gray for the light and dark themes, respectively.


.todo-wrapper {
    margin-top: 60px;
    background-color: var(--todo-wrapper-bg);
    border-radius: 30px;
    margin-left: auto;
    margin-right: auto;
    padding: 30px;
}

Let’s check our work so far, but first, create four items in the ordered list using Emmet, remember? Inside the <ol> element, start typing li*4 and hit Enter.

SITK-ToDoList styling ToDo wrapper div

Now let’s style the only paragraph inside the to-do wrapper using the --completed variable we defined previously to set the colour; let’s set it up to italic and define margins. Please copy the code below to the end of the CSS file.


.todo-wrapper p {
    font-style: italic;
    color: var(--completed);
    margin: 5px 0;
}

The variable --completed will also be used to style the finished tasks’ font and colour.

Next, we will style the ordered list and its items, setting margin and some padding to the items to allow a little bit of space between them. Please copy the code below to the end of the CSS file.


.todo-wrapper ol {
    margin: 20px 0;
}

.todo-wrapper ol li {
    margin: 5px 0;
    padding: 3px;
}

Now let’s highlight the even items in our list for easy reading. Please copy the code below to the end of the CSS file.


.todo-wrapper ol li:nth-child(even) {
    background: #999999;
}

Let’s check our work so far.

SITK-ToDoList styling list items in the ordered list

Next, we will style the completed list items. First, let’s go to the HTML file and set the two first items as completed using class="completed" attribute.

SITK-ToDoList setting completed list items in the HTML file

Now, we will style those completed list items. Please copy the code below to the end of the CSS file.


.todo-wrapper .completed {
    text-decoration: line-through;
    color: var(--completed)
}
SITK-ToDoList styling completed list items

Time to style all buttons inside to-do wrapper div to look pretty. Remember that we used class="pretty-button" all buttons except the theme switcher? We are now changing their margin, padding, border-radius, background colour, border, text alignment, and more at once. Please copy the code below to the end of the CSS file.


.todo-wrapper .pretty-button {
    margin: 10px 2px;
    padding: 5px 20px;
    border-radius: 15px;
    background-color: var(--bg);
    border: none;
    color: var(--fontColor);
    text-align: center;
    display: inline-block;
    outline-style: none;
}
SITK-ToDoList styling all buttons inside To-do wrapper div

If you hover the mouse over the buttons, you will see no indication they are clickable. Let’s fix it by changing the cursor to a pointer and add a little bit of opacity as we hover over them. Please copy the code below to the end of the CSS file.


.todo-wrapper .pretty-button:hover {
    cursor: pointer;
    opacity: 0.75;
}

If we type in the text input field in the Form, we will notice the text look tight inside the text field. Let’s fix it by adding some padding. Please copy the code below to the end of the CSS file.


.todo-wrapper input {
    padding: 6px 6px;
}
SITK-ToDoList styling input text field

Next, we will fix the Add button, placing it on the input text field’s right side, which looks better.

SITK-ToDoList styling form children display

Display using inline-block makes the element generate a block box that’s laid out as if it were an inline box. According to QuirksMode: An inline-block is placed inline (i.e. on the same line as adjacent content), but it behaves as a block.

We will now align the buttons in the control-wrapper div using the display flexible box module, a relatively new and easy way to arrange elements on the page.  There is rich content here, so you can dig deep and learn more. For now, let’s say that the setup below finds a way to arrange the buttons at the center of the div and provides even spaces between them and the margins, which fits in most of the scenarios. Please copy the code below to the end of the CSS file.

#control-wrapper {
    text-align: center;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
}

For the matter of responsiveness, the following code uses media query CSS resource to resize the .to-do wrapper width and .to-do wrapper input width. For devices with screen resolutions of at least 1400 pixels, the .to-do wrapper width will be set to 75%, and the .to-do wrapper input width will be 600 pixels. For devices with screen resolutions between 750 pixels and 1399 pixels, the .to-do wrapper width will be set to 50%, and the .to-do wrapper input width will be 400 pixels. Please copy the code below to the end of the CSS file.

@media only screen and (min-width: 1400px) {
    .todo-wrapper {
        width: 50%;
    }

    .todo-wrapper input {
        width: 600px;
    }
    
}

@media only screen and (max-width: 1399px) and (min-width: 750px) {
    .todo-wrapper {
        width: 75%;
    }

    .todo-wrapper input {
        width: 400px;
    }
}
SITK-ToDoList adding responsiveness

We can better see the responsiveness explained before in the following video where the .to-do wrapper width and .to-do wrapper input width resizes as soon as they reach the resolution limits defined in the media queries.

It is crucial to think of the behaviour you want your application to have in different devices and resolutions to catch and keep the user’s attention.

Let’s see the viewport metadata behaviour and how the elements arrange themselves in desktop, tablets, smartphones when properly styled using Flexbox, media queries, display inline-block and so on. Go back to the browser and click on the Toggle device toolbar button and play with the options.

Now that we have an awesome To-do List app well structured and styled, we will give it some action with JavaScript.

JavaScript

If you look at the page, you’ll see it has four buttons:

  • An Add button for adding new to-do items
  • A Clear Completed button for clearing items that you’ve marked as finished
  • An Empty List button for completely emptying the to-do list
  • A Save List button for saving what’s on the list

Of course, since you haven’t written any code yet, right now, none of them do anything!

Let’s add some code to our JavaScript file.

Add button

It is essential to tell JavaScript which parts of the HTML page are relevant and how user interactions with these parts should react. In this case, you want to tell it about the Add button and tell it to react when the user clicks this button.

Getting the Add button

Start by creating a button variable and telling JavaScript to get the id="add-btn" button element from the HTML document. Please type or copy the following code to the end of our JavaScript file and save it.

const addBtn = document.getElementById("add-btn");

Listening for the click

Now connect your button to an event handler so that JavaScript listens to a particular type of event and then runs a function when it hears it. The event is a click in our case. Do this with the function addEventListener. Please type or copy the following code to the end of our JavaScript file and save it.

addBtn.addEventListener("click", addToDoItem);

This listener will wait for the addBtn click, and when the click is heard, it will respond by running the addToDoItem function. Of course, because you haven’t written the addToDoItem function yet, it just won’t work yet!

Creating the function

Later in this post,  you will be writing code for your functions to add to-do items, clear the list, save it, etc. But for now, you want to check that you’ve connected your event listeners properly.

Create the addToDoItem function so that it will pop up an alert message informing the user which button they have clicked. Please type or copy the following code to the end of our JavaScript file and save it.


function addToDoItem() {
    alert("Add button clicked!");
}

Let’s check our work so far. Put the VS Code and the browser side by side again, and click on the Add button.

SITK-ToDoList checking Add button alert message

Write code for the remaining buttons.

Try it on your own, following the Add button steps to make sure you got the idea.

Connect the Clear Completed button, which has the Id clear-completed-btn, to an alerting function called emptyList.

Connect the Empty List button, which has the Id empty-btn, to an alerting function called clearCompletedToDoItems.

Connect the Save List button, which has the Id save-btn, to an alerting function called saveList.

Let’s see if you got it right! Check the code below and make some adjustments if needed or copy the following code to the end of our JavaScript file and save it.

let clearCompletedBtn = document.getElementById("clear-completed-btn");
clearCompletedBtn.addEventListener("click", clearCompletedToDoItems);
function clearCompletedToDoItems() {
    alert("Clear completed button clicked!");
}

let emptyBtn = document.getElementById("empty-btn");
emptyBtn.addEventListener("click", emptyList);
function emptyList() {
    alert("Empty button clicked!");
}
let saveBtn = document.getElementById("save-btn");
saveBtn.addEventListener("click", saveList);
function saveList() {
    alert("Save List button clicked!");
}

Don’t forget to save it! So far, the code should look like this.

SITK-ToDoList remaining buttons code so far

Click on all buttons to make sure they are working.

Add to-do items

Time to make the first of those buttons work correctly! This step will show you how to add a to-do item to the list.

First, go to the HTML file and delete all four list items <li> inside the ordered list <ol> and save it.

SITK-ToDoList clearing all list items from the ordered list

We are going to use a tiny bit of HTML in this step. Therefore you need to write some JavaScript to add <li> elements for each new to-do item. The user should enter text and then click the Add button to see it appear on the list as a numbered item.

First, just like you did with the buttons, create variables to select the text box and the ordered list. They already have the Ids todo-entry-box and todo-list. Please type or copy the following code to the end of our JavaScript file and save it.

let toDoEntryBox = document.getElementById("todo-entry-box");
let toDoList = document.getElementById("todo-list");

Now the text box and the ordered list can be easily reached from within your JavaScript.

Let’s create a function called newToDoItem to add an item to the list. This function will need to have two inputs:

  • The text of the item
  • The item completed or not

Of course, no new to-do item will ever be complete, but we are preparing ahead of time here: we will use the same function again when we load a saved list with some finished items. Please type or copy the following code to the end of our JavaScript file and save it.


function newToDoItem(itemText, completed) {
    let toDoItem = document.createElement("li");
    let toDoText = document.createTextNode(itemText);
    toDoItem.appendChild(toDoText);

    if (completed) {
        toDoItem.classList.add("completed");
    }

    toDoList.appendChild(toDoItem);
    toDoItem.addEventListener("dblclick", toggleToDoItemState);
}

This newToDoItem  does a few things.

let toDoItem = document.createElement("li");

Creates an li element to use as your new list item.

let toDoText = document.createTextNode(itemText);

Creates a text node, a special container for a text that we want to put inside an HTML element using JavaScript, and fills it with the contents of the itemText variable that is passed into the function.

toDoItem.appendChild(toDoText);

The function appendChild takes the element, or text node, that you pass to it (in this case toDoText), and puts it inside toDoItem. If there are already elements inside that one, the one we are adding now will be last.

if (completed) { toDoItem.classList.add("completed"); }

Checks if the value for the completed variable that was passed to newToDoItem is true. If it is true, it will add the class completed to the li element, changing how it looks on the page. In our CSS file, there are special styling rules text-decoration: line-through; for li elements.

toDoList.appendChild(toDoItem);

The function appendChild puts toDoItem, the <li> element inside of toDoList, the <ol> element.

toDoItem.addEventListener("dblclick", toggleToDoItemState);

Attaches an event listener for double-click to the toDoItem And tells it to call a function named toggleToDoItemState in response.

Next, connect to the function to the Add button. Change your existing addToDoItem function to get the text from the box and pass it to the function newToDoItem we’ve just created.


function addToDoItem() {
    let itemText = toDoEntryBox.value;
    newToDoItem(itemText, false);
}

Since a new to-do item is never finished, you can always pass false to the completed parameter of the newToDoItem function.

Let’s test it to see if it works!

SITK-ToDoList testing Add button

Completing items

When you can’t mark items as done, there’s not much point to a to-do list! It’s time to add that feature.

For a double-click on a to-do item, we have already set up the listener. All we need to do now is write a function that, when that double-click occurs, will toggle the item between complete and not complete.

Note that to mark items as complete, we are using the completed class. Not having that class implies that they aren’t complete. All our function needs to do is add or remove the class from the item’s class list, either add it if it is not yet on the list or delete it.

The this keyword

The trick is to know which item the class can turn on. You’ll need to use a new JavaScript keyword to describe the object that was clicked: this.

It’s a little bit complicated how exactly the this keyword works, but all we need to know here is that when used with a function called by an event listener, it means ‘the element to which the listener was bound.’ But you can use this to find the particular list item <li> we clicked on.

Add the toggleToDoItemState function to the end of our JavaScript like so:

function toggleToDoItemState() {
    this.classList.contains("completed") ? this.classList.remove("completed") : this.classList.add("completed");
}

Let’s test it to see if it works!

SITK-ToDoList marking items as completed

Remove items

If we have marked the items as complete, we will want to delete all of the completed items. Also, we may want to clear off everything on it if we come back to your list after a long time or if we just want to focus on something entirely different. To do this, we just need to update two functions that have already been connected to the buttons: clearCompletedToDoItems and emptyList.

Clearing completed items

Just as you can select all the elements in an HTML document, you can select the elements within any other element. The children of that element are called the elements inside another element. Likewise, just as you can select the Id elements, you can also select them by class.

Let’s update the existing clearCompletedToDoItems function with code to select the toDoList children who have the completed class to clear the completed items. Then loop over the selected items to remove one by one.

function clearCompletedToDoItems() {
    let completedItems = toDoList.getElementsByClassName("completed");

    while (completedItems.length > 0) {
        completedItems.item(0).remove();
    }
}

You will see that the code will always delete the item at position 0 of the list, the first item on the list. To do this, you need to use 0 since JavaScript starts counting at 0 and not 1. You delete this item to replace the first item every time the loop runs, so the list gets shorter and shorter. In this way, the loop will finally delete them all, no matter how many incomplete items are listed.

Let’s test it to see if it works!

SITK-ToDoList clearing items completed step1
SITK-ToDoList clearing items completed step2

Clearing everything

Let’s focus on the existing function emptyList. To delete all from the list, do the same thing as above, but pick all the children from the toDoList.


function emptyList() {
    let toDoItems = toDoList.children;
    while (toDoItems.length > 0) {
        toDoItems.item(0).remove();
    }
}

 It is always recommended to check your work as soon as you advance on it.

Save the list

You can save the to-do list to a local storage location on the user’s computer to make it even more useful. Then, as long as they open it in the same browser next time, it will remember their to-do list.

There are two important steps for this task: saving the list and loading it again when the page is reloaded if the list is there.

This gets a little tricky: HTML can’t be saved in the local storage, so we need to take the HTML code and convert it to pure JavaScript. To do that, you will need an array.

An array is a variable of a special kind, which is a list of variables. You can build one with square brackets and use the push method to add objects to it. You can remind yourself what a specific array item is, using alert and the item’s position in the array. Remember that JavaScript starts counting at 0. Take a look at the code below to understand it better.

const myArray = [];
myArray.push("array item 1");
myArray.push("array item 2");
alert(myArray[0]);
//This will alert "array item 1"

Next, we need to loop over the list toDoList and add each item to the array. Remember that we need to store the task and whether or not it has been completed. The best way to do this is to use JavaScript objects.

JavaScript objects

A JavaScript object is a set of properties and values. You create one like this:

const toDoInfo = {
    "task": "Learn HTML",
    "completed": false
};

You just need to save all the to-do items to local storage once you have converted them into objects. Local Storage can only store strings, but fortunately, JavaScript turns arrays into strings for you when you use the stringify function.

Saving in a Local Storage – Step 1

Enough said, let’s put it all together!

Let’s update the existing saveList function to:

  • Make an array
  • Use a for loop to put every item in toDoList into the array as an object
  • Stringify the array and store it in local storage with the key toDos

Here is the code for the saveList function.

function saveList() {
    const toDos = [];
    for (let i = 0; i < toDoList.children.length; i++) {
        let toDo = toDoList.children.item(i);
        let toDoInfo = {
            "task": toDo.innerText,
            "completed": toDo.classList.contains("completed")
        };
        toDos.push(toDoInfo);
    }
    localStorage.setItem("toDos", JSON.stringify(toDos));
}

Load the saved list – Step 2

We need to reverse everything we have done to save it, to load the list. But first, we need to check if anything needs to be loaded. You do this by checking whether there is no null value on the key you used to store the list. ‘Null’ is yet another ’empty’ word or ‘nothing’ word.

Let’s create a loadList function to:

  • Check if the toDos key exists in local storage
  • If it does, load it into a variable as an array
  • Loop over the array, and use newToDoItem to create new to-do items for everything in it

Add the code below to the end of our JavaScript file.

function loadList() {
    if (localStorage.getItem("toDos") != null) {
        let toDos = JSON.parse(localStorage.getItem("toDos"));

        for (let i = 0; i < toDos.length; i++) {
            let toDo = toDos[i];
            newToDoItem(toDo.task, toDo.completed);
        }
    }
}

Let’s call the loadList function after we have created it. Add the code below to the end of our JavaScript file and save it.

loadList();

To close it with a golden key, let’s save it to the local storage every time we add a new item, mark an item as completed, clear completed items, and empty the list.  All we need to do is adding the code below at the very end of addToDoItem, clearCompletedToDoItems, toggleToDoItemState and emptyList functions.

saveList();
SITK-ToDoList adding saveList to addToDoItem
SITK-ToDoList adding saveList to clearCompletedToDoItems
SITK-ToDoList adding saveList to toggleToDoItemState
SITK-ToDoList adding saveList to emptyList

Let’s test it to see if it works!

Congratulations, we have done a great job! I know it is a lot to take in, but you will get used to it with time and practice.

Let’s use Git to commit our work. We taught how to do it in our previous post; check it out here.

If you don’t know what Git is, take a look here and find out. The SITK-ToDo List source code can be found on our GitHub account.

We’ve got more of the basics of front-end web development. Certainly, there is much more to learn about HTML, CSS and JavaScript, and there are some free and excellent references for this: w3schools.com and MDN Web Docs.

If you need any help or something is going wrong, let me know. It will be my pleasure to help you. If you want to extend some point, we have discussed, contact me so we can cover in the next posts.

We also have pro bono projects just in case you are interested in learning more about them.

Be aware of why we started this project by clicking here.

Learn more about other posts here.

Contact us for any suggestions. And follow us on FacebookInstagram and Twitter.

If you are a good reader like myself, I recommend the following readings:

  • Docker Quick Start Guide: Learn Docker like a boss, and finally own your applications
  • Docker for Developers: Develop and run your application with Docker containers using DevOps tools for continuous delivery
  • Responsive Web Design with HTML5 and CSS: Develop future-proof responsive websites using the latest HTML5 and CSS techniques, 3rd Edition
  • JavaScript: The Definitive Guide: Master the World’s Most-Used Programming Language 7th Edition

See you in the next post!