Search for:
SITK - Pie Shop 5of7 Featured image

Exciting Web Development Journey – Pie Shop Website 6/7

Overview

Welcome back to Sharing Is The Key. Now that you are already acquainted with JavaScript basic concepts, let’s push a little bit further and create some interactivity in the Pie Shop website.

In this post, we cover some of the building blocks you need to create interactive web apps. We’ll discuss how to write code that can respond when certain things happen in the browser, like when the page loads, when you click a button, and so on. We’ll also review a wide array of built‑in features and functionalities available to you through JavaScript as you build web apps. Finally, we cover how to determine browser support for different features and what to do if that feature isn’t yet supported.

Selecting Elements

One of the fundamental actions that you’ll use JavaScript for working with the web page is selecting elements on the page. Going back to our sandbox.html we dealt with in the third post.

As we look at the HTML, each project is made up of a section element with an id, which represents each specific project, and they both have the class of project.

So when I want to access one, specifically, I can use the id attribute, and when I want to access these elements together as a set, I can use the project class. Inside the project, there is a title, an image and a paragraph for each project.

Now to use JavaScript to access these elements, I can do several different ways, and one of the first is to use a function called getElementById.

If we look at the document just in and of itself, we could see that this object represents the web page or the document running within the browser.

SITK- Pie Shop - selecting elements - document

By calling document.getElementById and pass in an id value, what it returns is a reference to the section element because it has the id of project-1.

SITK- Pie Shop - selecting elements - document.getElementById()

Now that works great for accessing elements with a specific id, but what if I want to select items based on their class name? Well, I can do that very easily as well. Here, I can use document.querySelector. Now, what we pass into the querySelector function will seem very familiar to you. It will take the same type of selectors that we used in building CSS rules. So to access my project class, I type in .project.

SITK- Pie Shop - selecting elements - document.querySelector()

Now the querySelector function returns the first match of your selector. Here, you can see that it returned the section for project-1 because that was the first item that had the project class.

Now we can get all of them just as easily by calling document.querySelectorAll. When we pass in .project, what’s returned is a node list of all the matching elements.

SITK- Pie Shop - selecting elements - document.querySelectorAll()

The queries you can pass into the querySelector function can be straightforward like I’ve shown you or even a little more complex. Retaking a look at the HTML, I have a img element with a src attribute that has a value of https://via.placeholder.com/150. So we can take these same values and format them in a selector to use with querySelector.

SITK- Pie Shop - selecting elements - more complex document.querySelector()

Now there is one more thing I want you to notice as well, see how I used the double quotes on the outside of the selector and the single quotes on the inside. The quotes on the inside are necessary to tell the selector that we’re searching for a string value that the src will be equal to, but we can’t use the double quotes because that would signal to the browser that the selector was going to end early. Any time you need to use quotes inside of quotes, you can use the single quotes inside the double or use the double quotes inside the single. The important thing is you only have to be consistent.

Working with Elements

Now that we’re able to select elements on the page let’s see what we can do with them. One of the actions that you can take against an element is to change attributes in JavaScript.

So with this Sharing is the key! link here right now, if I click on it, it’ll open up in the same browser tab as my sandbox page. And if we look at the HTML, the anchor has an href attribute and a class attribute.

SITK- Pie Shop - working with elements - link

But there’s an attribute that we can add named target that allows us to designate the destination of the link location. So let’s go down to the script on the page and add a little bit of code.

SITK- Pie Shop - working with elements - setAttribute()

Now what I’m doing here is using document.querySelector to select the only anchor element on the page. Once I have access to it, I can call the setAttribute function and tell it that I want to add an attribute target and set its value to blank. All right, well, let’s save it and switch back over to our page and see how it behaves now. Now when I click on the link, it opens up in a new tab.

SITK- Pie Shop - working with elements - setAttribute(target, _blank)

The setAttribute function works to create an attribute that isn’t there yet or update an attribute’s value.

