4. JavaScript Basics

It's an oxymoron - learning how to do computer programming without worrying too much about computer programming.

Before we start... / An overview / JavaScript syntax, a breakdown / Variable / Array and object / Function (Method) / Accessing an HTML document / Event and interaction / If...else... / Loop / Form, submit, and reset / For the next unit...

Before we start...

  1. Let me talk about some more English issues in your writing.
  2. I would like to highlight a few websites that seem to show a very good progress.

An overview

Credit: Koniart

For many people new to this area, computer programming sounds pretty scary. But Web programming is actually a lot easier than other types of computer programming (quantum computing? Gee...). JavaScript (henceforth, JS) is also very straightforward, and with the foundation you have built in previous tutorials, you should be able to adapt to it very quickly...let along the fact that people have created a more convenient version of JavaScript - jQuery - which you will learn in the next unit.

Let's begin with some preparation works. First of all, create a js directory right under a specifc example directory (such as /examples/3-2/js), which should include JS files used only for that specific directory. In your own website projects, you should therefore create different js directory for each website project folders, such as /personal/js and /analysis/js. If you will have any JS codes that apply to both website projects, then maybe you want to create /js in the root directory and place your global JS codes there.

Let's first create a demo.js file in the js directory in a specific example folder. To allow your webpage to run the JS codes in demo.js, we need to load it to your webpage using the <script> element. Insert the following line to your <head> section of your index.html under your the specific example directory:

<script src="js/demo.js"></script>

To check if the JS file is embedded correctly into the webpage, add the following line to your demo.js:

alert('embedded!');

After you update your website and reload the index page, you should be able to see a pop-up message showing the message embedded!.

And let's go back again to the useful Developer Tools hidden in your Web browser, and this time look at the Console tab:

When we try to debug your JS codes, you will be using this part of the tools a lot. I mean, A LOT. Let's see how it works. Now, add the following line to your demo.js:

console.log('logged!');

Again, update your website and reload the index page - in addition to the same pop-up message, you should see the new message in the Console tab of your Developer Tools.

If everything goes as expected, then you are ready for your journey of JS programming!

JavaScript syntax, a breakdown

The codes you will see in a JS file could be in general categorized into two types - statement and code blocks. We have seen some statements when we were preparing for our JS programming above, one of which is repeated here:

alert('embedded!');

This is a simplified statement that will be translated in an order by a Web browser as show a pop-up message 'embedded!' in the browser window!. The most important thing about a statement in a JS file is that it ends with a semi-colon ;, which indicates the end of a statement. Interestingly, if you remove the semi-colon from the statement above, you'll still see the pop-up message. This does not suggest that the semi-colon is not important - it's just that your Web browser is smart enough to ignore the mistake you make.

A good example showing the importance of semi-colon in a statement is to put the two statements we used above in the same line as:

alert('embedded!'); console.log('logged!');

Not only will you see the same pop-up message again, you will also see the text message in the console tab of your Developer Tools. This means that statements are processed based on the ending indicated by semi-colons, rather than by separate lines - of course, we still put individual statements in separate lines for a better readibility. So, always add a semi-colons at the end of a statement.

A code block is just a collection of statements represented with a pair of curved brackets { }. We have seen the adoption of this concept in the CSS tutorials, in which curved brackets include a collection of CSS properties, and we will extend this concept to objects when we get to Variable part. In the following example, we have a code block containing the two statements introduced previously:

{ alert('embedded!');
console.log('logged!');
}

We need to use code blocks from time to time in special occassions: Repeat the same statements for multiple times (see Loop), run different sets of statements when different conditions are met (see If...else...), etc. So code block is just very convenient for programmers. Again, the tradition is to have each statement indented from the left edge when it is included in a code block to better show where it belongs, and this is why a good editor like Sublime Text also does this for you automatically in a JS file. If you align all statements to the left edge, it won't matter; the statements will still be processed normally by Web browsers. It just creates a mess for you (and your partners in a collaborative project).

The most frequent type of statements you probably will write and see would involve some operator =, as in the following line:

num = 5;

This = (equal) operator assigns the value to its right (5) to a container named num to its left. More details about this process will be given in the Variable section below, but my point here is to bring up another tradition in coding that is not specific to JS. That is, when there's an operator in a statement, we tend to leave a space on both sides of it, again for a better readibility of your codes. You can remove the spaces and will find the processing of your codes unaffected, but it's not as clear in your text editor.

Finally, as in the case of HTML and CSS, you can also add some notes in your JS file using // or /**/. By adding // before some texts in a JS file, the texts after // in the same line are treated as a comment, rather than part of JS codes:

// This is a comment!

We also frequently use // to temporarily comment out a statement when debugging our codes:

//No more pop-up message!
//alert('embedded!');
console.log('logged!');

Variable

Credit: Polycart

When you do grocery shopping, you don't go back and forth between shelves and the casher. Instead, you get a basket or a cart with you. You grab goods on the shelves and put them into your basket/cart. Later, you would simply check your container to see the number of things there, and you don't have to go back to every corner in the grocery store to count how many times you grab things from the shelves. Of course, you could change the things in your container freely, but still, you only need to count the number of things in that container.

All programming languagues have such a container for storing information, from which you can re-use the information easily - it's called variable. In JS, you can use the keyword var to declare a variable, as in the following example:

var num; // Declare a variable named "num"

And we can put things into the container (I mean, variable) with the = operator introduced in the previous section:

var num = 5; // Declare a variable named "num" equal to a numeric value of 5

If you have multiple variables to declare, you don't need an individual statement for each variable. Instead, you can declare them altogether after var, and separate every variables with a comma ,:

var x = 10, y = 20, z = 40;

It's also possible to declare a variable first without assigning its value, mostly because the value will be assigned in other statements later. In this case, the variable has a undefined value, since it's value is not defined just yet:

var undefinedVar; // This variable has a "undefined" value

Let's try to print the value of both variables in the console tab of our Developer Tools to verify my claims:

console.log(num); // Print the value of "num"
console.log(undefinedVar); // Print the value of "undefinedVar"

Now, if someone start a conversation with you by saying Did I tell you I love him so much?, your first question would naturally be Who the hell is him?. Your question stems from the fact the referent of the pronoun is not defined in the conversational context. Similarly, in JS you cannot have access to the value of a variable without declaring the variable in your code, so you will get a reference error:

console.log(him); // Who the hell is "him"? - Reference Error

Intriguingly, if you declare the variable with var after it is accessed, there's no reference error - you only get undefined:

console.log(him); // Still undefined, but no reference error
var him = 'Mike';

This is because variable declaration is always hoisted in JS, and the above code is automatically interpreted as follows - the variable can be found since the hoisting of var him, but its content is still not defined when it is accessed in console.log():

var him; // Hoisted variable declaration
console.log(him); // There is the container, but the content is still not defined
him = 'Mike';

But why would you want to stupidly declare a variable after it is accessed in your codes? The best practice is to always declare your variables first in the entire script or in a code block (see var vs. let below), whether they are assigned a value right away.

Once you have declared a variable with var, you can always assign a new value to it later without var:

var newVal = 0; // Declare a new variable with a value 0
console.log(newVal); // Output = 0
newVal = 5; // Assign a new value
console.log(newVal); // Output = 5

You can also freely assign a value of a different type (for instance, a string, see below) to a declared variable, but it's going to be a very bad practice. A variable is usually created to serve a particular function, such as storing the name of a user, the type of the user's browser, the time the user spends on your website, etc., so it's usually not the case in which you need a variable to store both a numeric value and certain text info. And that's why we need a logical sense of naming.

The sense/rules of variable naming

