Beginning Modern Javascript a Step by Step Gentle Guide to Learn
Beginning Modern Javascript a Step by Step Gentle Guide to Learn
Preface
Working Through This Book
Requirements
Contact
Chapter 1: Introduction
What is JavaScript?
Why Learn JavaScript?
Computer Setup
Your First JavaScript Application
Summary
Chapter 2: JavaScript Variables
Variable Naming
Constant Variables
Summary
Chapter 3: Basic Data Types
Strings in JavaScript
Numbers (Integers and Floats) in JavaScript
Booleans in JavaScript
Undefined
Null
Type Conversion
Type Coercion
Type Coercion Rules
Summary
Chapter 4: JavaScript Operators
Arithmetic Operators
The Assignment Operator
The Comparison Operators
Logical Operators
The typeof Operator
Summary
Chapter 5: Control Flows
Conditional Flow
Loop Flow
Summary
Chapter 6: Functions
How to Create Your Own Function
Function Parameters and Arguments
Default Parameters
Default Parameters and Null
The return Statement
Variable Scope
The Rest Parameter
Arrow Function Syntax
Single and Multi-line Arrow Functions
Arrow function Without Round Brackets
Convert a Normal Function to an Arrow Function Easily
Summary
Chapter 7: Objects in JavaScript
Why a function is called a method when inside an
object?
How to access object values
How to add a new property to the object
How to modify object properties
How to delete object properties
How to check if a property exists in an object
Summary
Chapter 8 - Arrays
Array Index Position
Array Methods and Properties
Summary
Chapter 9 - The Document Object Model (DOM) Introduction
The DOM Explained
The Window Object
The Document Object
Summary
Chapter 10: Project 1 - Creating a Wizard Form Application
Project Setup
Putting the HTML and CSS First
Summary
Chapter 11: The Wizard Form Mechanism
DOM Query Selector Methods
querySelectorAll() Method
Hide Form Steps With CSS
Adding the Next Button
Adding the Previous Button
Chapter 12: Handling Wizard Form Submission
Adding a Submit Button
Listening to Form Submit Events Using JavaScript
Taking Care of Repeating Code
Summary
Chapter 13: Asynchronous JavaScript and Callbacks
Callback Functions Explained
Summary
Chapter 14: Promises
Callbacks vs Promises
Summary
Chapter 15: The Fetch API
What is an API?
How Fetch API works
Summary
Chapter 16: Project 2 - Creating a To-do List Application
Installing Node.js
Creating the Server and Database
Let’s Start the Project: HTML and CSS First
Summary
Chapter 17: Adding CRUD Functionalities to the Application
Use Fetch to Get and Display Tasks from the API
Adding a New Task
Deleting a Task
Editing a Task
Marking a Task as Completed
Filtering Tasks
Summary
Wrapping Up
The Next Step
About the author
Beginning Modern JavaScript
A Step-By-Step Gentle Guide to Learn JavaScript for Beginners
By Nathan Sebhastian
PREFACE
Requirements
No previous JavaScript programming knowledge is required as
I will introduce JavaScript from the basics, but you should be
somewhat familiar with HTML and CSS to make the most of this
book.
Contact
If you need help, you can contact me at
[email protected].
The email course would help you find the right path forward as
a software developer.
CHAPTER 1: INTRODUCTION
What is JavaScript?
JavaScript was created around April 1995 by a developer
named Brendan Eich. At the time, he was working to develop a
browser for a company called Netscape. He was told that he
only had 10 days to design and code a working prototype of a
programming language that could run on the browser.
1. A web browser
2. A code editor
https://www.google.com/chrome/
Now that you have a code editor installed, the next step is to
create your first JavaScript application.
Next, open the Visual Studio Code, and select File > Open
Folder… from the menu bar. Select the folder you’ve just
created earlier.
VSCode will load the folder and display the content in the
Explorer sidebar, it should be empty as we haven’t created any
files yet.
The next step is to put some content in the HTML file. You can
write the content below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="script.js" defer></script>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
Notice that the <script> tag above contains the defer attribute.
This attribute is added to ensure that the script will be
processed after the HTML document has been loaded by the
browser.
Inside the body tag, we just add a heading <h1> tag with the
'Hello World!' text.
Save the changes to the file, and the next step is to run this file
using a local server.
We’re going to use the Live Server extension to run our HTML
file. To install extensions, click the Extension icons that look like
a puzzle of blocks or press Ctrl+Shift+X or Command+Shift+X.
You should see the extension sidebar. At the top of the sidebar,
type in 'live server' and select the one that’s created by Ritwick
Dey. See the picture below:
The Live Server should start a local server and open your
browser automatically as follows:
Figure 3. Live Server Running
console.log('Learning JavaScript!');
Writing Comments
In programming, comments are text we use to communicate the
context of the code written in the file.
// This is a comment
// This is also a comment
console.log("Hello World!");
// console.log("I'm learning JavaScript");
Summary
In this chapter, we’ve explored the origins of JavaScript,
installed the required tools to code in JavaScript, and run our
first JavaScript application.
We’ve also learned how to write comments in our JavaScript
file. In the next chapter, we’re going to learn about variables.
CHAPTER 2: JAVASCRIPT
VARIABLES
Go back to the browser console, and you’ll see the same output
as when you write the 'Learning JavaScript!' message directly
inside the console.log() function. How can this be?
The first line in the code tells JavaScript to associate the message
variable with the value 'Learning JavaScript!':
Run the file and you’ll see two lines printed as the output:
Learning JavaScript!
Nice weather!
Variables are used to reference data so that you can use the
same data multiple times in your program.
Variable Naming
JavaScript has a few naming rules that you need to know to
avoid naming errors.
Variable names can only contain alphabet letters, numbers, and
underscores (_). This means you can name your variable
message, message_1, message_2.
1. camelCase
2. snake_case
let myAwesomeVariable
The snake case naming convention uses an underscore to
separate words. Here’s an example:
let my_awesome_variable
Constant Variables
There are times when you need to store a value that must not
be changed in a variable.
But before you learn more about how to make use of variables,
let’s learn about data types in JavaScript.
CHAPTER 3: BASIC DATA TYPES
console.log(2 + 2);
// Output: 4
console.log(2 + "ABC");
Output:
2ABC
Adding a number to letters will cause JavaScript to concatenate
or join the values together. Adding a number to letters is not the
same as adding a number to another number, and JavaScript
looks at the data type to know which operation to perform on
the data.
▪ Strings
▪ Numbers
▪ Booleans
▪ Null
▪ Undefined
You will also see how these different types react to operators
like the + operator shown above.
Strings in JavaScript
Strings are simply data defined as a series of characters.
You’ll get an error when you use different quotation marks like
this:
You can join two or more strings as one with the plus + symbol.
Don’t forget to add a space before the next string or you’ll get a
string without spaces!
To use the template strings format, you need to use the backtick
(`) character to wrap the string instead of quotations.
// Output: 1030
▪ Integers
▪ Floats
let x = 1;
let y = 2;
console.log(x + y);
// Output: 3
let f = 1.2;
let z = 2.35;
console.log(f + z);
// Output: 3.55
Booleans in JavaScript
Boolean is a type that represents true and false values.
let on = true;
let off = false;
Undefined
Undefined is a data type in JavaScript used to represent a
variable that hasn’t been assigned any value yet.
console.log(first_name); // undefined
Null
The null value is a special data type that represents an empty or
unknown value. Here’s how you assign a variable as null:
And you are correct. Both undefined and null are values that
represent nothing, and other programming languages usually
only have one, and that is null.
Type Conversion
At times, you might want to convert one data type into another
so that the program runs as expected.
let x = "7";
let y = 5;
console.log(x + y); // 75
▪ Number()
▪ String()
▪ Boolean()
As their name implies, these type conversion functions will
attempt to convert any value you specified inside the
parentheses to the type of the same name.
let x = "7";
let y = 5;
// Convert x to integer
x = Number(x);
console.log(x + y); // 12
Type Coercion
In JavaScript, type coercion is a process where a value of one
type is implicitly converted into another type.
console.log(1 + "1");
As you’ve seen in the previous section, JavaScript will consider
the number as a string and join the two letters as 11 instead of
adding them (1 + 1 = 2)
But JavaScript will see this and say: "I cannot do the operation
you requested as it is, but I can do it if the number 1 is
converted to a string, so I’ll do just that."
And that’s exactly what type coercion is. JavaScript notices that
it doesn’t know how to execute your code, but it doesn’t stop the
program and responds with an error.
Instead, it will change the data type of one of the values without
telling you.
But the order of the values matters when you have an object.
Writing objects first always returns numeric 1:
{ a: 1 } + "1" // 1
"1" + { a: 1 } // "1[object Object]"
true + { a: 1 } // "true[object Object]"
{ a: 1 } + 1 // 1
true + 1 // 1+1 = 1
false + 1 // 0+1 = 1
[1,2] + 1 // "1,21"
let myNumber = 1;
console.log(myNumber + "1"); // prints 11
console.log(myNumber); // still prints number 1 and not string
You can try to find some more on your own, but you hopefully
understand what type coercion is and how it works by now.
If you ask me, I would recommend that you avoid using type
coercion in your code all the time.
console.log(totalPrice);
Summary
JavaScript provides five basic data types that you can use in
creating a useful program. Each data type has specific traits that
define what you can and can’t do with the data.
Arithmetic Operators
The arithmetic operators are used to perform mathematical
operations like additions and subtractions.
console.log(10 - 3); // 7
console.log(2 + 4); // 6
// 1. Addition (+)
// Returns the sum between the two operands
console.log(5 + 2); // 7
// 2. Subtraction (-)
// Returns the difference between the two operands
console.log(5 - 2); // 3
// 5. Division operator
// Returns the sum between the two operands
console.log(5 / 2); // 4
// 6. Remainder operator
// Returns the remainder of the left operand after being divided by the right
operand
console.log(5 % 2); // 1
You may’ve noticed that the equals sign has a slightly different
meaning in programming than in math, and you’re correct!
let x = 2
let y = 1
// Assign y to x
x = y
console.log(x); // 1
let x = 2
let y = 1
x += y
console.log(x); // 3
let x = 2
let y = 1
x -= y
console.log(x); // 1
let x = 2
let y = 2
x *= y
console.log(x); // 4
let x = 5
let y = 2
x /= y
console.log(x); // 2.5
let x = 10
let y = 3
x %= y
console.log(x); // 1
operand
You should use the strict comparison operators unless you have
a specific reason not to.
Logical Operators
The logical operators are used to check whether one or more
expressions result in either true or false.
console.log(false || false);
console.log(!true);
let x = 5;
console.log(typeof x) // 'number'
The typeof operator returns the type of the data as a string. The
'number' type represents both integer and float types, the
'string' and 'boolean' represent their respective types.
Summary
In this chapter, we’ve learned about the operators available in
JavaScript. Of all these operators, only a select few are used
frequently, while the rest will be used only in specific
conditions.
1. if…else statement
2. switch…case statement
if (condition) {
// code to execute if condition is true
}
After the if statement, you can write another line of code below
it as follows:
Next, suppose you need to run some code only when the if
statement condition is not fulfilled.
This is where the else statement comes in. See the following
example:
Now change the value of balance to be less than 5000, and you’ll
trigger the else block in the example.
case "apple":
Keep in mind the data type of the case value that you want to
match with the expression. If you want to match a string, then
you need to put a string. switch statements won’t perform type
coercion when you have a number as the argument but put a
string for the case:
switch (1) {
case "1":
console.log("Hello World!");
break;
}
The same also happens for boolean values. The number 1 won’t
be coerced as true and the number 0 won’t be coerced as false:
switch (0) {
case true:
console.log("Hello True!");
break;
case false:
console.log("Bonjour False!");
break;
default:
console.log("No matching case");
}
The switch Statement Body
The switch statement body is composed of three keywords:
switch (0) {
case 1:
console.log("Value is one");
case 0:
console.log("Value is zero");
default:
console.log("No matching case");
}
When you execute the code above, JavaScript will print the
following log:
Your switch evaluation may match more than one case, so the
break keyword is commonly used to exit the process once a
match is found.
switch (20) {
case 10 + 10:
console.log("value is twenty");
break;
}
But you need to keep in mind that the value for a case block
must exactly match the switch argument.
let age = 5;
switch (age) {
case age < 10:
console.log("Value is less than ten");
break;
case age < 20:
console.log("Value is less than twenty");
break;
default:
console.log("Value is twenty or more");
}
You need to remember the differences between the if and case
evaluations:
let weekdayNumber = 1;
if (weekdayNumber === 0) {
console.log("Sunday");
} else if (weekdayNumber === 1) {
console.log("Monday");
} else if (weekdayNumber === 2) {
console.log("Tuesday");
} else if (weekdayNumber === 3) {
console.log("Wednesday");
} else if (weekdayNumber === 4) {
console.log("Thursday");
} else if (weekdayNumber === 5) {
console.log("Friday");
} else if (weekdayNumber === 6) {
console.log("Saturday");
} else {
console.log("The weekday number is invalid");
}
I don’t know about you, but the code above sure looks
cumbersome to me! Although there’s nothing wrong with the
code above, you can make it prettier with switch:
let weekdayNumber = 1;
switch (weekdayNumber) {
case 0:
console.log("Sunday");
break;
case 1:
console.log("Monday");
break;
case 2:
console.log("Tuesday");
break;
case 3:
console.log("Wednesday");
break;
case 4:
console.log("Thursday");
break;
case 5:
console.log("Friday");
break;
case 6:
console.log("Saturday");
break;
default:
console.log("The weekday number is invalid");
}
Loop Flow
As you program an application in JavaScript, you’ll often need
to write a piece of code that needs to be executed repeatedly.
Let’s say you want to write a program that prints the numbers 1
to 10 in the console. You can do it by calling console.log 10
times like this:
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
// and so on..
let heads = 0;
let tails = 0;
for (x = 1; x <= 10; x++) {
if (Math.random() < 0.5) {
tails++;
} else {
heads++;
}
}
The example above shows where the for loop offers the most
effective approach.
Now let’s see an alternative exercise about coin flips where the
for loop is not effective:
Find out how many times you need to flip a coin until it
lands on heads.
This time, you don’t know how many times you need to flip the
coin. This is where you need to use the while loop statement,
which you’re going to learn next.
The while Statement
The while statement or while loop is used to run a block of code
as long as the condition evaluates to true.
You can define the condition and the statement for the loop as
follows:
while (condition) {
statement;
}
Just like the for loop, the while loop is used to execute a piece of
code over and over again until it reaches the desired condition.
let i = 0;
while (i < 6) {
console.log(`The value of i = ${i}`);
i++;
}
Here, the while loop will repeatedly print the value of i as long
as i is less than 6. In each iteration, the value of i is
incremented by 1 until it reaches 6 and the loop terminates.
let i = 0;
while (i < 6) {
console.log(`The value of i = ${i}`);
}
An easy way to know when you should use while is when you
don’t know how many times you need to execute the code.
Back to the coin toss example, there’s one case that’s perfect for
a while loop:
Find out how many times you need to flip a coin until it
lands on heads.
You also need to show how many times you flip the coin until
it lands on heads:
let flips = 0;
let isHeads = false;
while (!isHeads) {
flips++;
isHeads = Math.random() < 0.5;
}
Because you can’t know how many times you need to loop until
you get the number 8, you need to use a while loop instead of a
for loop.
Summary
In this chapter, you’ve learned about the two types of control
flow available in JavaScript: the conditional flow and the loop
flow.
The if and for statements are usually preferred over the switch
and while statements. You can think of switch and while as an
alternative syntax that you use when if and for isn’t suitable.
CHAPTER 6: FUNCTIONS
You’ll learn more about objects and methods in the chapter. For
now, just know that a function and a method are essentially the
same, except that a method is called from an object.
Here’s an example:
function greet() {
// function body here
console.log("Hello!");
}
greet(); // Hello!
The code inside the function is executed when you call that
function.
function greet(name) {
// function body
}
function greet(name) {
console.log(`Hello, ${name}!`);
console.log("Nice weather today, right?");
}
Now whenever you need to call the greet() function, you need
to pass an input to fill in the name parameter.
greet("Peter");
Hello, Peter!
Nice weather today, right?
You can have more than one parameter when defining the
function, but you need to split each parameter with a comma as
follows:
greet("Nathan", "rainy");
Output:
Hello, Nathan!
It's rainy today, right?
Default Parameters
When defining a function, you can set a default value for any
parameter in that function.
greet();
greet("Jack");
Output:
Hello, Nathan!
Nice weather today, right?
Hello, Jack!
Nice weather today, right?
Any function you define can have a mix of default and non-
default parameters.
greet("sunny");
Output:
Hello, Nathan!
It's sunny today, right?
Notice that the weather parameter was placed in front of the
name parameter. This is for convenience so that you don’t need
to specify the default parameter.
greet(undefined, "sunny");
But when you pass null to the function, the default parameter
will be ignored:
greet(null); // null
When you use undefined, then JavaScript will replace it with the
default parameter. You might encounter this issue as you work
with JavaScript code in your career, so just keep this in mind.
function sum(a, b) {
return a + b;
}
function checkAge(age) {
if (age > 18) {
return "You may get a car license";
}
return "You may not get a car license yet";
}
console.log(checkAge(20));
console.log(checkAge(15));
Output:
When we call the checkAge() function the first time, the value of
age argument is greater than 18, so JavaScript executes the
return statement inside the if block.
function greet() {
console.log("Hello!");
return;
console.log("Good bye!");
}
greet()
Output:
Hello!
Variable Scope
Now that you’re learning about functions, it’s a good time to
talk about variable scope.
These two scopes are important because when you try to access
a local variable outside of its scope, you’ll get an error. For
example:
function greet() {
let myString = "Hello World!";
}
greet();
console.log(myString);
function greet() {
console.log(myString);
}
Next, you can also define a local variable with the same name
as the global variable without overwriting it.
Here’s an example:
function greet() {
let myString = "Morning!";
console.log(myString);
}
greet(); // Morning!
console.log(myString); // Hello World!
function printArguments(...args){
console.log(args);
}
function printArguments(...args){
console.log(args);
}
Keep in mind that a function can only have one rest parameter,
and the rest parameter must be the last parameter in the
function.
You can use a rest parameter when your function needs to work
with an indefinite number of arguments.
Arrow Function Syntax
The arrow function syntax allows you to write a JavaScript
function with a shorter, more concise syntax.
function greetings(name) {
console.log(`Hello, ${name}!`);
}
The arrow function syntax doesn’t add any new ability to the
JavaScript language.
But as you start using the arrow syntax, you will see that it’s
very convenient and easier to write.
function plusTwo(num) {
return num + 2;
}
Using the arrow function, you can omit both the curly brackets
and the return keyword, creating a single line function as
shown below:
When using the arrow function syntax, the curly brackets are
required only when you have a multiline function body:
When you use the function keyword, the round brackets are
always required:
function plusThree(num) {
return num + 3;
}
On the other hand, the arrow function allows you to omit the
round brackets when you have exactly one parameter for the
function:
As you can see, you can remove the round and curly brackets as
well as the return keyword.
But you still need the round brackets for two conditions:
The same applies when you have more than one parameter.
The arrow syntax makes the round brackets optional when you
have a single parameter function.
function plusTwo(num) {
return num + 2;
}
The three steps above are enough to convert any old JavaScript
function syntax to the new arrow function syntax.
// from this
const plusTwo = num => {
return num + 2;
};
// to this
const plusTwo = num => num + 2;
When you have exactly one parameter, you can also remove the
round brackets:
// from this
const plusTwo = (num) => num + 2;
// to this
const plusTwo = num => num + 2;
But the last two steps are optional. Only the first three steps are
required to convert any JavaScript function and use the arrow
function syntax.
Summary
In this chapter, you’ve learned how to declare and execute
functions in JavaScript.
You’ve also learned about the two syntaxes that you can use to
declare a function: The function keyword and the arrow
function syntax.
At first, the arrow function syntax might look weird, and you
might prefer using the function keyword. It’s totally fine, as I
also feel that way in the beginning.
An object is declared using the curly brackets {}, and each item
inside the brackets is written in the key:value format.
When you need to add more than one item to an object, you
need to separate each item using a comma. To access a
property, you can use a dot (.) notation like this:
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
price: 9.99,
describe: function () {
console.log(`Book title: ${this.title}`);
console.log(`Book author: ${this.author}`);
},
};
The this keyword refers to the context of the code, which is the
myBook object in this case.
For example, if you have data about a book, you can use object
keys such as title, author, and price to help you understand the
context of the value stored in each key.
When you call a function such as String(), you call the function
without specifying the object that has that function:
String(7)
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
};
console.log(myBook.title);
console.log(myBook.author);
And here’s how you use the square brackets to access the same
properties:
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
};
console.log(myBook["title"]);
console.log(myBook["author"]);
Keep in mind that you need to wrap the property name in
quotes like a string, or JavaScript will think you’re passing a
variable inside the square brackets.
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
};
console.log(myBook);
{
title: 'Beginning JavaScript',
author: 'Nathan Sebhastian',
year: 2023,
publisher: 'CodeWithNathan'
}
You can add as many properties as you need to the same object.
How to modify object properties
To modify an existing property, you need to specify an existing
object property using either the dot or square brackets notation
followed by the assignment operator as follows:
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
};
console.log(myBook);
Output:
{
title: 'Beginning JavaScript',
author: 'John Doe'
}
As you can see, the author property value has been changed.
let myBook = {
title: "Beginning JavaScript",
author: "Nathan Sebhastian",
};
delete myBook.author;
console.log(myBook);
Output:
When you try to access the deleted property, you will get the
undefined value.
propertyName in myObject
let person = {
firstName: "Nathan",
lastName: "Sebhastian"
}
Summary
In this chapter, you’ve learned about the object data type,
which is a special data type that can hold many properties and
methods.
Suppose you want to print the string 'Owl' from the birds array.
Here’s how you can do it.
let birds = ['Owl', 'Eagle', 'Parrot', 'Falcon'];
console.log(birds[0]); // Owl
console.log(birds[1]); // Eagle
There you go. You need to type the name of the array, followed
by the index number wrapped in square brackets.
You can also assign a new value to a specific index using the
assignment operator.
console.log(birds);
// ['Owl', 'Eagle', 'Vulture', 'Falcon']
Because the array index starts from zero, the value 'Parrot' is
stored at index 2 and not 3.
To get the size of an array, you can access the length property:
console.log(fishes.length); // 3
Next, you can use the push() method to add an item to the end
of the array:
let birds = ['Owl', 'Eagle'];
birds.push('Sparrow');
console.log(birds);
// ['Owl', 'Eagle', 'Sparrow']
birds.pop();
console.log(birds);
// ['Owl', 'Eagle']
The unshift() method can be used to add an item from the front
at index 0:
fishes.unshift('Sardine');
console.log(fishes);
// ['Sardine', 'Salmon', 'Goldfish', 'Tuna']
fishes.shift();
console.log(fishes);
// ['Goldfish', 'Tuna']
The indexOf() method can be used to find and return the index
of an item in the array.
The method will return -1 when the item isn’t found inside the
array:
console.log(pos); // 2
To iterate over an array, you can use the forEach() method. The
method accepts a function that will be executed for each
element in the array.
This method will pass the array’s element and index position to
the function. Here’s an example of using the method:
0: Salmon
1: Goldfish
2: Tuna
And that’s how you iterate over an array using the forEach()
method.
Summary
In this chapter, you’ve learned about the array special data type
and how to manipulate the values stored in an array.
By using arrays, you can store data of different types under one
variable just like objects, but much simpler as you don’t need to
add a key for each value.
CHAPTER 9 - THE DOCUMENT
OBJECT MODEL (DOM)
INTRODUCTION
Before we dive into the lesson, let’s recap what we know about
the web browser and HTML documents.
HTML and CSS were great partners, but even then, a web page
is still just a passive thing. Once the browser process the
content and render it, you can’t edit the page in any way.
You can’t change the background and text color of the page (the
dark mode, for example) you can’t create cool animations, turn
on the microphone or webcam, and retrieve data from another
website to display on the page after a click.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello World!</h1>
<p>This is a paragraph element</p>
</body>
</html>
You can access the window object from the Console by typing
window and press Enter. The browser would respond by showing
you the methods and properties of the object as follows:
The window object provides both child objects and methods that
you’re going to access to manipulate a web page.
For example, the window has the console object that you can use
to log a message to the console.
window.console.log('Hello World!');
console.log('Hello World!');
The same goes for when you access the document object. You can
write document.propertyOrMethod instead of
window.document.propertyOrMethod.
It’s not called the Window Object Model because we’re mostly
going to work with the Document object instead of the Window
object.
Figure 11. Document Object Model
From the document object you can retrieve any data related to
the HTML content using JavaScript. You can get the Document
title using the document.title property, and the URL using
document.URL. The referrer is available in document.referrer, the
domain in document.domain.
From the document object you can get the body and head nodes:
You can try to access these from the console as shown below:
Figure 12. Document Properties
Summary
The DOM is a tree-like structure generated from the HTML file
that we created and loaded into the browser.
The browser then exposes this DOM as a set of JavaScript
objects that we can access, such as the window and document
objects.
By clicking the next button, you can jump into the other parts of
the form. When you click on the submit button, an alert will be
triggered and your inputs will be displayed:
Figure 14. Wizard Form Alert
To keep things simple, the form will only have one input for
each step and you don’t need to validate the data.
Given the demo above, you basically need to break the form
into three <div> elements, and then use JavaScript to display
them one at a time. Let’s start the project in the next section.
Project Setup
To start this project, let’s create a new folder to separate the
project from the code examples we’ve written so far.
Let’s name the folder wizard_form and create three files that we
will fill later: index.html, style.css, and script.js.
Open the index.html file in VSCode and write the basic HTML
structure inside:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
</html>
<body>
The <form> tag is added as the child of the container div. Now,
you need to create the three steps of the Wizard Form.
<form id="wizard-form">
<div id="step1" class="step">
<div class="form-group">
<label for="email">Email address</label>
<input
class="form-control"
name="email"
type="text"
placeholder="Enter email"
/>
</div>
</div>
Now if you open the HTML file using Live Server, you can see
the skeleton HTML structure finished:
It’s time to add some CSS to beautify this application. Open the
style.css we created earlier and put the following styles inside.
I’ll briefly explain these rules below:
body {
margin: 1rem;
font-family: sans-serif;
background-color: #edceb0;
}
.container {
margin-left: auto;
margin-right: auto;
background: #fff;
height: 424px;
width: 338px;
padding: 71px 93px 0;
border-radius: 10px;
box-shadow: 0 2px 7px 0 rgba(0, 0, 0, 0.1);
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-control {
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
border: 1px solid #ced4da;
border-radius: 0.25rem;
box-sizing: border-box;
}
If you need to check your work, you can get the sample code
used in this chapter at https://github.com/nathansebhastian/js-
wizard-form/tree/main/check-point-1
CHAPTER 11: THE WIZARD FORM
MECHANISM
The CSS selector is the pattern you use when targeting a specific
element for styling. Here’s an example of CSS selectors:
document.querySelector('#box');
<body>
<h1>Query Selector</h1>
<h2>Learning Query selector method</h2>
<p id="opening" class="bold">Placeholder</p>
</body>
You can retrieve the <p> element by passing "p" as the method’s
argument:
console.log(element);
// <p id="opening" class="bold">Opening</p>
Or you can also pass the id or the class attribute, it will return
the same <p> element:
document.querySelector('.bold');
// <p id="opening" class="bold">Opening</p>
document.querySelector('#opening');
// <p id="opening" class="bold">Opening</p>
When you pass a selector that returns two elements, only the
first element will be returned by the method. The following
code tries to fetch both <h1> and <h2> elements:
document.querySelector('h1, h2');
// <h1>Query Selector</h1>
Above, you can see that the method returns the first match,
which is the <h1> element.
querySelectorAll() Method
The querySelectorAll() method is a JavaScript method from the
DOM API that allows you to retrieve all elements that match the
CSS selector you passed as the argument.
document.querySelectorAll('.box');
The string parameter passed to the querySelectorAll() method
follows the CSS selector pattern, where class is represented by
a period . and id is represented by a hash #.
<body>
<p id="opening" class="bold">Opening</p>
<p id="middle">Middle</p>
<p id="closing" class="bold">Closing</p>
</body>
You can retrieve all <p> elements by passing "p" as the method’s
argument:
console.log(elements);
// NodeList(3) [p#opening.bold, p#middle, p#closing.bold]
console.log(elements[0]);
// <p id="opening" class="bold">Opening</p>
Or when you only need the opening and the closing <p>
elements, you can pass either the class or the i`ds̀ as the
argument:
document.querySelectorAll('.bold');
// [p#opening.bold, p#closing.bold]
document.querySelectorAll('#opening, #closing');
// [p#opening.bold, p#closing.bold]
To access elements of the NodeList object, you can use the index
position. You can also use the forEach() method to iterate over
the elements:
The code above will log the current element inside the loop and
set its background-color value to "yellow";
.step.active {
display: block;
}
This causes all the div tags with the step class to be hidden from
display. To show the form step add the class active to the step1
div as follows:
Go back to the browser, and you now see only the first step of
the wizard form being displayed.
let currentStep = 1;
function nextStep() {
if (currentStep < 3) {
currentStep++;
document.querySelectorAll('.step').forEach(function (element) {
element.classList.remove('active');
});
document.querySelector(`#step${currentStep}`).classList.add('active');
document.querySelector('#currentStep').textContent = currentStep;
}
}
After that, we select the current step and add the active class to
the element using the classList.add() method. The final task is
to change the content of the currentStep paragraph to the
currentStep.
Open the wizard form in the browser, and you should be able to
navigate to step 3 using the next button. But notice that you
can’t go back to the previous step! This is what we’re going to
implement next.
function prevStep() {
if (currentStep > 1) {
currentStep--;
document.querySelectorAll('.step').forEach(function (element) {
element.classList.remove('active');
});
document.querySelector(`#step${currentStep}`).classList.add('active');
document.querySelector('#currentStep').textContent = currentStep;
}
}
The prevStep() function is identical to the nextStep() function,
except for the if statement check and decrementing the
currentStep part.
let currentStep = 1;
function showStep() {
document.querySelectorAll('.step').forEach(function (element) {
element.classList.remove('active');
});
document.querySelector(`#step${currentStep}`).classList.add('active');
document.querySelector('#currentStep').textContent = currentStep;
}
function nextStep() {
if (currentStep < 3) {
currentStep++;
showStep();
}
}
function prevStep() {
if (currentStep > 1) {
currentStep--;
showStep();
}
}
Back to the browser, now you can go to the next and previous
steps with the buttons. The last thing we need to do is to style
these buttons. Right now they look pretty bad:
.f-right {
float: right !important;
}
.btn {
color: #fff;
cursor: pointer;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
padding: 0.375rem 0.75rem;
border: 1px solid transparent;
}
.btn-next {
border-color: #007bff;
background-color: #007bff;
}
.btn-previous {
border-color: #6c757d;
background-color: #6c757d;
}
So far so good. The final task is to add the submit button and
display the data collected by the form.
If you get an error and would like to check your work, you can
get the code used in this chapter at
https://github.com/nathansebhastian/js-wizard-
form/tree/main/check-point-2
CHAPTER 12: HANDLING
WIZARD FORM SUBMISSION
.btn-submit {
border-color: #28a745;
background-color: #28a745;
}
Aside from using the onclick attribute, we can also add event
listeners using the addEventListener() method.
Let’s add an event listener so that we know when a form is
submitted. The Wizard Form we created has an id attribute, so
you can select that form using JavaScript.
event.preventDefault();
const email = wizardForm.elements.email.value;
const username = wizardForm.elements.username.value;
const password = wizardForm.elements.password.value;
You can test the Wizard Form in the browser. Fill in the
textboxes and you’ll get an alert when clicking the submit
button:
// alert(...);
wizardForm.reset();
currentStep = 1;
showStep();
The reset() method from the form will reset all textboxes, then
we set the currentStep variable to 1 and run the showStep()
function to reset the interface.
const previousButton =
'<button class="btn btn-secondary" onclick="prevStep()"
type="button">Previous</button>';
const nextButton =
'<button class="btn btn-primary f-right" onclick="nextStep()"
type="button">Next</button>';
const registerButton =
'<button type="submit" class="btn btn-success f-right">Register</button>';
For the first element at index 0, we only add the next button.
For the last element, we add the previous and submit buttons,
for all the middle elements, we add the previous and next
buttons:
Now we can remove all <button> elements from the HTML file.
Run the Wizard Form in the browser again, and you’ll see the
buttons added using JavaScript.
The Wizard Form is finished. You can check the completed
application at https://github.com/nathansebhastian/js-wizard-
form/tree/main/complete
Summary
Congratulations on finishing your first JavaScript project! The
Wizard Form application helps you learn the idea of using
JavaScript to manipulate what you see on the screen.
setTimeout(function () {
console.log('Hello!');
}, 3000);
Run the code above from the console, and after 3 seconds, you
should see the 'Hello!' string printed.
Now, let’s add some console log before and after the
setTimeout() method as follows:
console.log('Start');
setTimeout(function () {
console.log('Hello!');
}, 3000);
console.log('End');
When you run the code above, you will see this output:
image::images/3-4-example-settimeout.png
Back to the example above, we can make JavaScript wait for the
setTimeout() method to finish before printing the 'End' string by
moving the console log line below 'Hello!' as follows:
console.log('Start');
setTimeout(function () {
console.log('Hello!');
console.log('End!');
}, 3000);
But of course, we can do this because we’re the ones who added
the setTimeout() method.
callbackFn(response);
}, 3000);
}
To get the response from the function above, we need to call that
function and pass both data and a callback function.
Summary
In this chapter, you’ve learned that JavaScript is a programming
language that doesn’t stop to wait for a function that takes time
to finish. It will continue to run the next line of code when the
previous line isn’t completed yet.
To handle the Promise object, you need to chain the function call
with then() and catch() functions as shown below:
p
.then(message => console.log(`Promise resolved: ${message}`))
.catch(message => console.log(`Promise rejected: ${message}`));
Callbacks vs Promises
The promise pattern is created to replace the use of callbacks.
By using promises, the code we write would be more intuitive
and maintainable.
Here, you can see that the function processMessage accepts two
callback functions: resolveCallback and rejectCallback.
processMessage(
value => console.log(value),
reason => console.log(reason)
);
function processMessage() {
return new Promise((resolve, reject) => {
if (!isPhoneStore) {
reject({
name: 'Wrong store',
message: 'Sorry, this is a food store!',
});
} else if (!isPhoneAvailable) {
reject({
name: 'Out of stock',
message: 'Sorry, the X phone is out of stock!',
});
} else {
resolve({
name: 'OK',
message: 'The X phone is available! How many you want to buy?',
});
}
});
}
processMessage()
.then(response => console.log(response))
.catch(error => console.log(error));
Here, you can see that the processMessage() function returns a
Promise object that gets resolved only when both isPhoneStore
and isPhoneAvailable are true.
When one of the two variables is false, then the Promise object
will be rejected.
Here you can see that you don’t need to add two extra
parameters to the processMessage() function just for the
callbacks, and when calling the function, you also use the then()
and catch() methods to handle the result of the promise.
Summary
In this chapter, you’ve learned how the Promise object works in
JavaScript. A promise is easy to understand when you realize
the three states that can be generated by the promise: pending,
resolved, and rejected.
In this chapter, we’re going to learn about the Fetch API. But
before we start, let’s quickly review what we mean by API.
What is an API?
API stands for Application Programming Interface, but this
fancy term doesn’t really tell you what an API is, so let me
explain it in simpler terms.
Instead, you order food from the waiter, who takes the order to
the kitchen. In the kitchen, the chef will make the food, and
when it’s done, the waiter brings the food to your table.
There are two kinds of APIs: the web API (also known as REST
API), or library API.
The Fetch API we’re going to learn next is also a library API.
fetch('<Your URL>');
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => console.log(response))
.catch(error => console.log(error));
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => console.log(data))
{
userId: 1,
id: 1,
title: "delectus aut autem",
completed: false
}
fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
}).then(response => response.json())
.then(data => console.log(data))
When you send a POST method, you need to set the request
header and body properties to ensure a smooth process.
For the header, you need to add the Content-Type property and
set it to application/json. The data you want to send should be
put inside the body property in a JSON format. See the example
below:
fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title: 'Learn JavaScript' }),
}).then(response => response.json())
.then(data => console.log(data))
{
title: 'Learn JavaScript',
id: 201
}
Summary
The Fetch API allows you to access APIs and perform a network
request using standard request methods such as GET, POST,
PUT, PATCH, and DELETE.
Here, we only see the GET and POST methods in action, but
don’t worry because we’re going to use the rest in the following
project.
CHAPTER 16: PROJECT 2 -
CREATING A TO-DO LIST
APPLICATION
Installing Node.js
Node.js is a JavaScript runtime application that enables you to
run JavaScript outside of the browser. We need to install this
application on our computer to create a simple server.
{
"tasks": [
{
"title": "Learn Swimming",
"completed": true,
"id": 1
},
{
"title": "Learn Cooking",
"completed": false,
"id": 2
}
]
}
The next step is to run this JSON file as a server. To do so, you
need to use Node Package Manager (or NPM for short) to install
a json server.
json-server db.json
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To-do List</title>
</head>
<body></body>
</html>
<title>To-do List</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css
"
/>
CDN stands for Content Delivery Network, and it’s a server that
allows you to fetch popular libraries. By using a CDN, you don’t
need to download the Bootstrap CSS file onto your computer
and load it locally.
The last step is to add the script.js file to the HTML file. As
always, add the defer attribute so that the browser loads the
script after the HTML document has been rendered:
Now we need to add some HTML to the <body> tag. First, write
the container that’s going to contain all our HTML content. I’m
going to add some comments to help you put the HTML in the
right place:
<body>
<div class="container m-5 rounded mx-auto bg-light shadow">
<!-- App title section START-->
<div class="row p-4">
<div class="col">
</div>
</div>
<!-- App title section END -->
</div>
</body>
Under the 'App title section END' comment, add an input and a
button we’re going to use to create a task. The divs around the
input and the button is created only to apply Bootstrap style:
Next, we’re going to add the filter section under the 'Create task
section'. This filter section will contain a select input:
The last step is to display the tasks. We’re going to use table
element here:
Summary
In this chapter, we’ve installed Node.js and created a simple
server using the json-server package. The database we use in
this project is a simple JSON file named db.json.
We’ve also created the HTML file for our To-do application and
used Bootstrap to style the elements.
CHAPTER 17: ADDING CRUD
FUNCTIONALITIES TO THE
APPLICATION
"liveServer.settings.ignoreFiles": [
"db.json",
".vscode/**",
"**/*.scss",
"**/*.sass",
"**/*.ts"
]
Save the changes. Now Live Server won’t reload when there’s a
change in the db.json file.
fetch(BASE_API_URL)
.then(response => response.json())
.then(tasks => {
console.log(tasks);
});
}
getTasks();
If you open the browser console, you should see the following
output:
<tbody id="tbody-tasks">
...
</tbody>
This means we can get the element using the query selector:
Below, you can see that the code for creating the elements is
quite long, but it’s essentially the same as when we create
buttons for the Wizard Form:
function getTasks() {
let tableRows = '';
fetch(BASE_API_URL)
.then(response => response.json())
.then(tasks => {
tasks.forEach(task => {
const element = `<tr class=${
task.completed ? 'table-success' : ''
}><td><input class="form-check-input" type="checkbox" ${
task.completed ? 'checked' : ''
} onclick="toggleTask(event, ${task.id})"></td><td>${
task.title
}</td><td><button type="button" class="btn btn-link"
onclick="editTask(${
task.id
})">Edit</button></td><td><button type="button" class="btn btn-link"
onclick="deleteTask(${
task.id
})">Delete</button></td></tr>`;
tableRows += element;
});
tableElement.innerHTML = tableRows;
});
}
The elements are stored in the tableRows variable, and when the
forEach() process is finished, we set the innerHTML value of the
tableElement to the tableRows value.
If you open the browser, you should see the tasks appear on the
table like this:
Since we’re able to display the data, we can remove the hard-
coded <tr> elements in our index.html file. Remove the
highlighted lines below:
function addTask() {
const taskTitle = document.querySelector('#task-title');
const data = {
title: taskTitle.value,
completed: false,
};
fetch(BASE_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then(response => {
if (response.ok) {
alert('Task added');
getTasks();
taskTitle.value = '';
}
});
}
Now you can test adding a new task from the browser. The next
step is to add the delete functionality.
Deleting a Task
To delete a task from the database, we need to define the
deleteTask() function in our script.js file. Here’s the function
in full:
function deleteTask(id) {
const confirmation = confirm('Are you sure you want to delete the task?');
if (confirmation) {
fetch(BASE_API_URL + '/' + id, {
method: 'DELETE',
}).then(response => {
if (response.ok) {
alert('Task deleted');
getTasks();
}
});
}
}
First, we confirm the decision to delete the task with the user
using the confirm() function, which is part of the browser API.
Editing a Task
To edit a task, we need to declare the editTask() function in our
script.js file as follows:
function editTask(id) {
const newTitle = prompt('Enter the new task title');
if (newTitle) {
const data = { title: newTitle };
fetch(BASE_API_URL + '/' + id, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then(response => {
if (response.ok) {
alert('Task updated');
getTasks();
}
});
}
}
Filtering Tasks
The select input that we’ve created will serve as a filter
function. We can refresh the list to see only completed or active
tasks.
function filterTasks(event) {
const filterValue = event.target.value;
if (filterValue === 'all') {
getTasks();
} else if (filterValue === 'completed') {
getTasks('completed');
} else {
getTasks('active');
}
}
Here, we use the event property again to get the value of the
select input.
function getTasks(filter) {
This way, the GET request corresponds to the filter value you
selected.
And now the application is complete! You can try all the
functionalities on your browser.
Summary
Well done! You’ve just finished your second JavaScript project.
Hopefully, this project has shown you how JavaScript is the
secret ingredient that makes a web application interactive.
The browser also exposes signals known as events that you can
listen to. This way, you can execute some code as a reaction to
the event, such as the 'click' and 'submit' events.
The Next Step
There are other APIs that you can use to get the location of the
user, accept images and files submitted by the user, or access
your camera and microphones.
Exploring all these APIs is beyond the scope of this book, so I’ll
make another book where we’re going to explore the Browser
APIs in full and build projects with it.
If you didn’t like the book, or if you feel that I should have
covered certain additional topics, please let me know in the
email. This book can only get better thanks to readers like you.
Thank you and all the best for your career in tech!
By Nathan Sebhastian
https://codewithnathan.com