So here I’ll open up the developer tools, right‑click on the link, and select Inspect. When I come into the Elements pane, it automatically creates the $0 variable to select the element. And so I wouldn’t use $0 when I’m coding a web page. But here, I’d like to show you how you can do it for experimentation within the browser development tools. So now I’ll switch over to Console, and I’ll verify I have a value for $0.

SITK- Pie Shop - working with elements - $0 variable

So this variable is now pointing to this element. Now from there, what I can do is I can ask for the value of a specific attribute. By calling getAttribute and passing in the name of the attribute that I want to look at, I get the value of our new target attribute.

SITK- Pie Shop - working with elements - $0.getAttribute(target)

I could also ask for the href value. And I could also remove an attribute. Passing in the attribute name removes the attribute from the element.

SITK- Pie Shop - working with elements - $0.removeAttribute(target)

Now you can see that the HTML has been updated so that the target element is not there.

SITK- Pie Shop - working with elements - $0.removeAttribute(target) - result

So now when I click on the link, instead of this opening up in a new tab, it opens up in the same tab, all because we’ve removed that attribute.

Now there’s one attribute that acts a little bit different than the others, and that’s the class attribute. So in the next section, I’d like to show you how to work with the classList HTML attribute.

classList Property

Selecting elements is the first step, but once you have access to those elements, it opens up all kinds of possibilities for you to do something with them. Here we have our page that has our projects on it, and each section has a class list with project and desktop. Now, we’ll run some code that will update the classes that are applied to the project.

SITK- Pie Shop - classList Property - class list values

But first, let’s go to the CSS style sheet and add this rule for the dark class to make things easier to understand.

SITK- Pie Shop - classList Property - dark rule

I want to locate an element with the class project and desktop and add dark to the class list. When we take a look at the web page with the dark class applied, we could see that the project-2 is rendered with a dark background.

SITK- Pie Shop - classList Property - dark class applied to project-2

Now, what I just did was to add it directly here into the HTML. And so now I’d like to show you how to do the same thing using JavaScript. So we’re back to the web page’s default state, so I can use document.querySelector to get access to the element with the project class. When we look at the project variable, we can see that the first project is selected.

SITK- Pie Shop - classList Property - using querySelector

Now, from there, I can change the classes that are associated with this element. You might think that I could go to project.class and set that equal to something, but that does not work in JavaScript. The word class is a reserved word. And so here, what we’ll use is a class list. By accessing the class list, I can call the add function and pass in a class name. Here, I don’t add a dot before the name. Because this isn’t a selector, I’m passing in a name. And so now, as I press Enter, the list of classes associated with that element now includes dark, and in fact, we can look at that list here and see it returns both project, desktop and dark.

SITK- Pie Shop - classList Property - adding dark to the project class list

Now, what if I want to remove it? Well, I can call project.classList and run the remove function. And so now, when we look at the class list, there are only project and desktop.

SITK- Pie Shop - classList Property - removing dark from the project class list

Script Tag

One of the elements that you’ve seen me using a lot but we haven’t discussed yet is the script element in depth. Now the script tag is the element that we use on a page to execute scripts. The location of the script tag is essential. Let’s think about how a website works. When you send a page to the browser, the code is processed from top to bottom. Remember, HTML and CSS are responsible for the structure and layout of the page, while the scripts are responsible for its behaviour. I bring all this up because it’s for these reasons we want the script tag near the bottom of the page.

Often it shows up as the last element before the body’s closing tag. It ensures that our pages aren’t halted from loading and being styled due to a script that didn’t work quite right. So as you’ve seen throughout this post, I’ve added JavaScript to the script element.