When you declare a variable, it is also important give your variable a semantically meaningful name. In the above example, my first variable is named num because...it's assigned a numeric value. The second one is nullVar because it is not assigned any value. This logic is similar to those applied to the use of HTML elements - to be semantically meaningful. No one would stop you using random variable names like x, y, z, etc., but later you would have a hard time recalling the meaning of these variables. In addition to the above guideline, there are some more rules that you must be aware of before moving on:

  • A variable name cannot include any space, so when I need to combine multiple words for a meaningful variable name, I always capitalize the first letter of a word in the name to make it more readible, just like the nullVar variable above.
  • A variable name cannot start with a digit letter, so a variable name like 1stVar won't work.
  • Most symbols are reserved for special use, like the operators =, +, etc. The best way to avoid this problem is not to have any symbol in your variable name.
  • There is also a list of reserved words, like the var keyword, which cannot be used as a variable name. See a complete list on this W3CSchool page.

Interestingly, you can use weird Unicode characters in your variable name, like ಠ_ಠ:

var ಠ_ಠ = 'Raise my eyebrow'; // Declare a variable with a weird name
console.log(ಠ_ಠ); // You should get the string assigned to it

I would suggest no one to include any special Unicode characters in your variable names - imagine how emoticons could annoy other people trying to read your codes, for instance. Just use the regular English alphabet. If you have any conern over your variable names, you can check their validity on this page.

Lastly, I would like to remind you that everything in JS are case-sensitive:

var num = 5;
var Num = 10;
console.log(num); // It's impossible to be 10
console.log(Num); // It's impossible to be 5

Number, string, and operators

In the above examples, we see how to assign a value to a variable via the operator =, but the assigned value could also be the result of some mathematical calculations using the operators +, -, * (multiply), and / (divide), as in the following cases:

var math1 = 2 + 5;
var math2 = 3 * 4.5;
var math3 = 1 - 5 * 40;

Crucially, your formula can include other variables that are assigned a value earlier:

var num = 5;
var newNum = 4 + num;

So far, the focus has been on variables that are assigned a numeric value, but of course we have many different types of data that can be stored into a variable. The data type we will be using a lot in JS, especially when we need to make our webpages interactive, is string. In JS, a string must be enclosed inside a pair of single/double quotation marks, as in the following example:

var stringVar = 'Here is a string!';

Whether you use single or double quotation marks doesn't really matter, as long as you are consistent. You can't have a single quotation mark on the one side but a double quotation mark on the other:

var wrongStringVar = 'This will not work!"; /* You will see some error message in the console */

I prefer to use single quotation mark just because it's easier to type (you need shift + ' to get a double quotation mark). Yes, I'm just a lazybone. The only minor issue that you might encounter is when your string also has some single quotation marks. In this case, you need to add a backslash \ before each special symbol to escape the character (namely, to tell a Web browser that this single quotation mark should be treated as a regular character, not a special symbol):

var stringWithQuote = 'Here\'s a single quotation mark!';

In JS, you could also easily combine numeric and string values as a string using the + operator:

console.log('A number: '+5); // Output = 'A number 5';
var num = 5;
var str = 'A number ';
console.log(str+num); // The output is the same;

Let's have a small test here - what is the value of the two variables test1 and test2 below?

var test1 = 'Value: ' + 5 + 10;
var test2 = 'Value: ' + 5 * 10;

Of course, it is not possible to use other operators for numeric and string values:

var wrongVar = 'Value ' * 5; // What is this supposed to mean anyway?
var cantDoThis = 'Get rid of A' - 'A'; // Nope. You can't take out a string in this way.

The distiction between numeric and string values are particularly important for digit letters 0-9; when digits are enclosed inside quotation marks, they are viewed as a string, rather a numeric sequence:

var numAsNum = 5; // A numeric value 5 is assigned to "numAsNum"
var numAsStr = '5'; // A string value '5' is assigned to "numAsStr"
console.log(numAsNum + numAsStr); // The output is a string '55', not a numeric value 10

It is always possible to convert a string composed of digits into a numeric value with two JS functions parseInt() and Number() (we will get to the function section pretty soon). The main difference is that parseInt() always round the target number:

var strNum1 = '11.99';
console.log(1 + strNum1); // Output = '111.99'
strNum1 = parseInt(strNum1); // Convert the value of "strNum1" in to a numeric value assigned to "strNum1"
console.log(1 + strNum1); // Output = 12

var vs. let

So far, I have only talked about one way of declaring a new variable, which is to use the keyword var. But there's also another way, which is to use the keyword let:

var x = 5;
let y = 10;

The difference lies in the scope of the variables. When you declare a variable using var, it's scope is global, which means that the variable could be accessed in the entire script. When you declare a variable using let, its scope is limited within a code block represented with a pair of curly parentheses {}. To illustrate this point, let's check the example below with two code blocks. In the first block, we declare a variable using var and try to access it out of the code block, we can still retrieve its value because its scope is global. In the second block, we declare a variable using let, and in this case it is not possible to have access to its value outside the block, because the variable's scope is limited to the block.

{
var x = 5; }
console.log(x); // You get 5.

{
let y = 10; }
console.log(y); // The variable y cannot be found outside the scope.

So, why is this important? First of all, when writing a program, you always need to know which data you will have to keep all the time, and which is just used momentarily. For the variables that are used momentarily (usually inside a code block), you can just throw them away after the task is done. This way, you will write a mor efficient program.

Function (Method)

We rely on functions (or something alike) a lot in all kinds of computer programming since sometimes we need to repeat the same process in our program, so we make the process a function and use it whenever necessary without writing the same codes again and again. A JS function can be created with the following syntax:

function name () {
// some statements here }

First of all, you need the keyword function to create a JS function, which should be followed by the name of the function. After the function name comes with a pair of brackets ( ), which would enclose the data submitted to the function; in our use of console.log, we always submit the data to the function for it to print out the data. It is nevertheless not required for a function to receive any submitted data, as we will see later. After the brackets, we see a pair of curved brackets { } that defines a code block (remember this term?) including some statements. Let's add the following example to our JS file:

function actionCompleted () { alert('Completed!'); }

And when we need to call this newly created function, we just need a statement with the function's name plus a pair of brackets ( ). Since we don't need to submit any data to this function, nothing has to be put into ( ):

actionCompleted();

It doesn't matter whether the statement calling a function occurs before or after the function itself; the function part is always hoisted to the top of your JS code, too. If we follow the logic discussed earlier about variable declaration, then we should develop a habit to put the function codes to the top of our JS file, because it's natural to think that we create them first and then call them later. However, since you might have many complex functions, their codes are going to be lengthy. Plus, they are not used all the time, and what really matters more is the main code. In this case, putting your function codes to the top of your JS file might be in fact bothersome, because most of the time you just want to debug your main code. So, I'll let you decide which works best for you.

However, if you declare a variable and assign a function to it with var, this declaration and initialization won't be hoisted to the top, which means that the statement that calls this function must follow the declaration and initialization codes:

actionCompleted(); // You get an error
var actionCompleted = function() { // Declare a variable "actionCompleted" and initialize it with a function alert('Completed!'); }
actionComplete(); // Not a problem

To avoid the above complication, I usually use just go with the hoisting approach.

Data transmitted between functions and their calls

Just like console.log, we can create a function that receives data as its parameter(s) from its calls and do something with that data. In our first example, we create a function that can send whatever message it receives, make some changes, and put it into a pop-up message box:

function popupMsg (msg) { // "msg" is the parameter of popMsg() let finalMsg = 'Site message: ' + msg; // Make some twist
alert(finalMsg);
}
popupMsg('Test!');
popupMsg('Warning!');

In the above example, the function popupMsg is set to receive one data entry, which is given the name msg in this code block. In other words, msg will be the variable name of the data receive by popupMsg only in this function. In popupMsg, I declare another variable finalMsg to store the twist I make for msg. Then, finalMsg is submitted to the alert function to create a pop-up window with the final message in the last statement. After the function code, I call the function twice with two different string values, and two pop-up windows would appear in the order I call the function.

A function can receive multiple data entries, as long as you separate the names of the variables representing each data entry in the function with a comma , inside ( ):

function userMsg (name, msg) { let finalMsg = name + ' says: ' + msg; // Compose the final msg
alert(finalMsg);
}
popupMsg('Victor', 'Surprise!');
popupMsg('Mary', 'I love Victor!');

To know more about the behavior of a function and a function call, let's assume two possible scenarios. First, you call popupMsg, but nothing is submitted to the function. What would happen?

popupMsg(); // Call the function without submitting the required data entry

Second, supposed that you rename userMsg as popupMsg, making it two functions with the same name, but requesting different numbers of data entries. What would happen when you run the two following lines?

popupMsg('Test!');
popupMsg('Victor', 'Hey!');

Since you can submit some data to a fuction, a function can also return some data back to where it is called as well, and the keyword is exactly what you expect - return. In the following function complexCalc, it always does a complex calculation based on the two numeric values submitted to it:

function complexCalc (x, y) { let result = 0; // Declare a variable used to store the result of calculation
result = x * y;
result = result / (x - y);
result = result * y - x;
return result; // Return the final outcome
}

Now you can save lots of space without repeating these statements in this function, assuming that you need to do the same calculation for multiple times in your JS codes. But when you call this function, you also need to prepare a variable to store the data the function returns as in the following statement:

var num = complexCalc(4, 6); // Assign the returned value to a newly declared variable

One important thing about return is that one a function reach this line, the statements after it won't be executed:

function poorLine () { let msg = 'test';
return msg; // Function stops here
alert('Q_Q'); // Not executed
}

Array and object

Now let's move on to some more advanced data types - array and object. You could imagine an array of data to be a train, and each carriage is stored with their own data. Maybe just look at the code directly:

var numArray = [1, 5, 2, 11, 7]; // An array of five numeric values

In the above example, you see a train of data defined by a pair of square brackets [ ], in which every two carriages are seperated from each other with a comma. Each carriage has its own value, and the entire train is assigned to the variable numArray. The value in each carriage can be accessed or replaced by specifying the number of the carriage (or more precisely, the index of an array position) in [ ], starting with 0 (thus the picture you saw above):

var anArrayValue = numArray[0]; // Get the first value of an array
console.log(anArrayValue); // Output = 1
anArrayValue = numArray[1]; // Get the second value of the array
console.log(anArrayValue); // Output = 5
numArray[1] = 18; // Replace the second value of the array with 18
console.log(numArray[1]); // Output = 18

Why does the index of an array position start with 0 not 1? I didn't dig very deep into the history, but I have seen on the internet is that 0 is the first natural number in math. However, whether 0 can be considered a natural number seems still to be under heated debate. Another explanation is more computation-oriented, but also a bit more technical - 0 means this position is not distant from the head of the array in computer memory (see this post for a much clearer explanation). I'm not a mathmatician, neither a computer scientist, so you can do your own research. But things won't change with your complains, so let's deal with it and remember that the index starts with 0.

Again, things are pretty free in JS, so an array in JS can have different types of data in different positions, as in the following example:

var mixedArray = [1, 'hello!', 'yes!', 9, 87]; // An array with mixed data types

However, you should not take this feature for granted and always use arrays with different types of data in different positions. Supposed that you use an array to store the profile of a person, the organization might make sense in the first place, but soon you will forget which position stores which part of the profile info:

var profile = ['TY Chen', 'NTHU', 39, 180, 72]; // Name, Affiliation, Age, Height, Weight

Instead, an array is best used to store homogeneous data, that is, data that represent similar info. For instance, if you have multiple phone numbers, you can use an array to store them:

var phoneNums = [0937149506, 0956771481, 0982982143]; // Don't call any of these!

When you use an array to store homogeneous data, the index number makes sense - the first phone number, the second phone number, and so on:

phoneNums[0]; // The first phone number
phoneNums[1]; // The second phone number
...

It is also possible to declare an empty array if the values in it will be determined later. A new value could be inserted by directly specifying the position where the value is stored to:

var emptyArray = []; // Leave nothing in the square brackets
emptyArray[0] = 1; // Insert a value 1 to the array's first position
console.log(emptyArray[0]); // Output = 1

A quick test: What's the output you will get by running the following codes?

var emptyArray [];
emptyArray[1] = 1; // Insert a value 1 to the array's second position
console.log(emptyArray[0]); // What's the output?

It would certainly be a rare occassion for you to create an empty array first and insert values by skipping some positions. But since you might make a common mistake (for instance, forget that the first position is 0), it's good to know how the above case works.

The above examples include arrays that have only one single dimension; since you only need one coordinate to identify the position of a value in it. Since each position can be stored with any type of data, it is possible for each position of a single-dimensional array to contain another single-dimensional array, and the entire array becomes a two-dimensional array:

var multiDArray = [[2, 3, 4], [11, 24, 25], [9, 0, 1]]; // A 3-by-3 2D array
console.log(multiDArray[0][1]); // Output = 2
console.log(multiDArray[2][0]); // Output = 9

The codes shouldn't be too difficult to understand - the variable multiDArray is declared as a one-dimensional array with [ ], in which each position is stored with another one-dimensional array with [ ]. When try to access this two-dimensional array, you use two paris of [ ]. The first pair specifies the index of the outer array, as in multiDArray[0]. Since the first position of the outer array is another array, you use the second pair to specify the index of this inner array, as in multiDArray[0][1] - now you have access to the first position of multiDArray and then second position of multiDArray[0], which gives you the output of 2. The same logic applies to multiDArray[2][0]. In Web design, a one-dimensional array would suffice in most cases. For instance, when we retrieve a group of HTML elements from a webpage in JS, they will be presented in a one-dimensional array. Remember, an array is mostly used to store homegeneous data. Before you go for a two-dimensional array or even beyond that, you need to think carefully if you really need this level of coding complexity, or whether using an object will be a better choice.

Object-oriented programming

An object is suitable for a complex data structure, especially when there's no better way of presenting this complex data structure. Let's go back to our profile example. I have demonstrated why array is not a good container for a profile, since every entry of the profile has different meanings, which are thus not homogeneous. We can declare each entry as a separate variable as in the example below, but not only does the code become lengthy, the relation between these entries is not properly presented:

var name = 'TY Chen';
var age = 39;
var affiliation = 'NTHU';
...

But imagine an array-like container, in which each position can be given a meaningful label, so we can clearly know which position stores which type of data...and then you're talking about object. An object is defined with a pair of curved brackets { } (still remember code block?), which include keys and their values:

var profile = {
name: 'TY Chen',
age: 39,
affiliation: 'NTHU',
height: 180,
weight: 72
};

In the above example, each key and its value is separated by a colon :, and the value is followed by a comma , indicating the end of a key, except for the last one. Note that after the second curved bracket }, a semi-colon ; is still added since we are dealing with a statement that declares a variable profile. The way a value is accessed from an object is also very straightforward - just add a period . after the name of an object and the key of the target key:

profile.name; // Get the value of the 'name' key
profile['name']; // This would work, too

The same logic applies when you need to update the value of a key or insert a new key:

profile.name = 'Tsung-Ying Chen'; // Update an existing key;
profile.gender = 'Male'; // Add a new key called 'gender' with a value 'Male'

As with the case of array, there is no constraint on the type of data in each key - it can be a numeric value, a string, an array, and even another object:

var profile = {
name: 'TY Chen',
age: 39,
affiliation: 'NTHU',
height: 180,
weight: 72,
contact: { home: 0227682149, // Don't call!
mobile: [0937149506, 0956771481, 0982982143]
}
};

In the above example, I added another key with a key contact to store my contact info, whose value is also an object with two keys for my fake home number and fake mobile phone numbers. The mobile key has an array of three numeric values for my three fake mobile numbers, which are homogeneous. Now, the entire profile has a very clear, sensible structure, and can be accessed in a meaningful way:

profile.contact.home; // Get the home number
profile.contact.mobile[0]; // Get the first mobile number

Now back to the statement we have been using a lot - console.log(); clearly, there's an object named console, which refers to the console tab of the Developer Tools in your Web browser in this case, and it has a key with a key named log, which is a function represented with a pair of brackets ( ) that allows you to print some message in the console (again, we will get to the function part soon). Yes, we have been in the world of object since the very beginning of this tutorial!!!. JS is one of the obeject-oriented programming languages, and thus the concept of object is crucial to the rest of this tutorial. In fact, all variables are an object regardless of they are assigned a number, string, or array as their value. For instance, once you declare a string variable, you actually can have access to its length key for the number of characters in this variable:

var str = 'This is a loooooooooooooooog string!';
console.log(str.length); // Print the number of characters in "str"

Or, when you have an array, its length key provides you the number of data entries in this array:

var mobileNum = [0937149506, 0956771481, 0982982143];
console.log(mobileNum.length); // Get the number of data entries stored in "mobileNum"

You can also add a new entry to the end of an array with the function of the push key of an array object:

mobileNum.push(0976111358); // Insert a new number to the last position of "mobileNum"
console.log(mobileNum[3]); // Output = 0976111358

So from now on, when you see a period . in our JS code, you should know that it means two different things - First, it could indicate a decimal in a numeric value as in 3.14159, or alternative, it could indicate a key of an object, as in console.log. We will see both cases a lot in the rest of this course.

Before we leave this section, we can create an object in which each key is a function to mimic the codes like console.log, which calls a function in an object. We can use the userMsg and complexCalc functions we created earlier and included them as the value of the keys with the same name in an object:

var demoFuncs = { popupMsg: popupMsg, // Key name before the colon, function name after the colon
userMsg: userMsg,
complexCalc: complexCalc
};

In the demoFuncs object, similar functions are grouped together since they are all some kind of demos. Then, we can call the corresponding function by referring to the specific key in the object, just like console.log:

demoFuncs.userMsg('Victor', 'I am calling a function!');

Knowing how to create functions helps you save your effort in coding repeating processes, and knowing how to link a function to a key of an object helps you understand the nature of object-oriented programming.

Accessing an HTML document

Credit: Wikipedia Commons

After telling you how object-oriented programming in JS works, I am now able to explain the interface between JS and an HTML document. In JS, we can communicate with our Web browser via the object window. Evidently, this object refers to the browser window, which includes many keys that are directly linked to different parts of a Web browser. For instance, we can get the viewport width and height by accessing to the innerWidth and innerHeight property of window:

var winWidth = window.innerWidth; // Viewport width in pixel, numeric value
var winHeight = window.innerHeight; // Viewport height
console.log(winWidth);
console.log(winHeight);

Or, you can get the URL of the current page with window.location.href or the path of the current page with window.location.pathname. You can also assign a new value to these properties, which controls a Web browser to navigate to a different website or a different path of the current website:

window.location.href = 'https://google.com'; // Change the current URL to Google
window.location.pathname = '/nosuchpath'; // Change to a path that does not exit

We can also run some functions depending on the action executed in a browser window, such as popping up a message when a webpage is fully loaded in the browser window:

window.onload = function() { // Initialized a function in the "onload" key of "window" alert('Website is ready!'); // Executed when everything in the webpage is fully loaded };

Window.document

There are many properties or functions that you can take advantage of (see the full list on this MDN Web Docs page), but our focus in this section will be mostly on window.document, which apparently refers to the HTML document that is currently presented in a Web browser. Since hardly anyone writes JS codes without using window.document, JS developers decided to create a shortcut for it, so you can simply use document for window.document. In document, you can have access to the DOM of an HTML document, such as the title of the website specified inside <title></title>:

var siteTitle = document.title;
console.log(siteTitle);

Since document allows JS codes to go through the DOM of an HTML document, it is certianly possible for us to retrieve some HTML elements from the DOM, with the functions like getElementsByTagName, getElementsByClassName, and getElementById. Note the emphasis put on the plural suffix -s of Element in the first two - It implies that the functions could get one or more HTML elements from an HTML documents. Let's start with getElementsByTagName, which retrieve all the elements with the same tag name for you:

var allDivs = document.getElementsByTagName('div'); // Assign all "div" elements returned as an array to "allDivs"
console.log(allDivs[0]); // Examine the first "div" element as an object in the console tab
var allAudios = document.getElementsByTagName('audio'); // Assign all "audio" elements returned as an array to "allAudios"
console.log(allAudios); // Probably "undefined" - nothing is found

In the above example, we first use getElementsByTagName to retrieve all <div> elements. Since your HTML document is likely to have one or more <div> elements, each element would be collected as an object and stored in an array and finally returned to where getElementsByTagName is called. This is true even if there is only one element, which is stored in an array with just this element. If the function finds nothing in an HTML document, it returns a null value, which represents the absolute emptiness in the word of computer programming.

The other two functions trawl through an HTML document for elements that are given some specific class and id value, as their name suggests. As briefly discussed earlier, you could group HTML elements together with the class attribute with the same value, or make them distinct from each other with the id attribute assigned a unique value:

<div class="group1" id="no1">Group 1 No 1</div>
<div class="group1" id="no2">Group 1 No 2</div>
<div class="group1" id="no3">Group 1 No 3</div>
<div class="group2" id="no4">Group 2 No 4</div>
<div class="group2" id="no5">Group 2 No 5</div>
<div class="group2" id="no6">Group 2 No 6</div>

With the six <div> elements in our HTML document, we could try out getElementsByClassName and getElementById respectively. For a specific element object, we could use the property innerHTML to retrieve its content:

var allGroupEm = document.getElementsByClassName('group1'); // Get all "group1" class elements
var groupEm = allGroupEm[0]; // Get the first element of the "group1" class
console.log(groupEm.innerHTML); // Print the content in the console
var idEm = document.getElementById('no6'); // Get one 'no6' id element
console.log(idEm.innerHTML); // Print the content

To be able to retrieve the content of these elements means we can also change the content of these elements by assigning a different value to their innerHTML property:

var idEm = document.getElementById('no5'); // Get one 'no5' id element
idEm.innerHTML = 'Group 2 No 5 (Changed)'; // Change the content of the element

JS styling

Not only can we change the content of an element, we can also style a particular element in JS, which is probably the most important thing in Web design. For each element object, there is a key named style...and it's clear what it does, right? In this key, you can find and set CSS properties with their name, such as:

idEm.style.color = 'red'; // Set the font color

For CSS properties that originally have a hyphen in their name, such as background-color, you need to remove the hyphen and captalize the first letter of the second word, such as backgroundColor in order to set these CSS properties:

var anotherIdEm = document.getElementById('no6'); // Get one 'no6' id element
anotherIdEm.style.backgroundColor = 'red';

What if you need to set multiple CSS properties? Well, you can have one statement for each CSS property, but more easily, you can set them all at once with the cssText property of the style key, in which you put in CSS properties as in the CSS file:

var oneMoreIdEm = document.getElementById('no2'); // Get one 'no2' id element
// Set CSS properties one by one
oneMoreIdEm.style.backgroundColor = 'red';
oneMoreIdEm.style.color = 'white';
oneMoreIdEm = document.getElementById('no3'); // Get one 'no3' id element
// Set CSS properties all at once
oneMoreIdEm.style.cssText = 'background-color: green; color: yellow;';

Since the styles set in JS are viewed as inline styles, they would be more specific than the styles set in a CSS file for the target elements when same CSS properties are involved. This make sense because you can have some default styles for some elements, whose properties are changed by JS in some contexts.

You can also set inline styles with the function setAttribute() for an element object, as demonstrated on this MDN Web Docs page, but I won't go through it here.

Query selector

You are now probably very excited, because it's so convenient to refer to the class and id attributes when you need to set the style for a specific element, so you start to think about grouping elements together or making them distinct from each other with these attributes. That's the first mistake I made as an amateur Web designer - I used semantically-free <div> only, and gave them different class and id attribute value so I could locate them in my JS codes very easily. But as I said in the previous unit, this is not the best practice. When you use semantically meaningful elements and have a clear structure for your HTML document, you shouldn't need the class and id attributes most of the time. For example, the <p> elements inside <article> and <section> in the following example might represent different types of information in your webpage. But you don't need different id values for the two paragraphs, because you can use different selectors to refer to each paragraph: article > p and section > p.

<article>
<p>
</p>
</article>
<section> <p>
</p>
</section>

In JS, you can use the querySelector() function to retrieve an element or the querySelectorAll() to retrieve all elements with a selector and set their styles accordingly, such as:

var em = document.querySelector('section > p'); // Get the first 'p' element whose parent node is 'section'
em.style.color = 'white'; // Set the font color of the first element as 'white'

One potential setback in the use of querySelector() or querySelectorAll() is that it's going to be a bit slower than other functions like getElementById() if your selector is structurally complicated. So, if what you want to do is really to search for elements by their id attribute, for instance, just use getElementById(). But it's definitely not fatal if you use querySelector() or querySelectorAll() to do a complex search - One would feel something only if they use some outdated computers or Web browsers or you use too many querySelector() for too many complicated queries.

This is also not saying that adding the class or id attribute is a taboo in Web design, or they would have been abandoned for a very long time. For me, I add these attributes in two cases.

First, CSS selector can't do any further for me. You can see two different types of text emphasis in my webpage, such as this and this. For both types, I use the semantically meaningful element <em>, but since both types could be used inside a <p> element, selector is not helpful, and I have to add a class attribute to the type I need to highlight a bit more.

Second, a class or id value is semantically straightforward than a selector. Imaging that you have two buttons in your webpage. One changes the background color of all <div> elements, and the other resets the color. Wouldn't it make more sense to give these two buttons some id attribute specified with changeAll and resetAll respectively? If you use selector, it might be something like the first button in a paragraph and the last button in a paragraph, which is not as semantically transparent as the id attribute.

So it all comes down to how to save your effort in your Web design project by minimizing and simplifying your codes.

Event and interaction

So far the role of our JS codes is pretty active - they are executed automatically after the webpage is fully loaded. But then there's no interaction! What we're hoping for is that there are some elements that could trigger some JS codes when the users do something to them - an interactive Web interface. To have an interaction, you need to start an event, and receive some feedback from the event target. If you and your friend just sit next to each other, then there's no interaction. If you poke your friend, and s/he has no response at all, then there's no interaction. If you poke you friend, and s/he screems at you, then there's an interaction, although probably not a good one.

With the EventHandler in JS, you can set an element in an HTML document to listen to the following events and react accordingly (see this MDN Web Docs page for a full list):

  • scroll - when you scroll
  • keydown - when you press down any key
  • keyup - when you release a key
  • click - when you click a mouse key
  • mousedown - when you click down a mouse key
  • mouseup - when you release a mouse key
  • mouseover - when you move a mouse cursor over the target
  • mouseenter - when you move a mouse cursor into the target
  • mouseleave - when you move a mouse cursor out of the target
  • touchstart - when you touch one our more points on the surface
  • touchend - when you remove one or more touch points from the surface

The listener of a JS event could be added to an element with the addEventListener() function of an element object, in which you need to specify the event the element listens to and the action taken in JS specified in the listener, which is essentially a function (but see this MDN Web Doc page for some complications):

var em = document.querySelector('body'); // Get the 'body' element
em.addEventListener('click', function() { // A function triggered upon a 'click' event alert('clicked!'); }); // It's a statement, so add a semi-colon at the end

If you have different elements that are linked to the same listener, you can create the listener function separately so it can be used in every addEventListener() function:

function clickedMsg() { alert('clicked!'); }
var em = document.querySelector('body');
em.addEventListener('click', clickedMsg);

It is also possible to add multiple event listeners to the same element:

function enteredMsg() { alert('mouse entered!'); }
em.addEventListener('mouseenter', enteredMsg);

When you link an element to a named function in addEventListener(), you are able to remove this event listener later with removeEventListener():

// Remove the 'enteredMsg' listener of the 'mouseenter' event for em
em.removeEventListener('mouseenter', enteredMsg);

Styling the webpage with an interactive interface

Before I explain how this is done, perhaps you could try to accomplish this challenge by yourself?

  1. In an HTML file, create two <div> elements inside <body>,
  2. style both <div> elements using inline styles, specify a width and a height of 5vh to make them a square, and
  3. add two event listeners to each <div> - one changes the background color to any color of your choice when you move your mouse cursor over it, and the other changes the background color back to the default white backgroud.

Select the following section to find a complicated solution:

var div = document.getElementByTagName('div');
var div1 = div[0], div2 = div[1];
// For the first 'div' element
function newDiv1BgColor () { div1.style.backgroundColor = 'red'; }
function oldDiv1BgColor () { div1.style.backgroundColor = 'white'; }
// For the second 'div' element
function newDiv2BgColor () { div2.style.backgroundColor = 'blue'; }
function oldDiv2BgColor () { div2.style.backgroundColor = 'white'; }
div1.addEventListener('mouseenter', newDiv1BgColor);
div1.addEventListener('mouseleave', oldDiv1BgColor);
div2.addEventListener('mouseenter', newDiv2BgColor);
div2.addEventListener('mouseleave', oldDiv2BgColor);

The magical this

The above solution is complicated because in each listener, I have to retrieve all <div> elements, isolate the target <div>, and set the style accordingly. But there's a keyword this in JS, which can save us lots of works. It has different meanings in distinct contexts, but here we focus on its use in an event: this represents the element that receives the target event. Let's validate this first:

document.addEventListener('click', function() { console.log(this); // Print the element that receives this 'click' event, namely 'document'
});

So, how can this help simplify our codes in the complicated solution? We can directly set the style of the target <div> element directly with this - we don't even need two functions to reset the background color since the default color is the same:

var div = document.getElementByTagName('div');
var div1 = div[0], div2 = div[1];
// For the first 'div' element
function newDiv1BgColor () { this.style.backgroundColor = 'red'; }
// For the second 'div' element
function newDiv2BgColor () { this.style.backgroundColor = 'blue'; }
// Same reset function for both 'div' elements
function oldDivBgColor () { this.style.backgroundColor = 'white'; }
div1.addEventListener('mouseenter', newDiv1BgColor);
div1.addEventListener('mouseleave', oldDivBgColor);
div2.addEventListener('mouseenter', newDiv2BgColor);
div2.addEventListener('mouseleave', oldDivBgColor);

Building on the previous practices, our next task to create a more common interactive interface - click on a link and change the style of an element accordingly. Let's first create three <div> blocks and three corresponding button elements in an HTML document:

<!-- Divs with inline styles -->
<div style="width: 5vh; height: 5vh; background-color: black; color: white;" >Block1</div>
<div style="width: 5vh; height: 5vh; background-color: red; color: white;" >Block2</div>
<div style="width: 5vh; height: 5vh; background-color: green; color: white;" >Block1</div>
<button id="block1">Change Block1 Background Color</button>
<button id="block2">Change Block2 Background Color</button>
<button id="block3">Change Block3 Background Color</button>

Apprently, what I want to do here is to click a button and change the background color of the corresponding <div> element to some other color. Before I give you a complex solution (well, just select the section below), could you try yourself and make this happen in your JS file embedded into the webpage (choose your own new background colors)?

var divs = document.getElementByTagName('div');
var buttons = document.getElementByTagName('button');
function changeBg1 () { divs[0].style.backgroundColor = 'red'; }
function changeBg2 () { divs[1].style.backgroundColor = 'green'; }
function changeBg3 () { divs[2].style.backgroundColor = 'black'; }
buttons[0].addEventListener('click', changeBg1);
buttons[1].addEventListener('click', changeBg2);
buttons[2].addEventListener('click', changeBg3);

We can greatly simplify the above complex solution by adding an id attribute to the three <div> elements, whose value partially overlaps with the value of the id attribute of the three <button> element. Then, we can use the magical this keyword to get the id attribute of the clicked button, make changes to this id information to match the id attribute of the targt <div> element, and then change the background color of the target <div> block:

<!-- Add the same id attribute to each div -->
<div id="targetblock1" style="width: 5vh; height: ...
<div id="targetblock2" style="width: 5vh; height: ...
<div id="targetblock3" style="width: 5vh; height: ...
...

// Simplified JS codes
var buttons = document.getElementByTagName('button');
function changBg () { var buttonId = this.id; // Get the id attribute of the clicked button
// Add the string 'target' to the beginning of buttonId to match the id of
// the target div; buttonId = 'block1' -> targetId = 'targetblock1'
var targetId = 'target' + buttonId; var target = document.getElementById(targetId); // Get the target div with targetId
target.style.backgroundColor = 'purple'; // Set a new background color
}
buttons[0].addEventListener('click', changeBg);
buttons[1].addEventListener('click', changeBg);
buttons[2].addEventListener('click', changeBg);

The function of the simplified codes is a bit different, because it could not set a different background color for each <div>, but we will get there eventually. The main point of the above demo is to show how to use the id attribute shared by <div> and <button> to do the trick more easily.

More on the nature of an event

Credit: Paul

Every listener function could receive an event object passed from the event handler that comes with every single detail about the triggered event, and we can examine these details in our Developer Tools console:

function changBg (event) { // "event" is just a variable name, so you can change it if you want console.log(event); }

In this object, you would see some keys that includes some crucial information, such as target (the element on which the event is triggered) and path (the nodes passed in the DOM to reach the target). The former target is useful since sometimes multiple elements could be tied to the same listener as in the example above, and you might want to do different things based on different target elements (again, we will get back to this very soon). The latter path is more crucial at this point, because it shows how your Web browser tries to locate the target element - starting with the root node, and then gradually descend to the target node/element. This of course makes sense because if you click on the <button> element in the following structure, it also means you click the <div> and its child <p>, too:

<div> <p> This is a paragraph.
<button>I'm a button</button>
</p>
</div>

Now, supposed that in the above HTML document, both of the <button> element and the <p> element are set to listen to a click event, clicking on the button element will also fire the clicking event of the <p> element. This is called bubbling, as your Web browser starts with the target element and gradually go back to check every ancestor to see if it listens to the same event (this is an oversimplified fact; see this MDN Web Docs page):

// You can read this line, right?
document.getElementsByTagName('p')[0].addEventListener('click', function(event) { this.style.backgroundColor = 'red'; });
// You can also read this line, right?
document.getElementsByTagName('button')[0].addEventListener('click', function(event) {
alert('Clicked!'); });

Again, this makes sense because you are clicking on the <button> element inside a <p> element. But most of time you don't want this default behavior - you just want the button to do its own thing. So, you need to stop the event from propagating with the stopPropagation() function of the event object passed to the listener function:

document.getElementsByTagName('button')[0].addEventListener('click', function(event) {
event.stopPropagation(); // Stop the clicking event from bubbling to its ancestor
alert('Clicked!');
});

I could imagine that sometimes the default bubbling effect is useful, but more often it creates bugs for newbies, so you need to pay attention to it when adding event listeners to your webpages.

If...else...

Credit: Jocelyn Wallace

To have your JS codes interacting with the HTML interface in the previous sections, you need to write complex codes, such as creating separate listener functions that are essentially similar. For instance, if you need to have a different background color for <div> and <p> respectively after clicking on a different button, then you need two listener functions in which the JS code specifies a different background color. But why can't we have just one listener function in which the JS code gives an element a different background color dependending on which element it is? Of course we can, if we use the if...else... statement in our JS code:

if(condition) { // Statements executed when the condition is met } else { // the "else" part is optional // Statements executed when the condition is not met }

With the above syntax, our JS codes would tell a Web browser to execute a code block when a condition is met, or the other code block when it is not met. Before we break this down, we need to know one more data type in addition to number, string, array, and object, that is, a boolean value. A boolean value could be either true or false, and there's nothing in between. When I say a condition is met, it means the condition is true, or it is false. In JS, a condition is generally a comparison of two values using some operators:

  • A > B - A is numerically higher than B
  • A >= B - A is numerically equivalent to or higher than B
  • A < B - A is numerically lower than B
  • A <= B - A is numerically equivalent to or low than B
  • A == B - A is equivalent to B, regardless of their data type
  • A != B - A is not equivalent to B, regardless of their data type
  • A === B - A is equivalent to B in terms of both value and data type
  • A !== B - A is not equivalent to B in terms of both value and data type

The first four comparisons should be rather understandable, so let's put our focus on the last four comparing the equivalence of two values. In addition to the use of ! to represent not in a condition, the major difference lies in whether an extra equal sign = is used to compare values by taking their data type into consideration. The difference matters the most in cases where numerical values and digit characters are compared as in the following two lines:

console.log('5' == 5); // You get a "true"
console.log('5' === 5); // You get a "false"

In the first comparison, you get a true value when a character 5 is compared to a numerical 5 using == - since the two values are compared with no regard to their data type, automatical type conversion applies to make the two values equivalent to each other. By contrast, when the same values are compared using ===, the type conversion does not apply, and the result is a false since they are of different data types. In other words, comparisons using === are more conservative than those using ==. I personally always choose the more conservative option since it lowers the risk of having bugs in your codes. If I need to convert strings into numerical values, I would do it myself manually using the Number() or parseInt() function.

Now back to our if...else... statements. We use the previous example with three <div> elements, and three corresponding <button> elements sharing the same id attribute with the <div> elements:

<div id="block1" style="width: 5vh; height: ...
<div id="block2" style="width: 5vh; height: ...
<div id="block3" style="width: 5vh; height: ...
<button id="block1">Change Block 1</button>
<button id="block2">Change Block 2</button>
<button id="block3">Change Block 3</button>

We would associate all three <button> elements to the same listener function for an clicking event, but only when the id attribute is block1, the corresponding <div> element would have a new background color:

var buttons = document.getElementByTagName('button');
function changBg () { let buttonId = this.id; // Get the id attribute of the clicked button
let target = document.getElementById(buttonId); // Get the target div with the same id
if(buttonId === 'block1') { // Change the background color only for a specific div target.style.backgroundColor = 'purple'; }
}
buttons[0].addEventListener('click', changeBg);
buttons[1].addEventListener('click', changeBg);
buttons[2].addEventListener('click', changeBg);

As mentioned earlier, the else part in the if...else... statement is optional, so in the above example we only make it clear what to do when the condition buttonId === 'block1' is true. Next, we add the else part to specify the background color for a corresponding <div> element when the id is not block1:

...
function changBg () { let buttonId = this.id; // Get the id attribute of the clicked button
let target = document.getElementById(buttonId); // Get the target div with the same id
if(buttonId === 'block1') { // Change the background color only for a specific div target.style.backgroundColor = 'purple'; } else { // If buttonId is not block1, change the background color to 'yellow' target.style.backgroundColor = 'yellow'; }
}
...

But maybe you actually want to style every single <div> element with a different color depending on which button is clicked. In this case, you could choose to add another if condition after else for another comparison if the result of the previous comparison is false:

...
function changBg () { let buttonId = this.id; // Get the id attribute of the clicked button
let target = document.getElementById(buttonId); // Get the target div with the same id
if(buttonId === 'block1') { // Change the background color only for a specific div target.style.backgroundColor = 'purple'; } else if(buttonId === 'block2') { // When the previous condition is 'false', check another condition target.style.backgroundColor = 'yellow'; }
else { // Use another background color if all previous conditions are 'false' target.style.backgroundColor = 'grey'; }
}
...

In the above example, I tested all the conditions one by one until one of them is true, or do everything in the else block when none of them is true. Since I only have three elements that require a different background color, I don't need to add another condition when the first two are not true - the else part itself represents the third and the only possible choice - block3.

!!!!!!!!!!!!

What the heck is this subheading? You might ask. In this section, I've shown to you that the exclamation mark ! means not in a comparison. But there is also another role it plays in a condition. When you put a ! immediately before the name of a variable, the result is a boolean value that tells you if the variable is null, undefined, 0, NaN (Not a Number), or '' (empty string):

var valueVar = 5, nullVar = null;
console.log(!nullVar); // You get a 'true'
console.log(nullVar); // You get a 'false' (do you know why?)
console.log(!valueVar); // You get a 'false' (do you know why?)

How is this useful in styling? Well, when you use functions like querySelector() or querySelectorAll() to retrieve HTML elements, there's no guarantee that you will get any. If there's no HTML element that meets your criteria, these functions would return a null value, which literally just means...nothing. If this is the case, then you don't need to do any action related to styling and HTML element. For example, let's add a fourth button with a weird id attribute in the earlier example, and we link it to the same listener function:

...
<button id="iamweird">I'm weird</button>
...
buttons[3].addEventListener('click', changeBg);

Now, we know that if you click on this button, you JS code won't be able to find a <div> with the button's id. The target variable will thus be assigned a null value returned from getElementById(). Therefore, we can add a new if...else... after this line to judge if the variable target has a null value, and style the target element if it's not:

...
function changBg () { let buttonId = this.id; // Get the id attribute of the clicked button
let target = document.getElementById(buttonId); // Get the target div with the same id
if(!target) { // Show a pop-up message if no element is found
alert('No such element: ' + buttonId);
}
else { // The old if...else... statement for styling divs }
}
...

Logical operators AND and OR

Sometimes in an if...else... statement, you want to have an action executed when one or all of the conditions are true, and you need the logical operators || (OR) and && (AND) to do this job for you. For instance, in our ealier example, if you need to change the background color when the id of a <div> element is block1 or block3, then you can have the following conditions combined using the logical operator || in the if...else... statement:

if(buttonId === 'block1' || buttonId === 'block3') { // Conditions combined with the OR operator // Style the background }

In the above example, the result is true if one of the conditions is true. Alternatively, we can check if we have successfully retrieved a target element and whether the element has a specific id attribute with the logical operator && in the same if...else... statement:

if(target && buttonId === 'block3') { // Conditions combined with the AND operator // Style the background }

In the above example, the result is true only when both conditions are true - The variable is neither null nor undefined, and the element id is block3. In JS, it is almost impossible to make things happen easily without if...else... statements, and I believe you will find it useful very soon.

Loop

Credit: Science is Beauty

Even after we have learned so much about JS, we can still do very little about the interaction between JS codes nad the HTML document. The primary reason is that our JS codes can only target at one HTML element at a time. If there are seven <div> elements we need to style in JS, then we wouldn't be able to complete the task without writing same JS statements for each of the elements. In addition, what if we want to use the same styling JS function to deal with separate groups of elements whose quantity is variable? Without knowing how many elements there are, we are not able to count how many times we need to repeat the same JS statements for each element. That's why we need loop, which repeats a code block for a specific number of times. It is possible to create a loop with either for or while, and let's start with the first one, which has the following syntax:

for(loop variable ; loop condition ; loop action) { // For loop code block }

The for loop includes three core statements separated with a semi-colon ; (you can surely guess why this is the case...?). In the first statement, you can create a variable that is only accessible in this loop, such as var i=0. This variable often functions as a counter in this loop (1st loop, 2nd loop, 3rd loop, etc.). The second statement is a condition that we have seen in the previous section, which determines whether a loop should continue (when the condition is true) or not (when the condition is false). For instance, with the condition i < 10, the loop would continue until the variable i is assigned a value larger than 10. Finally, the last statement will be executed at the end of the loop, which is usually used to updated the counter variable declared in the first statement, such as i++ (i increases by 1) or i-- (i decreases by 1). By this point you probably can see how a for loop works - define a counter, set a condition involving the counter, and update the counter until the condition is no longer true, as in the following example:

// A loop that is repeated 10 times
for(var i = 0 ; i < 10 ; i++) { console.log(i); // Print the value of the counter }

The most common bug in coding a JS loop is to create an infinite loop, usually by accident. That is, the loop condition in never false, so a loop never stops. Here are two possible examples:

// The counter never reaches the threshold
for(var i = 0 ; i < 10 ; i--) { console.log(i); }
// The condition involves a wrong variable
var j = 0;
for(var i = 0 ; j < 10 ; i++) { console.log(i); };

Alternatively, your loop could never start since the condition is false right at the beginning:

// The condition is never met
for(var i=0 ; i > 10 ; i++) { console.log(i); }

As mentioned earlier, we want to style a group of elements in a loop, which requires you to have the following sense:

  • You will use getElementsByTagName(), getElementsByClass(), or querySelectorAll() to get a group of elements.
  • These elements will be stored in an array return by the aforementioned functions.
  • You loop through this array and style each element until the end of this array (your loop condition).

Let's now have three <div> siblings, and use a <button> element that listen to a click event to style all three elements:

var button = document.getElementsByTagName('button')[0]; // Get one and the only 'button' element
button.addEventListener('click', function() { let divs = document.getElementsByTagName('div'); // Get all 'div' elements returned as an array
let divNum = divs.length; // Get the number of data entries in the 'divs' array
// Counter starts with zero, increases by 1 at the end, loop stops when i === divNum
for(let i = 0 ; i < divNum ; i++) { let div = divs[i]; // Get the target div by the index number
div.style.backgroundColor = 'red'; // Style it
}
});

In the above example, we usually name the counter variable something like i since it represents the index number in an array, and it starts with 0 because it represents the first data entry in an array. Since we need to loop through the end of the array, we need to know how many data entries there are in the array, which can be obtained with the length property of an array object. In the condition, we need the operator < rather than = since the index of the last data entry in an array is always arrary length minus 1.

If we include if...else... statements in a for loop, it will be possible to make our styling function more flexible to deal with subgroups of elements in the same. For example, perhaps we don't want to change the background color of the second <div> element of the three <div> elements we had above, so we add an id attribute to it with a value nostyle:

<div id="nostyle">Div 2</div>

Then, we would use the if statement to style a <div> element only when it's id attribute is not nostyle:

...
for(let i = 0 ; i < divNum ; i++) { let div = divs[i]; // Get the target div by the index number
if(div.id !== 'nostyle') { // Style the element only when the id attribute is not "nostyle" div.style.backgroundColor = 'red'; }
}
...

Hit the "break"

Sometimes you just need to loop through an array of elements to search for a very specific element. If you get lucky, you would find it in the first position of an array, in the very first loop in for. Then, you don't need to run the rest of the for loop, and you want to use the keyword break to discontinue the for loop. In a nutshell, we need an if...else... statement for a condition that is met when a specific element is found, and some statements to style this element and discontinue the loop afterwards. Let's start with our HTML part, in which we have three <div> elements specified with a distinct id value and a <button> element specified with one of the three id values:

<div id="div1">Div 1</div>
<div id="div2">Div 2</div>
<div id="div3">Div 3</div>
<button id="div2">Style Div 2</button>

Now, let's add an event listener to the <button> element for a clicking event, and we get the id of the <button> element, search for the <div> element with the same id, style it, and discontinue the loop:

...
let buttonId = this.id; // Get the id of the clicked button
for(let i = 0 ; i < divNum ; i++) { let div = divs[i]; // Get the target div by the index number
if(div.id === buttonId) { // Style the element only when there's a match in id div.style.backgroundColor = 'red';
console.log(i); // Print out the counter
break; // Discontinue the loop
}
}
...

The Web browser in your modern computer won't be overwhelmed by a simple loop even if it is unnecesary to complete it. But our goal is always to create an efficient program that saves as much computation resources as possible. When your program grows further, every redundancy would matter. Speaking of efficient, there's in fact an even much simpler solution using querySelector(), but I'll leave this to you, since my point is to demonstrate the use of break in the for loop.

A for loop can also be used to iterate through an object, mostly for retrieving the keys of the object:

var div = document.getElementsByTagName('div')[0]; // The first div element of the document
for(let key in div) { // Declare a variable "key" for each key string of the object in each iteration console.log(key); // Print the key
console.log(div[key]); // Print the value of each key
}

do...while...

Another type of loop in JS can be created with the do...while... statement, which could simply be interpreted as do something while the condition is met:

do { // Some statements executed when the condition is met }
while(condition);

The idea is similar to the for loop - as long as the condition is met (=true), the same statement(s) will be repeated over and over, and of course we can also use break to discontinue the loop manually. Let me translate the previous for loop that attempts to identify a <div> element with a specific id attribute into a do...while... loop:

...
var buttonId = this.id; // Get the id of the clicked button
var i = 0; // Declare a counter variable
do { let div = divs[i]; // Get the target div by the index number
if(div.id === buttonId) { // Style the element only when there's a match in id div.style.backgroundColor = 'red';
console.log(i); // Print out the counter
break; // Discontinue the loop
}
i++; // Increase the counter by 1
}
while(i < divs.length);

There is one thing worthnoting here. The counter variable i is declared outside the loop, so its value can be accessed by other parts of your JS codes in the same file, which could lead to some potential bugs. One possible solution is to avoid using counter in do...while... to iterate through an array. Instead, we can always check the first entry of an array, and get rid of it from the array if there's no match, and the second entry would "drop" to the first place, just like the animation above demonstrating the Japanese game Daruma Otoshi. This way, we can always just check the first entry without the need for a counter, until all the entries are dropped or a break statement is executed. First, we will need the splice() function of an array object, which requires two parameters - index (where to start dropping data entries) and number (how many data entries to be dropped):

Array.splice(index, number)

The following examples give you some overview about how it is used:

Array.splice(0, 1); // Start from the first position and drop one entry (drop the first entry)
Array.splice(2, 3); // Start from the third position and drop three entries (drop 4th, 5th, and 6th entries)

Every time you drop an entry from an array object, its length key decreases by 1. So when there is no more entry in the array object, its length key would be 0, and that's when the iteration through the array object should be over. Taken altogether, the for loop can be translated into a do...while... loop without a counter is as follows:

...
var buttonId = this.id; // Get the id of the clicked button
do { let div = divs[0]; // Always get the first div in the array
if(div.id === buttonId) { // Style the element only when there's a match in id div.style.backgroundColor = 'red';
break; // Discontinue the loop
}
divs.splice(0, 1); // Drop the first div object in the array
}
while(divs.length > 0); // Repeat when there's at least one data entry in the array object

So, for loop or do...while... loop? I mostly use the former, because its syntax is pretty straightforward, and it's useful when you need to iterate through an object. Some might also have the question about which one is computationally more efficient. I have a vague memory that do...while... is faster than for, everything else being equal. But after googling information a bit, the performacne seems to vary across Web browsers. So do you own research, and choose the one the works most efficiently for most of your users. It's always like this in the world of computer programming.

Form, submit, and reset

I know what you are probably thinking right now: This tutorial is already beyond basics. But what I mean by basics is not simple; it's all about the basic concepts that you will be using frequently in Web design. And please allow me to add one more part for you to create an interactive website using the HTML elements <form> and <input> as well as JS codes. Let's start with <input>, which represents an object that collects some data input into the webpage. Depending on the type attribute, an <input> element will be presented as a different object (see this W3CSchool page for a full list):

  • text - A field that you can fill in some texts
  • number - A field that you can fill in some number
  • color - A field that you can choose a color value
  • button - A simple button, pretty much like <button>
  • submit - A button used to submit the data of a <form>
  • reset - A button used to reset all fields in a <form>

We can try the text type and style HTML elements by their tag name input into the text field. Let's create three <div> elements and three <p> elements with one <input> element:

<div>Div1</div>
<div>Div2</div>
<div>Div3</div>
<p>P1</p>
<p>P2</p>
<p>P3</p>
<input type="text" id="styleTag" placeholder="enter a tag name..." />

In the <input> element, I add a placeholder attribute, whose value would fill in the field before we type anything in it and thus provides a useful hint. Now we need some JS codes to capture the keypress event of the <input> element, and try to style all the elements of a tag name in the <input> element every time we type something in it:

var inputEm = document.getElementById('styleTag'); // Get the 'input' element
var targetTag; // The value of the 'targetTag' variable will be retrieved from the 'input' field
function styleEm () { // The listener function that styles all found elements targetTag = inputEm.value; // The value of 'targetTag' is the texts from the 'styleTag' input field
let Ems = document.getElementsByTagName(targetTag); // Get all elements by 'targetTag'
// You MUST check whether 'Ems' is 'null' or not, but I skip this step here
for(let i=0 ; i < Ems.length ; i ++) { // Iterate through the element array let currentEm = Ems[i];
currentEm.style.backgroundColor = 'red'; // Style every single selected element
}
}
// Every time you press a key in the 'styleTag' element, the listener function is activated
inputEm.addEventListener('keypress', styleEm);

This function currently can style all elements that are found to have the target tag, but it cannot reset the style if a new target tag is obtained. I want you to make this possible by modifying the JS codes in the above (Hint: You need to restore the default style of the current target tag before using a new target tag to style the other set of elements).

Now we can try to demonstrate the full function of a <form> element by including the text field and a submit and reset button in it:

<form> <input type="text" id="styleTag" placeholder="enter a tag name..." />
<input type="submit" id="submitTag" value="Submit" />
<input type="reset" id="resetTag" value="Reset" />
</form>

For the submit and reset types of the <input> element, the attribute value determines their texts. Now, if we type some texts in the text field, and hit the reset button, the texts will be cleared completely. If an <input> field has a default value attribute, then clicking on the reset button will reset the <input> field back to its default value. If you place the reset button outside the <form> element, the default resetting behavior doesn't work:

<form> <input type="text" id="styleTag" placeholder="enter a tag name..." />
<input type="submit" id="submitTag" value="Submit" />
</form>
<input type="reset" id="resetTag" value="Reset" /> <!-- This does not work -->

Now let's try the submit button - clicking on it (or hit the enter key in the text input field) will reload the entire page, which is the result of submitting of the entire form for data processing by default (just like the form you submit when you sign up for an account in a website). But this is not our case; we don't need to submit a form to somewhere else for data processing. We just want to input some tag name to style HTML elements. So, we need to prevent this default behvaior from happening. Do you still remember the event object that will be passed to a listener function when an event is triggered? We need to use the preventDefault() function of this object to stop the default behavior of a submit button:

var submitButton = document.getElementById('submitTag'); // Get the submit button
submitButton.addEventListener('click', function(event) { // Add a 'click' event listener event.preventDefault(); // Stop the default behavior of the button });

The preparation work is now completed. We can further add the JS codes required to get the input texts as the tag name, and style the corresponding elements. After we retrieve the text input element in the JS code, we can retrieve texts of the element with the value property of the element:

...
submitButton.addEventListener('click', function(event) { // Add a 'click' event listener event.preventDefault(); // Stop the default behavior of the button let targetTag = document.getElementById('styleTag').value; // Get the value of the input field // Complete the rest of the styling codes by yourself... });

Now that we have completed the styling part, we probably want the reset button to do a better job. Instead of just resetting the text input fields back to their default value, we hope to change the styled elements back to its default background color, too. In our earlier challenge, you have already done something very similar, but this time you need to do it in an event listener linked to the reset button:

var resetButton = document.getElementById('resetTag'); // Get the 'reset' button
resetButton.addEventListener('click', function() { // Add a 'click' event listener // Try to complete the codes for resetting the background color by yourself! });

Before I conclude this unit, let me leave this main challenge to you:

  1. Find six pictures that can be categorized into two groups.
  2. Insert each of them into a webpage using an <img> element, and add the group label as their class attribute.
  3. Create a text input field, a submit button, and a reset button in a <form> element.
  4. In the text input field, when you enter a group label, all <img> elements with the corresponding class attribute remain non-transparent, whereas others become half-transparent.
  5. The CSS property you need in this challenge would be opacity - 1 = non-transparent, 0 = fully transparent.
  6. Remember, to set a CSS property in JS codes, the value of the property should be enclosed inside '', just like '1'.

For the next unit...

You have walked even further in this journey of Web design, and you have the wherewithal to complete your website projects. So, what I need to see before the next unit is:

  • Complete the template of your personal website!

All Copyright Reserved; Tsung-Ying Chen 2021