There are times, probably more often than not, when you’ll want to move the script into an external file. We move JavaScript into its file for a lot of different reasons:

  • One is that the scripts are easier to write and maintain when they’re in dedicated files. You can load as many script files as you want into a page, and by splitting up the code and the logic, just makes your applications easier to work with.
  • The second main reason is that browsers make copies of files that they request from the server. We call it caching. When a file is cached, the browser loads its file from a saved copy rather than making a new request to the server. Now, this speeds up the web a lot. So if you have a script file that many pages of your site use, then adding it as an external file makes a lot of sense so you can take advantage of browser caching.

There are other excellent reasons to use external scripts, but these are the two main ones that are relevant right now. So I have an external script provided for us, and just so that you can see the location, I created sandbox.js right next to sandbox.html and moved the code from the script tag in the sandbox.html page into the sandbox.js file.

SITK- Pie Shop - script tag - moving javascript code to a dedicated file

Now to reference it, the script tag will have a source attribute that points to sandbox.js. And then, I need to add a type attribute and set that equal to text/javascript. This attribute is important because it tells the browser what language the script is coming in as. The only client‑side scripting language we use is JavaScript, but it’s added here just for completeness with modern web development. Now some tags within an HTML page are self‑closing. So if you notice, the meta tag up here doesn’t have an associated closing tag. The script tag does need the closing tag, though. Even though we don’t put anything in between the script tag, we still have to close it here explicitly. When we save the file, the page reloads, and we get the Sharing is the key! link working the same way as before.

SITK- Pie Shop - script tag - linking javascript to html

Next, let’s take a look at how the window object is used inside of the browser and through JavaScript.

Window

The Window object is the context for scripts running within a web browser, and we’ll use Sharing is the key website to illustrate.

SITK- Pie Shop - window - sitk website

So the window generally refers to the window that’s running inside a current tab. So if I open up the About page in a separate tab, there are two different Window objects where your script will run, even though they’re technically within the same website. They’re independent pages, so they get a different context.

SITK- Pie Shop - window - sitk website - about page

The window hosts the document object, which is the DOM for this tab. So if we take a look at the window, you can see scores of functions and properties available off the window. And much of the functionality of the browser is available off of the window object.

SITK- Pie Shop - window - sitk website - window object in the about page

Now, from here, we have access to things like the location of the browser tab. We can get all kinds of information about the URL of the page and even the different segments that make up this location. So we could do an entire course on the window object inside the browser. But for now, what you need to know is that we’ll use the window object to get access to the built‑in functionality of the browser.

SITK- Pie Shop - window - sitk website - window.location info

Debugging

If you use nothing else inside the browser developer tools, you will use the debugger. The debugger allows you to stop code from running at a specific point so you can inspect what’s happening right at that line of code.

Now, I’ve written a simple script here, and if you haven’t already noticed, I’ve introduced a bug into my code. So I’m concatenating together my first name and last name, but here, when I go to use them together, I use the variable called firstName instead of first.

SITK- Pie Shop - debugging - known error

So, if we save the file, you’ll notice that I get a reference error, and it says firstName is not defined. Now, this is a straightforward script. We can see the error very quickly.

SITK- Pie Shop - debugging - save and get an error

But let’s imagine for a moment that this bug is hard to find. Within the browser developer tools, I can go over to the Sources tab, and when I click on the sandbox.js file, it shows me right where I have an error for this line of code. Now, if I come down to the line numbers and I click on line 2, you’ll notice that it’s highlighted with this blue arrow. It is called a breakpoint, and you can see in the window on the right that it shows the list of breakpoints.

SITK- Pie Shop - debugging - set a breakpoint in the source tab

A breakpoint is a point at which the execution will break, or essentially stop, and allow you to examine what’s going on in the code. So now, with that breakpoint set, I can refresh the page and notice what happened. It highlighted the line, and it shows up at the top that it’s paused in the debugger.

SITK- Pie Shop - debugging - execution stopped at the breakpoint

Now, as I look around on my page, I can hover over variables, and I can see at the variable named first has a value of "John". The variable of last is not yet declared because we haven’t executed this line yet.

So, I’ll click on Step over the next function call button, or you can press F10, or CTRL+apostrophe, either one of those will work, and now the next line of code has been run.

SITK- Pie Shop - debugging - step over the next function call

So the variable named last now has a value of "Smith". But you’ll notice it didn’t keep going. It stopped at the following line of code. So at this point, I can hover over last and see that I have a value. And then I see firstName. Well, that doesn’t have value. And so, by stopping the code as it’s executing, I have a much better chance of figuring out what my problems are.

So, here we’ve realized, firstName is not the variable that we need. What we need is to call that first. So now I can switch over to the editor. Make the change. Remember, the execution is still stopped on line 3, so I’ll let it run by clicking on the paused in debugger button at the top of the page and let’s refresh it, and now it hits the breakpoint again.

SITK- Pie Shop - debugging - debugging again with the code fixed

So first has a value of "John", step over the following line, last has a value of "Smith", and first and last now have values. I fixed my bug, and now the name variable has "JohnSmith" as its final value. On the right-hand side, you’ll notice over here that it shows the current script that’s running and all the variables that are currently in scope and their values.

SITK- Pie Shop - debugging - checking the final result

By setting breakpoints and using the browser’s debugging tools, you have a lot of power available to you to debug your applications. Now that we have a good view of what it takes to write and debug code let’s start writing some code against the browser and find out how to work with HTML elements on the page.

Events

Events, in programming languages, just like in real life, signal when something happens. When an event occurs in a program, the application sends a notification to the rest of the code, essentially saying, hey, something happened here. You get to select which messages you want to respond to this event. Now, for instance, there’s an event that fires when the page loads. You can also listen for an event that fires as a button is clicked. You can even tap into an event that lets you know when a change in the orientation of your mobile device has occurred. And these are just a few, among many, events that you can use in your app. In JavaScript, you set up your code to listen for events. Let’s take a look at the function that allows us to respond to these events, and that function is called addEventListener.

addEventListener Function

AddEventListener is the function you use to run some code when a given event takes place. The syntax for this function is made up of a few different parts. Here addEventListener is being called off the window object. But now we need to tell the browser which event we’re interested in. The name of the event is added first between quotes here because the name is a stream. Now there are dozens of events to choose from, and in the coming examples, I’ll show you how to use a few.

SITK- Pie Shop - addEventListener - first argument

The second argument of the function expects a function. It is similar to how the forEach function worked when we spoke about arrays, where the function defined here is called when the event notification is sent.

SITK- Pie Shop - addEventListener - second argument

This is an anonymous function, and it is being called gets a reference to an object that contains information about the event that was just raised. Often you’ll need to access different information to find out more about what’s happening in the program, and that will be available within the event object.

SITK- Pie Shop - addEventListener - second argument - event object

Inside the function is where you write the code that’s meant to respond to the event.

SITK- Pie Shop - addEventListener - second argument - logic content

Okay, let’s look at some actual implementations and how you can respond to a few different events in JavaScript.

Demo: Events

Here we are back with our sandbox page, and one of the first events that I’d like to look into is to know whether or not the page is completely loaded within the browser. This is important because our page is represented in the browser through the Document Object Model.

SITK- Pie Shop - demo events - sandbox page

Now, let’s recall our discussion from cascading style sheets, where we talked about the Document Object Model, or the DOM. In that case, it’s an in‑memory hierarchical representation of the elements that make up a web page. When the browser requests a page, the server’s response includes HTML that’s read by the browser. Based on the HTML structure, elements are created in‑memory that correspond to the markup in the page.

SITK- Pie Shop - demo events - abstract DOM representation

Now load times can vary depending on many factors. Sometimes web pages load so quickly that it seems instantaneous to our eyes. Still, there’s always a certain amount of time that elapses before a web page is completely loaded and rendered inside the browser to a computer. Since there’s this delay in time, even if it’s slight, the code we write needs a way to know that, indeed, that page is loaded before attempting to run.

The event that tells us that the page is loaded and ready to work is called DOMContentLoaded. So the first thing that we want to do is go to the window object and call addEventListener for DOMContentLoaded. Then we’ll create an anonymous function that’s called when the event fires. Within the body of this function, we can add in our logic that runs as the page loads. So we’ll log out to the console saying that the page is loaded.

SITK- Pie Shop - demo events - DOMContentLoaded

When we save the page, it’s reloaded, and only until all of the page contents are loaded into memory. Each of the HTML elements are processed in the DOM. The DOMContentLoaded event is fired. We get the console to report that the page is loaded. Our page is elementary, so it goes fast, but this is the place where you want to begin writing code if you’re going to use any elements on your HTML page.

SITK- Pie Shop - demo events - DOMContentLoaded - result

If we wanted to interact with a button on the page, we could do that simply here within this event. I’ll start by creating a new element for a button that we can click on. I’ll assign the button with the id of click‑me and the text of Click Me.

SITK- Pie Shop - demo events - creating a button element

So now, down at DOMContentLoaded, I can add in the logic to interact with my element. The first thing that I want to do is select the element on the page. Here, I’ve used the querySelector function to pass in an id selector to find that element on the page. Next, we’ll add an event listener on the button to listen for the click event. And with that, I can log out to the console that the button was clicked.

SITK- Pie Shop - demo events - adding code to the event listener function

So, as I save the page and reloads, I get a button here rendered on the page. So when I click on that, I get a message logged to the console, saying, “The button was clicked.”.

SITK- Pie Shop - demo events - adding code to the event listener function - result

And lastly, I’ll show you how to handle the event when the device orientation changes within the browser. It is an event that only fires for mobile devices, so I’ll show you how we can simulate it within the desktop browser. First, we’ll add an eventListener for orientationChange off the window object. Now we can look at the screen object to get orientation information about the device. We can get the angle and the orientation type, and we can take that information and log it out to the browser. So as I save the changes, when we look over in the console, you don’t see anything show up.

SITK- Pie Shop - demo events - adding listener to orientationchange event

Remember that we have the mobile emulator available within the browser developer tools, and I can switch over to that. You’ll notice that as I switched over to the mobile view, it fired this event. Expanding the window out to make it a little bit easier to see, if I click on this button for Rotate, you’ll notice that as the event fires, the type goes from portrait primary to landscape primary 0 degrees to 90 degrees.

Now, there is a wide array of available events as you build your web apps, and we’ll be using a number of them as we finish up the implementation for Bethany’s Pie Shop in the next post. But next, let’s get a feel for what type of APIs are available directly within the browser.

Native API

API stands for Application Programming Interface. Now that’s just a fancy way of saying that there’s some code that you can use to work directly with the browser’s functionality. Putting it another way, using the browser’s API, you can control the features and the behaviour of a web browser. The best way to get an overall look at what APIs are available is to go over to the MDN web docs, and we’ll bring up the web API page that lists all the built‑in functionality of a web browser.

SITK- Pie Shop - native apis - MDN web docs

Now, by looking around on this page, you’ll be able to see that there’s a vast array of functionality that you can tap into. Want to draw something on the page?

SITK- Pie Shop - native apis - canvas

Want to request an additional file from the server? Fetch does that.

SITK- Pie Shop - native apis - fetch

Want to create drag‑and‑drop interaction on your page? Well, it is called HTML Drag and Drop API.

SITK- Pie Shop - native apis - HTML Drag and Drop

What about saving data in the database? The tragically named IndexedDB can do that for you.

SITK- Pie Shop - native apis - IndexedDB

And the list goes on and on. But there’s one problem. Not all of these APIs are available in all browsers. How do you know what you can use and whether or not there are alternatives if something you need isn’t available? Well, I’ll tell you right now, options do exist, and we’ll discuss those next.

Fallbacks and Polyfills

Sometimes the API you want to use isn’t fully supported by browsers, and we’ll look at how to determine browser support next. But for now, I want to introduce a few terms regarding API support. In situations where API support is inconsistent, there are two types of scripts that you can include on your pages that help compensate for the missing feature.

SITK- Pie Shop - api absense - fallback

A fallback is a script that your browser can fall back to if the built‑in functionality doesn’t exist. The fallback often doesn’t do as much as what the native API is supposed to do, but they exist as an alternative to built‑in browser functionality.

SITK- Pie Shop - api absense - polyfill

On the other hand, a polyfill is meant to mimic built‑in browser functionality by already using supported features. The main benefit here is that a polyfill is specifically built to have the functions, objects, properties, and behaviour that work the same way the built‑in browser functionality is supposed to work.

This way, once browsers fully support the API, you should remove the polyfill, and everything should work. Now that’s the promise. But be warned, you should still do extensive testing before you make changes like that.

Now that you’re familiar with fallbacks and polyfills let’s look at a few tools that can help you determine whether an API is supported well enough for your application.

Browser Support

Determining browser support is crucial because you need to know that the code you write will work with the web browsers accessing your site. Now, there are a couple of tools that you can use to help make these decisions. The first one is HTML5 Please. This website lists different native APIs and gives you some hints on how well they’re supported and what to do if they’re not.

SITK- Pie Shop - browser support - html5 please

In conjunction, caniuse.com is an excellent resource that brings you a broad spectrum of statistics regarding how well APIs are supported on different browsers. You saw caniuse.com a little bit when we talked about CSS, and we’ll use it a bit more here.

SITK- Pie Shop - browser support - caniuse

So we’ll take a look at a couple of different APIs here within HTML5 Please so you can get an idea of what this website offers. First, let’s take a look at the API of local storage. This is a built‑in API that allows you to store information within a web browser. You’ll notice that HTML5 Please has marked localStorage as use. So this is a fully supported API, meaning that you can use it with high confidence in just about any web application.

SITK- Pie Shop - browser support - html5 please - localStorage

As we look at the bottom, you’ll notice a link down here that says View browser share %. When we click on that, that brings us to caniuse.com directly to the section on localStorage. Here we get an idea of browser support and any extra resources that are available for this API.

SITK- Pie Shop - browser support - caniuse - localStorage

Now, localStorage is an extensively well‑supported API, so let’s look at something a little different. Currently, Web Workers are marked as used but with a fallback. So the advice here is that there may be a small percentage of web browsers that don’t support the API, and it gives you some ideas of how to work around that if you need to have 100% browser capability.

SITK- Pie Shop - browser support - html5 please - web workers

All right, let’s look at IndexedDB. This API is marked as caution with a fallback. So here it gives you some recommended polyfills, IDBWrapper and the IndexedDB Polyfill.

SITK- Pie Shop - browser support - html5 please - indexedDB

When we go over to caniuse.com for IndexedDB, scrolling down to the Resources tab, you’ll notice that it has links for polyfills and detailed information about places where there might be some deficiencies in the implementation. But you’ll see from the chart here, almost every modern web browser supports IndexedDB.

SITK- Pie Shop - browser support - caniuse - indexedDB

All right, let’s look at one last example. The WebSQL API is marked as to avoid. It is a deprecated API, and you don’t want to use this API unless you have a particular reason. And the advantage here is that the website gives us all of that knowledge ahead of time without having you figure that out the hard way.

SITK- Pie Shop - browser support - html5 please - WebSQL

Next up, we have the last post in this series where I’ll demonstrate to you how to update the rest of Bethany’s website to collect user data and send it to the server. And this is where it gets exciting because now we get to put into practice all the knowledge that you’ve learned about JavaScript to update the website to make it truly interactive.

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 fllowing 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!

    ToDoList Blog banner

    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!

    %d bloggers like this: