Getting Started With JavaScript 20205 - Converted
Getting Started With JavaScript 20205 - Converted
You are reading this e-book in a file format (EPUB or Mobi) that
makes the book content adaptable to the display options of your
reading device and to your personal needs. That’s a great thing; but
unfortunately not every device displays the content in the same way
and the rendering of features such as pictures and tables or
hyphenation can lead to difficulties. This e-book was optimized for the
presentation on as many common reading devices as possible.
If you want to zoom in on a figure (especially in iBooks on the iPad),
tap the respective figure once. By tapping once again, you return to
the previous screen. You can find more recommendations on the
customization of the screen layout on the Service Pages.
Table of Contents
Notes on Usage
Table of Contents
1 Introduction
1.1 What Can JavaScript Do?
1.2 What Can JavaScript Not Do?
1.3 Browsers and Mobile Browsers
1.4 ECMAScript
1.5 Structure of This Book
1.6 First Example with HTML and CSS
1.6.1 Output of the Program
1.6.2 HTML File
1.6.3 UTF-8 Encoding
1.6.4 Responsive Web Design
1.7 Some Special Characters
1.8 JavaScript in the Document
1.9 JavaScript from an External File
1.10 Comments
1.11 No JavaScript Is Possible
2 Basic Principles of
Programming
2.1 Storing Values
2.1.1 Formatting Strings
2.1.2 Naming Rules
2.1.3 Input and Output of Strings
2.1.4 Storing Numbers
2.1.5 Storing Truth Values
2.2 Performing Complex Calculations
2.2.1 Calculation Operators
2.2.2 Combined Assignment
2.2.3 Entering Numbers
2.2.4 Number Systems
2.3 Different Branches in a Program
2.3.1 Branches with if
2.3.2 Requesting a Confirmation
2.3.3 Linking Multiple Conditions
2.3.4 Linking and Assigning
2.3.5 Checking the Input of Numbers
2.3.6 Checking the Value and Type
2.3.7 Priority of Operators
2.3.8 Branches with switch
2.4 Repeating Program Sections
2.4.1 Loops with for
2.4.2 Loops and Tables
2.4.3 Loops and Fields
2.4.4 Loops with while
2.4.5 Loops with do … while
2.4.6 A Game for Memory Training
2.5 Finding and Avoiding Errors
2.5.1 Developing a Program
2.5.2 Finding Errors Using onerror
2.5.3 Exception Handling Using try ... catch
2.5.4 Throwing Exceptions Using throw
2.5.5 Debugging a Program
2.6 Custom Functions
2.6.1 Simple Functions
2.6.2 Swapping Out Functions
2.6.3 Functions with Parameters
2.6.4 Changing Parameters
2.6.5 Functions with Return Value
2.6.6 Destructuring Assignment
2.6.7 Evaluation Using Short Circuit
2.6.8 Any Number of Parameters
2.6.9 Default Values for Parameters
2.6.10 The Validity of Variables
2.6.11 Recursive Functions
2.6.12 Anonymous Functions
2.6.13 Callback Functions
3 Custom Objects
3.1 Objects and Properties
3.2 Methods
3.3 Private Members
3.4 Setters and Getters
3.5 Static Members
3.6 Static Blocks
3.7 Reference to Nothing
3.8 Object in an Object
3.9 Inheritance
3.10 Operations with Objects
3.10.1 Access Operators
3.10.2 Creating and Comparing References to Objects
3.10.3 Checking Instances
3.10.4 Determining a Type
3.10.5 Checking a Member
3.10.6 Objects and Functions
3.10.7 Deleting Properties
3.11 Copying Objects
10 Three-Dimensional Graphics
and Animations Using Three.js
10.1 First 3D Graphic
10.1.1 3D Coordinate System
10.1.2 Structure of the Program
10.1.3 3D Object with Geometry and Material
10.1.4 Camera
10.1.5 Canvas and Scenes
10.2 Moving the Camera
10.3 Animation
10.4 Various Shapes
11 jQuery
11.1 Structure
11.2 Selectors and Methods
11.3 Events
11.4 Animations
11.5 Example: Sinusoidal Movement
11.6 jQuery and Ajax
12 Mobile Apps Using Onsen UI
12.1 Structure of a Page
12.1.1 First Page
12.1.2 List of Elements
12.1.3 Table with Items
12.2 Elements within a Page
12.2.1 Icons and Floating Action Buttons
12.2.2 Standard Dialogs
12.2.3 Input Fields
12.2.4 Selection Fields
12.2.5 Selection from a Number Range
13 Mathematical Expressions
Using MML and MathJax
13.1 Basic Elements
13.2 Parentheses and Tables
13.3 Summarizing Expressions
13.4 Fractions
13.5 Mathematical Symbols
13.6 Dynamically Generated Expressions
14 Sample Projects
14.1 Financial Investment
14.2 Fitness Values
14.3 Fun Run
14.4 Solitaire
14.5 Concentration
14.6 Snake
B The Author
Index
Service Pages
Legal Notes
Materials for This Book
The following resources are available for you to download from this
book’s webpage:
All sample programs
All exercises with solutions
Bonus chapters
Go to www.rheinwerk-computing.com/5875 and scroll down to the
Product supplements. You will see downloadable files along with
brief descriptions of their contents. Click the Download button to start
the download process. Depending on the size of the file (and your
internet connection), you may need some time for the download to
complete.
1 Introduction
What is JavaScript, what can and can’t I do with it, and how do I
integrate it into my website? This chapter provides answers to
initial questions like these.
JavaScript also offers many elements that may be familiar to you from
other programming languages, such as loops for a quick repetition of
program parts, branches for the different handling of different
situations, and functions for breaking down a program into
manageable components. You also have a variety of objects at your
disposal. Using the Document Object Model (DOM), you can access
all elements of your webpages in JavaScript so that you can change
them dynamically.
1.1 What Can JavaScript Do?
The programs are available within internet pages, and they contain
JavaScript code along with Hypertext Markup Language (HTML) code.
It’s important to note here that JavaScript provides additional
possibilities and aids that HTML doesn’t have.
JavaScript can only store a small amount of data in the browser during
use. It can’t cause any damage there.
1.3 Browsers and Mobile Browsers
Internet pages, with or without JavaScript, are received by different
browsers on different operating systems on different end devices and
implemented during use.
Many elements of the newer standards are also useful for beginners
and can be found in the relevant sections of this book. I will introduce
each of them separately.
1.5 Structure of This Book
First of all, a note for my own behalf: I would like to thank the team at
Rheinwerk Germany, especially Anne Scheibe, and Rheinwerk US,
especially Megan Fuerst, for their help in creating this book in its
German and English editions.
I present each topic in this book with a brief description of the theory
behind it; a meaningful screenshot; a complete, executable sample
program; and a detailed practical explanation. The screenshots were
taken in the Google Chrome browser, either on a PC with Windows 11
or on an Android smartphone. This way, you get a quick introduction
to each topic, and you are not forced to place individual lines of code
in a suitable context first to consider their effect. You’ll find all sample
programs in the downloadable materials for this book, which you can
access at www.rheinwerk-computing.com/5875.
You’ll also find references to exercises in this book, and you can find
those tasks in the bonus chapters that are also included in the
downloadable materials. The exercises allow you to test your
knowledge, and you’ll find a solution to each exercise in the
downloadable materials.
The contents of this book build on each other in small, clear steps.
This has the advantage that the prerequisites for each topic are
clarified for you in advance, but it has the disadvantage that you need
to actually read the book from cover to cover. If you simply open it at
any point, you can’t assume that all the details will be explained there,
as they may have been included in an earlier section.
<p>A list:</p>
<ul>
<li>First entry</li>
<li>Second entry</li>
</ul>
<p>A table:</p>
<table>
<tr>
<td>Cell A</td>
<td>Cell B</td>
</tr>
<tr>
<td>Cell C</td>
<td>Cell D</td>
</tr>
</table>
</body>
</html>
The entire document is in the html container from the start marker
<html> to the end marker </html>. The start marker html has the
attribute lang with the value en-us, which indicates that the text of the
document is written in US English. The html container contains a head
container with information about the document and a body container
with the actual document contents.
In the head container, you’ll first find a title container, which provides
the content for the title bar of the browser. Metadata about the
document can also be found here. In this example, you can see that
this is an HTML document that uses the widely used 8‐bit UCS
Transformation Format (UTF-8) character set (Section 1.6.3), which
contains many special characters, including German umlauts, for
example.
You can use the link marker and the rel and href attributes to
include an external CSS file for formatting the document. In this
example, the file is js5.css, which is located in the same directory as
the first.htm file. The CSS file is explained in more detail in
Section 1.6.4.
The font size within table cells (marking td) is separately set to 11
points. The background color of table cells is set to a light gray by
using #e0e0e0, and this makes the cells slightly darker than the
background of the document. By using the padding specification, you
can adjust the inner distance between an element and its surrounding
element. Here, a distance of 5 pixels is set around the text content of
a table cell to the edge of the table cell.
The following HTML elements are used to design forms. You will get
to know them in Chapter 4. Their size is also adjusted depending on
the media.
General input, single-line text field (input)
Selection using a menu (select)
Selection using radio buttons (input type=radio )
Checkbox (input type=checkbox )
Selection of a color (input type=color )
Setting a number in a range (input type=range )
Multiline text field (textarea)
1.7 Some Special Characters
You use the following program to output some special characters. You
can also enter some special characters directly using the keyboard,
and all of them can be output using so-called entities.
Note
In this example and many of the following examples, the beginning
of the document is omitted to save space. It’s only printed if it
contains additional information, and the end of the document is
presented in a more compact form.
You can insert the special characters in the first line directly into your
document using the (>) (greater than), (&) (ampersand), ($) (dollar),
and (@) (at) keys.
You should output the < (less than) character in the fourth line using
the < entity; otherwise, the associated document won’t be
validated as an HTML document.
You can embed JavaScript in any number of places in the head or body
of an HTML document. In each case, a script container is required.
This container begins with <script> and ends with </script>.
I have used the font-weight CSS property with the bold value to set
part of the text in bold.
Useful Information
Observe the correct notation of the statements when
programming. Unlike in HTML, browsers don’t forgive errors in
JavaScript.
JavaScript distinguishes between uppercase and lowercase
letters. You’ll not be successful with the document.Write(...)
statement, as the Write() method with an uppercase W doesn’t
exist.
You can also write multiple statements in one line. The main thing
to remember is that there’s a semicolon at the end of each
statement.
In Figure 1.4, you can see various parts of the document, some of
which originate from HTML and some from JavaScript.
Figure 1.4 JavaScript within a File
1.9 JavaScript from an External File
You can save program parts that you want to use in several
JavaScript programs in an external file. You can easily access the
code of such an external file by integrating the file into your program.
Here’s an example:
...
<body>
<script src="external_file.js"></script>
<script>
document.write("<p>This comes from external.htm</p>");
</script>
</body></html>
The first script container is empty, but the src attribute is noted with
the external_file.js value. This integrates the code from the relevant
file into the external.htm file. The external_file.js file only contains the
following code:
document.write("<p>This is from external_file.js</p>");
In Figure 1.5, you can see the two paragraphs that are generated from
the merged program using the document.write() method.
Figure 1.5 Additional JavaScript from an External File
Please note that there’s no script container in the external file. The
name of this file can have any extension, but the js ending has
become established as a convention.
The jQuery library (see Chapter 11) and other large JavaScript
libraries with their many useful functions are integrated into
applications in this way.
1.10 Comments
Comments are used to describe the individual parts of your programs,
and they make it easier for you and others to understand those
programs. Let’s look at an example:
...
<body>
<!-- This is a comment
in the HTML section -->
<p>A paragraph from the HTML section</p>
<script>
/* This is a comment across multiple lines
in the JavaScript section */
document.write("<p>A paragraph from the JS section</p>");
// A short comment, only to the end of the line
</script>
</body></html>
Note
Long after you create a program, you’ll often want to look at or
expand it. Then, you’ll be grateful for every line of commentary you
find in it. For the same reasons, it’s also highly recommended that
you write clear, easy-to-read programs.
1.11 No JavaScript Is Possible
As already mentioned in Section 1.2, there may be individual cases
where JavaScript has been switched off in the browser. Since
JavaScript can’t switch itself on, what can you do?
You can recognize whether it’s switched on or not. If it’s not switched
on, you can either offer a simple version of the page in pure HTML or
indicate that the use of the page in question requires JavaScript to be
switched on.
Here’s an example:
...
<body>
<script>
document.write("<p>JavaScript is running here</p>");
</script>
<noscript>
<p>JavaScript is not running here<br>
Please switch it on</p>
</noscript>
</body></html>
Within the noscript container, you can note down text and HTML
markers in the event that JavaScript is switched off.
If JavaScript is enabled, only the statements from the script container
will be executed, and the page will then look like the one shown in
Figure 1.7.
Figure 1.7 JavaScript Is Enabled
You can deactivate JavaScript once for testing purposes, and the
necessary procedure is explained in the following text using the
example of the Google Chrome browser. Call the menu using the
three dots at the top right, select the Settings menu item, and enter
the term “JavaScript” in the search window. You’ll find the Site
settings in the search results. There, you’ll find the JavaScript area,
with the option Sites can use JavaScript by default. Select the other
option Don't allow sites to use JavaScript.
Then, when you call a file that contains JavaScript, you’ll see an icon
on the far right of the browser address bar with the information that
JavaScript has been blocked on this page. Click on the icon to open a
dialog box. There, you can activate JavaScript for this page. The
Manage button also takes you directly to the previously mentioned
options for switching JavaScript on and off.
This section deals with the storage of texts, numbers, and truth values
using variables, which can be changeable or unchangeable.
Unchangeable variables are also referred to as constants. In this
section, you’ll also get to know two dialog boxes for simple inputs and
outputs.
let text4;
document.write("Text: " + text4 + "<br>");
Note
Variables can also be declared using the var keyword, but these
variables can be changed more easily by mistake and are more
difficult to control. This is a particular disadvantage with longer
programs that consist of a large number of program sections, so the
use of these variables can result in program errors more quickly.
Instead, you should always use the let and const keywords, as is
done in all the examples in this book.
Both methods use strings and cause the program to stop in its course,
after which, the program only starts running again after an entry has
been made or the message has been confirmed. This gives you the
opportunity to interact, request an input, or initiate the reading of an
output.
Here’s an example:
...
<body>
<script>
const input = prompt("Input:\nPlease make an entry:",
"Good morning");
alert("Your input:\n" + input + "\nThank you very much");
</script>
</body></html>
As soon as you start the program, the prompt shown in Figure 2.2
appears.
The prompt() method creates the prompt with the text that comes from
the first string, and the \n control character creates a line break within
a dialog box. The letter n stands for new line.
If you change the input to This is my input , a message like the one
shown in Figure 2.3 is displayed due to alert().
The \n control character is also used in this dialog box to create a line
break, and the output is combined into a longer string using the +
operator.
Note
If a statement is very long, you can write it in several lines in the
editor. You can implement a line break in many places within a
statement, but not in the middle of a keyword and not within a string.
Note
Finally, a number is assigned that has many digits both before and
after the decimal point. ECMAScript 2021 introduces the _
(underscore) character as a numeric separator, which is an input and
reading aid for numbers with many digits and is already used by all
modern browsers. It makes sense to insert the underscore after every
three digits.
The output of the program is shown in Figure 2.4.
Figure 2.4 Output of Numbers
Let’s look at the program code for the output using document.write().
In the first line, a character string is connected to a number using the +
operator. This involves an automatic type conversion. The number 42
becomes the string "42", and in total, this results in the string "First
number: 42" . The same applies to the second line.
This procedure leads to an unexpected result in the third line. After the
type conversion of the two numbers, the three strings "Addition
incorrect: " , "72.8", and "25" become the long character string
"Addition incorrect: 72.825" . If the two numbers are to be added up
first, then parentheses must be used, as shown in the next line. Only
then will the correct result of the addition be displayed.
Finally, the small number, the large number, and the number with
many digits are output.
In this way, information is stored for which there are only two states.
The options for states are on or off, 0 or 1, and correct or incorrect.
Here’s an example:
...
<body>
<script>
let saved;
saved = true;
const done = false;
document.write("<p>Saved: " + saved + "<br>");
document.write("Done: " + done + "</p>");
</script>
</body></html>
The saved variable is assigned the true value, while the done constant
is assigned the false value. Then, they are output, as shown in
Figure 2.5.
First, do the calculations in the first of the three blocks in Listing 2.5:
The first calculation is performed from left to right and results in the
value 3.5.
In the second calculation, 4 * 3 and 6 / 2 are calculated first. Only
then can an addition or subtraction operation follow. The result is
2 + 12 – 3 = 11.
In the third calculation, the contents of the parentheses (2 + 4) and
(3 – 6) are calculated first. Only then can the remaining calculations
be carried out. The result is 6 * –3 / 2 = –9.
If we divide 13.5 by 5, we get "2 with a remainder of 3.5" . The
modulo operator % determines the remainder, 3.5.
The third block deals with the exponentiation operator **, which has
an even higher priority than multiplication and division:
The first calculation is 2.5–3.5. The value before the ** operator is
called the base, and the value after the operator is the exponent.
Both can have decimal places, and the exponent can be preceded
by a negative sign.
If there’s a negative sign in front of the base, the value must be
placed in parentheses. You can see this in the second example.
The third example shows that the exponentiation of a negative
number with a negative number that has decimal places is
mathematically not permitted. This results in the NaN value, which
stands for not a number.
In the fourth example, due to the higher priority, the exponentiation
is carried out first, followed by the multiplication. The result is 4 * 32
= 4 * 9 = 36.
Figure 2.6 shows the results.
Figure 2.6 Calculations and Their Results
let z;
z = 6; document.write("<p>" + z + " ");
z++; document.write(z + " ");
z--; document.write(z + " ");
z += 13; document.write(z + " ");
z -= 5; document.write(z + " ");
z *= 3; document.write(z + " ");
z /= 6; document.write(z + " ");
z %= 3; document.write(z + "</p>");
let a = 5, b = 5, c = 5, d;
d = a++; document.write("<p>" + d + " ");
d = ++b; document.write(d + " ");
d = c++ + ++c + c++; document.write(d + "</p>");
</script>
</body></html>
You can set the ++ and -- operators both before a variable (prefix
notation) and after a variable (postfix notation). With the statement d =
a++;, d receives the old value of a, and then a is increased. With the
statement d = ++b; , b is increased first, and then d receives the
increased value of b.
The result of the first input is saved in the input variable. This is a
string, and the parseFloat() function is used to convert a string into a
floating point number (i.e., a number with decimal places). After this
conversion, the z1 variable contains a number.
The same happens with the second entry, only in a shortened form.
The prompt() method returns a string for which the parseFloat()
function is called directly, the two numbers are then added together,
and the entire calculation is output. You can see an example in
Figure 2.8 to Figure 2.10.
Note
Note
The write() method is called for the document object, but the global
parseFloat() and parseInt() functions are not called for a specific
object.
Note
The octal system only uses the digits 0 to 7, so the number 0o33
corresponds to the following value:
3 × 81 + 3 × 80 = 24 + 3 = 27
The dual (or binary) system only uses the digits 0 and 1, so the
number 0b11011 corresponds to the following value:
1 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 1 × 20 = 16 + 8 + 2 + 1 = 27
2.3 Different Branches in a Program
Depending on certain conditions, different parts can be run through in
a program. We also say that the program branches out. Branches are
among the most important control structures, and in this section, you’ll
learn about different types of branches and their effects.
You should pay particular attention to the double equals sign with the
== operator. A common mistake is to confuse == with the = operator,
which results in an assignment instead of a comparison. All
comparison operators can be used for numbers, but only the last two
—== and !=—can be used for strings.
You can use the ternary operator ?: to create a shortened branch, and
a value is provided that can be assigned.
The following is an example with a total of six branches. These are
numbered in the program and in the output for a better overview:
...
<body>
<script>
const a = 12, b = 7;
// 1: Single branch
if(a > b)
document.write("<p>1: a is greater than b</p>");
// 4: Multiple branches
if(a > b)
document.write("<p>4: a is greater than b</p>");
else if(a < b)
document.write("<p>4: a is less than b</p>");
else
document.write("<p>4: a is equal to b</p>");
// 5: Character strings
const country = "Spain";
if(country == "Spain")
document.write("<p>5: Country is Spain</p>");
if(country != "Spain")
document.write("<p>5: Country is not Spain</p>");
// 6: Ternary operator
const greater = (a > b) ? a : b;
document.write("<p>6: The greater number is " + greater + "</p>");
</script>
</body></html>
What will the output be if you set the value b = 17 in the program?
Think through the answer on your own. Then change the program
accordingly and check whether your assumption was correct. What
happens if b = 12 ? Here too, first determine the result by thinking
through and then use the modified program to check whether your
assumption was correct.
Two Tips
Write clear, easy-to-read programs. In the case of a branch, you
should work with indentations after if and else, as in the previous
program.
Don’t write a semicolon after the condition after an if or an else.
Therefore, it should not look like this: if(a > b); or else;, which
would result in the branch ending immediately and the
subsequent statements always being executed. There could also
be no output at all because the else is generated without its
associated if. These typical rookie mistakes are hard to detect.
Note
You’ll find the u_if exercise in bonus chapter 1, section 1.3, in the
downloadable materials for this book at www.rheinwerk-
computing.com/5875.
In Figure 2.13, you can see the dialog box with the question.
There’s also the logical operator ! for the logical NOT, which reverses
the truth value of a condition. True becomes false, and false
becomes true.
Here’s an example:
...
<body><p>
<script>
const a = 12;
In the first case, the system checks whether the number is greater
than or equal to 10 and less than or equal to 20 (i.e., whether it’s
within the number range from 10 to 20).
The second case tests whether the number is less than 10 or greater
than 20 (i.e., outside the number range).
What will the output be if you set the value a = 25 in the program?
Think through the answer on your own. Then change the program
accordingly and check whether your assumption was correct. What
happens when a = –15? Here too, first determine the result by thinking
through and then use the modified program to check whether your
assumption was correct.
Two Tips
The a >= 10 || a <= 20 condition is fulfilled by every number, while
the a < 10 && a > 20 condition is not fulfilled by any number. You
should avoid these links.
The so-called short-circuit behavior is defined for the logical
operators in JavaScript. This is important for the use of functions,
and in Section 2.6.7, you can see an example of this.
Note
You can find the u_linked exercise in bonus chapter 1, section 1.4,
in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
if(input == null)
document.write("Cancel");
else if(input == "")
document.write("No input");
else if(isNaN(input))
document.write("No valid number");
else if(number_input < 0 || number_input >= 1)
document.write("No number in the valid range");
else if(distance < 0.1)
document.write("You are close");
else
document.write("You are far off");
In the first part of the program, a random number gets generated and
saved. Due to the use of prompt(), you are prompted to make an
entry, and this input is saved both as a string and as a number.
The second part of the program contains multiple branches. If the first
condition doesn’t apply, the second will be checked. If this doesn’t
apply either, the third will be checked, and so on.
The Cancel button causes the value null to be saved (see also
Section 2.1.3). If nothing is entered and then the OK button gets
clicked, an empty string will be saved. You can query both using the
== operator.
The isNaN() function stands for is not a number. If the input doesn’t
contain a valid number, the method returns true; if it does, then the
method returns false. The subsequent linked condition checks
whether the number is within the valid range.
Then the difference between the random number and the entered
number gets determined. The absolute value is calculated from this
difference, and if the absolute value is less than 0.1, the input is close
to the actual number.
Note
if(a == b)
document.write("a == b<br>");
if(a === c)
document.write("a === c<br>");
if(a === b)
document.write("a === b");
else
document.write("Not a === b");
</script></p>
</body></html>
In Figure 2.18, you can see the name, the value, and (thanks to
typeof) the type of the value of the four variables that are used in the
program. In the case of a number, typeof returns the designation
number, in the case of a string, the designation string and in the case
of a truth value, Boolean.
Figure 2.18 Checking Value and Type
The a, b, and c variables all contain the value 4711, but in b as a string.
The true value is always returned for all comparisons between these
variables using the == operator. If the === operator is used, this is only
successful when comparing a and c.
Table 2.1 shows the operators used so far in order of their priority.
Here’s an example:
...
<body>
<script>
const country = prompt("Please enter a country:");
let city;
switch(country)
{
case "Italy":
city = "Rome";
break;
case "England":
case "Wales":
case "Scotland":
city = "London";
break;
default:
city = "Not known";
}
If Italy was entered, Rome is provided as the capital and the switch
block is exited.
If none of the cases apply, you can use the default keyword to catch
all remaining cases. As with an if without else, however, this case
doesn’t necessarily have to exist.
You can use a switch branch to examine a specific number or a
specific string, but it’s not possible to specify ranges such as case < 10
or case >= 10 && <= 20 .
2.4 Repeating Program Sections
In addition to branches, loops are important control structures. Many
processes that are repeated in an identical or a similar way can be
programmed efficiently with loops, using the ability of a computer to
perform a large number of steps in a short time.
// 2: Downward
document.write("<br>2: ");
for(let i=20; i>=10; i--)
document.write(i + " ");
In loop 1, the loop variable i is given the start value 1. The following
applies as a condition for the entire loop: It runs as long as i is less
than or equal to 5, and after each run, the value of i is increased by 1
using the assignment operator, ++. This results in a sequence of
numbers from 1 to 5, in increments of 1. The start value, condition,
and change are separated from each other by a semicolon.
There’s only one statement within loop 1: the output of the loop
variable. In Figure 2.19, you can see the regular sequence of
numbers. If multiple statements are supposed to be executed within a
loop, they must be in a block with curly brackets, as in a branch.
In loop 3, you can see that a loop variable can also be a number with
decimal places. In contrast to integers, numbers with decimal places
can’t be saved with mathematical precision, and that gives rise to two
problems:
An output results in unsightly small deviations, as you can see in
Figure 2.19 from the value 3.4 onwards.
The loop doesn’t run to the desired end of 4. After the increase of
0.2, the value of i is just above 4, and a value above 4 is no longer
reached due to the condition i<=4.
Both problems can be solved, as you can see in loop 4 in Figure 2.19.
First, a value is entered within the condition that’s clearly above the
last desired value. This can be a value that’s half an increment higher,
for example. In addition, the toFixed() method is used to output the
numbers. This is a method of the Number object that you’ll get to know
in Chapter 6, Section 6.3. The toFixed() method rounds a value to the
desired number of decimal places (e.g., for the output).
Loop 5 contains multiple statements that must be placed in a block
with curly brackets. Loop 5 contains the continue and break
statements.
The continue statement causes the current loop run to be ended
immediately and the next run to continue. This means that the values
from 16 to 24 are not output, as you can see in Figure 2.19.
Note
Note
You’ll find the u_for exercise in bonus chapter 1, section 1.5, in the
downloadable materials for this book at www.rheinwerk-
computing.com/5875.
document.write("<table><tr>");
for(let s=0; s<3; s++)
document.write("<td>S" + s + "</td>");
document.write("</tr></table>");
document.write("<br>");
document.write("<table>");
for(let z=0; z<3; z++)
{
document.write("<tr>");
for(let s=0; s<5; s++)
document.write("<td>Z" + z + "/S" + s + "</td>");
document.write("</tr>");
}
document.write("</table>");
</script>
</body></html>
The first step is to create a table with three rows. Each row contains a
table cell in which the current number of the row is displayed, and the
numbering starts at 0. You’ll often encounter this type of numbering in
connection with fields (Section 2.4.3).
Then, a table with one row gets created. This row contains three table
cells, each of which displays the current number of the table cell.
Finally, there’s a table with three rows and five columns. The number
of the current row and the current column is displayed in each table
cell.
Two nested loops are used and are controlled with different variables:
Initially, the z variable of the outer loop has the value 0. The inner
loop is then run through a total of five times using the s variable and
the values 0 to 4 to create the five cells of a row.
Then, z receives the value 1, and five cells are created again, and
so on.
Note
document.write("<table>");
for(let z=0; z<3; z++)
document.write("<tr><td>" + person[z] + "</td></tr>");
document.write("</table>");
document.write("<br>");
document.write("<table>");
for(let z=0; z<3; z++)
{
document.write("<tr>");
for(let s=0; s<2; s++)
document.write("<td>" + age[z][s] + "</td>");
document.write("</tr>");
}
document.write("</table>");
</script>
</body></html>
The person variable represents a reference to the field after the field
has been assigned, and the field and its elements can be accessed
using the reference. The name of the field is person, and you can
change the individual elements of the field as well as assign a different
field to the variable.
As with the while loop, it’s not known during the development of the
program how often the loop must be run through. However, at least
one attempt is required, which is why the do ... while loop is used. A do
... while loop is also referred to as a read-in loop, as it’s often used for
the controlled input of a value:
The input is entered at least once.
If the entry doesn’t meet the specifications, it will be repeated.
Here’s the program:
...
<body>
<p><script>
const number_random = Math.random() * 20 + 1;
const a = Math.floor(number_random);
const b = Math.floor(Math.random() * 20 + 1);
const sum = a + b;
let number_input;
do
{
const input = prompt(a + " + " + b + " = ");
number_input = parseInt(input);
if(number_input != sum)
alert("Please try again");
}
while(number_input != sum);
The text entered is saved in the number_input variable, and it’s then
converted into an integer using parseInt(). This is followed by a
condition that’s used twice: is the integer different from the correct
result? If this is the case, a message gets displayed, and the do...
while loop continues to run.
If the correct result has been entered, the condition no longer applies,
the loop ends, and the task and the correct result are displayed again
in the document.
In Figure 2.23, you can see the task and a possible incorrect entry.
The program’s response to this is shown in Figure 2.24.
Three Tips
At the start of the programming work, the condition of the while or
do ... while loop and the condition in the second part of the for
loop present a certain challenge. Remember that this is a run
condition and not a termination condition, so it’s not the case that
the loop terminates as soon as the condition is met. The loop
runs as long as the condition applies.
The beginning of a for loop or a while loop must not be followed
by a semicolon, so it should not look like for(let i = 1; i <= 5;
i++); or while(a > b); . This would result in the loop terminating
immediately and the subsequent statements only being executed
once. This error is difficult to detect.
In contrast, the end of a do ... while loop must be followed by a
semicolon.
do
{
counter++;
if(counter>3)
{
length++;
counter = 1
}
text = "";
for(let i=1; i<=length; i++)
text += Math.floor(Math.random() * 10);
alert("Sequence of digits: " + text);
The random number sequences are stored in the text variable. The
length variable contains the current length of the digit sequence, and
counter contains information on whether it’s the first-, second-, or
third-digit sequence of the same length.
The do ... while loop runs as long as the input and the sequence of
digits match. The value of counter gets increased each time, and if the
value exceeds 3, it will be reset to 1. At the same time, the value of
length increases, which ensures that a sequence of digits of the same
length appears three times.
Write your programs clearly. If you are thinking about how you can
write three or four specific steps of your program at once, turn them
into individual statements that are executed one after the other. This
simplifies any troubleshooting process, and if you (or another person)
change or expand your program at a later date, it will be much quicker
to start building the program.
The values of the two variables should be displayed on the screen, but
the x variable never gets declared. The attempt to output x results in
the program being aborted, which means that the value of the
correctly declared y variable will no longer be output either. The
screen remains blank and the cause remains unknown.
So, let’s now extend the program for troubleshooting purposes:
... <head> ...
<script>
function error_handling(error, file, line)
{
alert("Error: " + error + "\nFile: " + file
+ "\nLine: " + line);
}
</script>
</head>
<body><p>
<script>
onerror = error_handling;
const y = 42;
document.write(x + "<br>");
document.write(y + "<br>");
</script></p>
</body></html>
Section 2.6 explains how you can modularize programs using your
own functions, but let me provide just a brief explanation at this point.
The onerror event occurs when an error occurs, and as a result, the
function that was assigned to the event gets called. In this example,
that’s the error_handling() function. Functions are usually defined in
the head of the document, and the error_handling() function is
provided with information about the error that has occurred, which you
can output using alert().
The message for our example is shown in Figure 2.26, and it’s
referred to as an Uncaught ReferenceError. A reference error occurs
if no reference (i.e., no declared name) exists for a variable. Errors
can be caught, but this error is not caught. It occurs in line 18 of the
onerror.htm file, and thanks to this message, the cause of the error
can be found and the problem can be solved.
Here’s an example:
...
<body><p>
<script>
const y = 42;
try
{
document.write(x + "<br>");
document.write(y + "<br>");
}
catch(e)
{
alert(e);
}
finally
{
document.write("This will definitely be done<br>");
}
</script></p>
</body></html>
The values of the two variables are to be output in the try block. The
missing declaration of x is noticed, the program branches to the catch
block, and information on the error that has occurred is transmitted in
an error object. It has become common practice to designate such an
error object with e (for error), and you can output the object using
alert().
try
{
if(isNaN(number))
throw "No valid number";
if number <= 0:
throw "Number too small";
document.write("Number: " + number);
}
catch(errorObject)
{
alert(errorObject);
}
</script></p>
</body></html>
Listing 2.25 throw.htm File
When you initiate debugging, it’s assumed that the browser is open
with the named program. Open the browser menu using the three dots
in the top right-hand corner and select the More tools • Developer
tools menu item. Then, the developer tools are displayed, and they
may offer many different options.
Figure 2.29 Program, Normal Procedure
For debugging, you want to select the Sources tab at the top of the
screen. The program code is displayed as shown in Figure 2.30, and
the individual lines are preceded by line numbers.
After you click on one of the line numbers, a breakpoint is created for
this line. A second click removes the breakpoint. In Figure 2.31, you
can see that breakpoints have been created next to the lines in which
the b and output variables receive their values.
You can monitor the values of the variables farther down in the Watch
tab. To show the variables, click the button with the + sign, enter the
name of the respective variable, and press (Enter).
In Figure 2.32, you can see the status after stopping the program,
after all four variables of the program have been displayed.
Up to this point, the b and c variables have also been assigned values,
and after the next resume, the program runs to the end and the output
appears. After removing the breakpoints, the program runs again
without stopping.
In the first script container, the src attribute is used to reference the
external file.
You have already used parameters. The alert() method of the window
object and the write() method of the document object are methods
with parameters, and each of them outputs the string that’s
transmitted to it as a parameter.
In the program that follows, you can see a separate function to which
two strings are passed. The function creates a sentence from this and
outputs it.
Two Tips
The parameters can be strings, numbers, or truth values. Later,
you’ll also pass objects or fields using parameters.
The number of parameters must match when calling and defining.
You’ll find additional options in the following sections.
2.6.4 Changing Parameters
You can change parameters within a function. Depending on the type
of parameter, this change has an effect on the variables with which
the function was called.
function change(n, t, d, p)
{
n = 4711;
t = "Hello";
d = false;
p[0] = "Brad";
document.write("In the function, element changed: ");
document.write(p + "<br>");
Let’s first look at the main program in which the functions are called.
Four variables are declared, and they are assigned a string, a number,
a truth value, and a field. They are then passed twice to the output()
function so that you can see the values at the beginning and end of
the program. In the simple output of a field, the listed elements are
separated by commas.
You have already used return values of functions several times. The
isNaN() method returns a truth value, while the Math.random() method
returns a random number.
The following program contains a function that requires two numbers
as parameters, and it returns the sum of the two numbers:
... <head> ...
<script>
function sum(a, b)
{
const result = a + b;
return result;
}
</script>
</head>
<body>
<p><script>
const x = 3, y = 5;
const z = sum(x, y);
document.write("Sum: " + z + "<br>");
document.write("Sum: " + sum(14, 20/4));
</script></p>
</body></html>
The sum() function expects two parameters that are added together
within the function. The next statement contains the return keyword,
and it has two tasks:
To return from the function from any position
To return the result of the function to the point at which it’s called
The first time the sum() function is called, the current values of x and y
are passed to the function. The sum is returned and stored in the z
variable, and this is then output.
Note
You’ll find the u_function exercise in bonus chapter 1, section 1.7,
in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
The output in Figure 2.39 shows that the two values of a and b have
been successfully swapped with each other.
In the swap() function, the names of the two parameters are x and y,
and they are swapped with each other using a third variable. The
function returns the x and y variables, which are enclosed in
rectangular brackets like a field. This ensures that x is passed to a and
y is passed to b at the call point.
Note
You’ll find the u_destructuring exercise in bonus chapter 1, section
1.8, in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
Let’s first look at the definition of the function. The parentheses after
the name of the function are empty, and all transferred parameters of
a function call are not only available within these parentheses but also
in the arguments object. This object resembles a field, and you can
access the first parameter via arguments[0], the second via arguments
[1], and so on. The length property contains the number of
parameters.
To calculate the sum, all parameters are run through using a for loop.
The sum of the parameters is determined and returned as the return
value of the function.
In the program, you’ll see three different calls to the function: one with
two parameters, one with four parameters, and one without
parameters. The sum of the parameters is determined and output, and
calling the function without parameters should show that the number
of parameters is really arbitrary.
The output of the program is shown in Figure 2.41.
The add() function is used to add two, three, or four values. The last
two parameters have the default value 0, so the sum is not distorted.
The output of the program is shown in Figure 2.42.
In the following program, you can see the connections just described:
... <head> ...
<script>
function oscar(x)
{
const a = 52;
const d = 45;
document.write("In a function: a:" + a + ", b:" + b
+ ", x:" + x + ", d:" + d + "<br>");
if(true)
{
const a = 62;
document.write("In a block: a:" + a + ", b:" + b
+ ", x:" + x + ", d:" + d + "<br>");
}
}
</script>
</head>
<body><p>
<script>
const a = 42;
const b = 43;
const c = 44;
document.write("In the entire program: a:"
+ a + ", b:" + b + ", c:" + c + "<br>");
oscar(c);
</script></p>
</body></html>
Listing 2.36 function_validity.htm File
Note
You could also use the original c variable in the function, but within
the function, you should work as little as possible with variables that
apply to the entire program. If a variable is required from outside the
function, it should be passed as a parameter. This makes it easier
to control and more difficult to change accidentally.
A variable with the name a is again declared within the block after the
if. This variable hides all variables of the same name mentioned
above and is valid in the entire if block.
In Figure 2.43, you can see the variables and their values.
You can find further examples of recursion in this book, for example,
in Chapter 5, Section 5.3.
In addition, you can work not only with references to fields or objects
but also with references to functions. A reference to a function can be
assigned to a variable, and the reference to a function can also be
passed as a parameter.
function oscar(welcome)
{
welcome();
}
</script>
</head>
<body><p>
<script>
greeting();
oscar(greeting);
The first anonymous function outputs the text Hello world , and it’s
defined without parameters. The reference to this function is assigned
to the greeting variable.
In the second anonymous function, the two transferred parameters are
added together, and the result is delivered as a return value. The
reference to the function is assigned to the sum variable.
Each of the two anonymous functions is called using the name of the
variable followed by parentheses, as with a named function:
greeting() or sum(35, 7) .
At the end of the program, you can see two anonymous functions that
are not assigned to any variables but are called directly:
The first function has no parameters and returns the text "Good
morning". The returned text is output using document.write().
In this chapter, you'll learn about creating your own objects and
gain a better understanding of the structure of existing objects.
dodge.speed = 75;
document.write("Color: " + dodge.color
+ ", Speed: " + dodge.speed + "<br>");
The class keyword introduces the definition of the class. Note that the
name of the class is not followed by parentheses, as would be the
case with a function.
The constructor method is used to create an object of the class. It has
the fixed name constructor, and its parameters correspond to the
properties of an object of the class.
Two properties are defined within the constructor method, each with
the this keyword, the dot operator, and a name. The two properties
are assigned the values that are transferred as parameters when an
object of the Car class gets created. The this keyword references this
object (i.e., the current object), and it ensures that a property of the
current object is accessed or a method is executed for the current
object.
In the following example, the Car class from Section 3.1 gets
extended. The paint() and accelerate() methods are defined and
used to change the color and speed properties. In addition, a special
method with the name toString() is defined; it enables the simple
output of an object, similar to the output of a variable.
accelerate(value)
{
this.speed += value;
}
paint(c)
{
this.color = c;
}
toString()
{
return "Color: " + this.color
+ ", speed: " + this.speed;
}
}
</script>
</head>
<body><p>
<script>
const dodge = new Car("Red", 50);
document.write("Color: " + dodge.color
+ ", Speed: " + dodge.speed + "<br>");
dodge.accelerate(35);
dodge.paint("Blue");
document.write(dodge);
</script></p>
</body></html>
The accelerate() method is called using the dot operator for the dodge
object. This method expects a numerical value that is used to change
the value of the speed property, while the paint() method expects a
character string that represents the new value of the color property.
Note
You can find the u_class exercise in bonus chapter 1, section 1.9, in
the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
Figure 3.2 Using Methods
3.3 Private Members
In the previous programs in this chapter, you were able to access the
properties and methods of objects directly from the actual program.
The members in those programs are therefore publicly accessible, or
public for short.
constructor(c, s)
{
this.#color = c;
this.speed = s;
}
accelerate(value)
{
this.speed += value;
}
#paint(c)
{
if(c == "Red" || c == "Yellow" || c == "Blue")
this.#color = c;
}
change(c, value)
{
this.#paint(c);
this.accelerate(value);
}
toString()
{
return "Color: " + this.#color
+ ", speed: " + this.speed;
}
}
</script>
</head>
<body>
<p><script>
const dodge = new Car("Red", 50);
document.write(dodge + "<br>");
// document.write("Color: " + dodge.#color + "<br>");
dodge.change("Green", 25);
document.write(dodge + "<br>");
dodge.change("Blue", 10);
document.write(dodge + "<br>");
// dodge.#paint("Yellow");
</script></p>
</body></html>
In the paint() method, care is taken to ensure that only the permitted
colors red, yellow or blue can be used. In this way, the private
property color is protected. You can see the result of the program in
Figure 3.3.
Figure 3.3 Access to Private Members
3.4 Setters and Getters
Since ECMA 2022, you can also use setters and getters (so-called
accessors) for controlled access to properties. A setter is used for
write access using the set keyword, while a getter is used for read
access using the get keyword.
constructor(c, s)
{
this.color = c;
this.setSpeed = s;
}
get getSpeed()
{
return this.#speed;
}
set setSpeed(s)
{
if(s < 0) s = 0;
else if(s > 100) s = 100;
this.#speed = s;
}
accelerate(value)
{
this.setSpeed = this.getSpeed + value;
}
toString()
{
return "Color: " + this.color
+ ", Speed: " + this.getSpeed;
}
}
</script>
</head>
<body>
<p><script>
const dodge = new Car("Red", 150);
document.write(dodge + "<br>");
dodge.accelerate(20);
document.write(dodge + "<br>");
dodge.accelerate(-120);
document.write(dodge + "<br>");
dodge.accelerate(30);
document.write(dodge + "<br>");
</script></p>
</body></html>
In the program, the value of the private speed property can only be
changed using the accelerate() method. In this method, the current
value of the speed property is first determined using the getSpeed
getter, and the getter returns the value like a method using return.
A new value for the speed property is calculated on the right-hand side
of the assignment. The assignment itself leads to a call of the setSpeed
setter, and the new value is transferred as a parameter, as with a
method. The setter ensures that the new value for the speed is only
within the permitted range between 0 and 100, and it’s then assigned
to the private speed property.
The setter is also used in the constructor of the class to limit the value
of the property. The output of the program looks as shown in
Figure 3.4.
Figure 3.4 Control with Accessors
3.5 Static Members
Different objects of the same class usually have different values for
their properties. In addition to these object-related properties, you can
define static properties, which relate to the class as a whole and are
available to all objects with the same value. You can also define static
methods, which also refer to an entire class and are called for this
class.
constructor(c, s)
{
this.color = c;
this.speed = s;
Car.quantity = Car.quantity + 1;
this.number = Car.quantity;
}
toString()
{
return "Color: " + this.color + ", Speed: "
+ this.speed + ", Number: " + this.number;
}
static output_quantity()
{
document.write("Quantity: " + Car.quantity + "<br>");
}
}
</script>
</head>
<body>
<p><script>
const dodge = new Car("Red", 50);
document.write(dodge + "<br>");
const renault = new Car("Yellow", 65);
document.write(renault + "<br>");
Car.output_quantity();
</script></p>
</body></html>
In the following program excerpt, you’ll only see those lines that differ
from the obj_static.htm program:
... <head> ...
<script>
class Car
{
static
{
Car.quantity = 0;
}
constructor(c, s)
{ ...
The static block is introduced using the static keyword. Variables that
are declared in it are static, and as the variables are located within the
block, they must be accessed via the name of the class. The output of
the program corresponds to that of the obj_static.htm program, as you
can see in Figure 3.5.
3.7 Reference to Nothing
In connection with objects, a given variable can reference nothing
instead of an object. This can be the case after the given variable has
been assigned the value null, but it can also happen if the variable is
supposed to reference a DOM element (see Chapter 5) that doesn’t
exist in the document.
A variable that references nothing has the value null, and a reference
to nothing often leads to the program being aborted. There are two
operators that can prevent this from happening:
The null coalescing operator, ??
The optional dot operator, ?.
toString()
{
return "Color: " + this.color
+ ", speed: " + this.speed;
}
}
</script>
</head>
<body><p>
<script>
let dodge = new Car("Red", 50);
document.write((dodge ?? "Object does not exist") + "<br>");
document.write("Color: " + dodge?.color + "<br>");
dodge = null;
document.write((dodge ?? "Object does not exist") + "<br>");
// document.write("Color: " + dodge.color + "<br>");
document.write("Color: " + dodge?.color + "<br>");
</script></p>
</body></html>
When you use the null coalescing operator, ??, the left operand gets
checked. If its value is null, the right operand will be returned;
otherwise, the left operand will be returned.
Then, since the object that the dodge variable references exists, the
object gets output. The reference is later assigned the null value in
the program for demonstration purposes, which means that dodge is a
reference to nothing, which causes the "Object does not exist" text to
be displayed.
tune(x)
{
this.power += x;
}
toString()
{
return "Power: " + this.power + ", Cylinders: "
+ this.cylinders + ", fuel: " + this.fuel;
}
}
class Car
{
constructor(c, s, d)
{
this.color = c;
this.speed = s;
this.drive = d;
}
toString()
{
return "Color: " + this.color + ", Speed: "
+ this.speed + ", Drive: " + this.drive;
}
}
</script>
</head>
<body><p>
<script>
const dodge = new Car("Red", 50,
new Engine(60, 4, "Diesel"));
dodge.drive.tune(10);
document.write(dodge + "<br>");
dodge.drive.power = 80;
dodge.drive.cylinders = 6;
dodge.drive.fuel = "Gasoline";
document.write(dodge);
</script></p>
</body></html>
An object of the Engine class has the power, cylinders, and fuel
properties. In addition, there’s the tune() method for changing the
performance and the toString() method for outputting the three
property values.
The drive property has been added to the Car class, and the value of
this property is also output using the toString() method.
An object of the Car class is created in the program, and the third
parameter is an object of the Engine class, which is also created using
new.
The first dot after the name of the dodge object addresses the drive
property of the Car object, and the second dot leads to the subproperty
of the Engine object.
accelerate(value)
{
this.speed += value;
}
toString()
{
return "Color: " + this.color
+ ", Speed: " + this.speed;
}
}
toString()
{
return super.toString() + ", Payload: " + this.payload;
}
}
</script>
</head>
<body><p>
<script>
const kenworth = new Truck("Orange", 30, 15);
document.write(kenworth + "<br>");
kenworth.accelerate(50);
kenworth.load(25);
document.write(kenworth);
</script></p>
</body></html>
The constructor of the Car base class expects two parameters for the
initial values of the color and speed properties. The constructor of the
derived Truck class expects three parameters, which are the initial
values for the color and speed properties of the Car base class and for
the payload property of the derived Truck class.
The super() method calls the specific constructor of the base class
within a constructor, and in this way, the first two parameters are
passed on to the constructor of the base class. The third parameter is
assigned to the payload property, and in this way, all properties of the
Truck class object are given an initial value.
Note
accelerate(value)
{
this.speed += value;
}
paint(c)
{
this.color = c;
}
toString()
{
return "Color: " + this.color
+ ", Speed: " + this.speed;
}
}
</script>
</head>
Once an object has been created, its properties are output multiple
times. Within the square brackets, the name of the property is written
like a string within double quotation marks. You can compose this
string from individual parts, also using variables, and this kind of
notation makes the creation of programs even more flexible. You can
see the output, among others, in Figure 3.9.
Variables are compared with each other in the following part of the
program, in the following ways:
First, there are two variables that reference the same object.
Second, there are two variables that reference two different objects
with the same properties and property values.
const renault = dodge;
document.write("Second reference: " + renault + "<br>");
if(renault == dodge)
document.write("Same object<br>");
const simca = new Car("Red", 50);
if(simca != dodge)
document.write("Not the same object<br><br>");
In Figure 3.10, you can see that the Car class and the accelerate()
and paint() methods have the function type while the dodge object has
the object type. The two properties are simple variables of the string or
number type.
3.10.5 Checking a Member
You can use the in operator to check whether a class has a specific
member (i.e., a specific property or method):
document.write("in:<br>");
if("color" in dodge)
document.write("color is member<br>");
if("accelerate" in dodge)
document.write("accelerate is member<br>");
if("paint" in dodge)
document.write("paint is member<br>");
if(!("power" in dodge))
document.write("power is no member<br><br>");
The check shows that the color, accelerate, and paint strings are
members of the class of the dodge object, but power is not, as shown in
Figure 3.11.
The dodge variable, the x parameter, and the lada variable reference
the same object. You can see the output in Figure 3.11 as well.
The speed property gets deleted, and after that, the values of the
color, speed, paint, and power properties are output. The speed
property no longer exists, while the power property never existed. You
can see the resulting output in Figure 3.12.
Figure 3.12 Property Values after a Deletion Process
3.11 Copying Objects
To copy an object, it’s not sufficient to create a copy of the variable
that references the object (see also Section 3.10.2). Instead, a new
object, whose properties are assigned the values of the original object,
must be created.
accelerate(value)
{
this.speed += value;
}
toString()
{
return "Color: " + this.color
+ ", Speed: " + this.speed;
}
copy()
{
const c = new Car(this.color, this.speed);
return c;
}
}
</script>
</head>
<body><p>
<script>
const dodge = new Car("Red", 50);
document.write(dodge + "<br>");
dodge.accelerate(25);
document.write(dodge + "<br>");
document.write(renault + "<br>");
</script></p>
</body></html>
A new object of the Car class is created in the copy() method, and the
values of the two properties of the current object are transferred to the
constructor. This means that the new object is a copy of the current
object. The copy() method returns a reference to this new object.
The copy() method for the dodge object is called in the program, and
the result of the method (i.e., the reference to the new object) is
assigned to the renault variable. Two objects whose properties can
have different values are then available.
The properties of the class from the preceding example can be copied
using a simple assignment, as they have a simple data type, such as
string, number, or truth value. In this case, a so-called flat copy is
sufficient.
The Car and Engine classes that you already know from Section 3.9
are used next, and the drive property of the Car class is an object of
the Engine class. The following is an excerpt from a program in which
an object of the Car class is copied using a total of two copy methods:
... <head> ...
<script>
class Engine
{
constructor(p, c, f)
{
this.power = p;
this.cylinders = c;
this.fuel = f;
}
tune(x)
{
this.power += x;
}
toString()
{
return "Power: " + this.power + ", Cylinders: "
+ this.cylinders + ", Fuel: " + this.fuel;
}
copy()
{
const e = new Engine(this.power, this.cylinders, this.fuel);
return e;
}
}
class Car
{
constructor(c, s, d)
{
this.color = c;
this.speed = s;
this.drive = d;
}
toString()
{
return "Color: " + this.color + ", Speed: "
+ this.speed + ", Drive: " + this.drive;
}
copy()
{
const c = new Car(this.color,
this.speed, this.drive.copy());
return c;
}
}
</script>
</head>
<body><p>
<script>
const dodge = new Car("Red", 50, new Engine(60, 4, "Diesel"));
document.write(dodge + "<br>");
dodge.drive.tune(15);
document.write(dodge + "<br>");
document.write(renault + "<br>");
</script></p>
</body></html>
When an object of the Car class gets copied, the copy() method of this
class is called. This creates a new object of the Car class, and the
values of the color and speed properties of the original object are
assigned.
For the third property (drive), the process is somewhat more complex.
First, the copy() method of the Engine class is called for this property,
and this creates a new object of the Engine class. The values of the
three properties of the original object of the Engine class are assigned,
and a reference to the newly created object is returned to the copy()
method of the Car class and assigned to the drive property of the Car
class.
The following example shows a form with the input field Input and the
Click button. After entering a text in the input field, you must click the
button (see Figure 4.1), and then a dialog box with information
appears (see Figure 4.2).
You can also use input fields to subsequently output information within
documents that have already been mapped in their entirety. In the
example, the same information appears in the Output field as in the
dialog box (see Figure 4.3). You can use the disabled attribute to
ensure that no entries can be made in this field.
Figure 4.3 Field for Output
Note
You can find the u_form exercise in bonus chapter 1, section 1.11, in
the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
Note
Since the entire process doesn’t take place on a web server, the PHP
file can’t be called properly. Only your code is displayed (see
Figure 4.6). A workaround for this problem follows in Section 4.2.2.
If you reset the form instead of submitting it, the response will be as
shown in Figure 4.7.
If you don’t use either of these two alternatives and only call the
programs with the forms from the directory, it’s not a problem. You can
use the JavaScript code in any case.
function reset()
{
alert("Form will be reset");
}
</script>
</head>
<body>
<form id="idForm" method="post" action="form_submit.php">
<p><input id="idText" name="entry"> Enter text</p>
<p><input type="submit"> <input type="reset"></p>
</form>
<script>
const fo = document.getElementById("idForm");
fo.addEventListener("submit", submit);
fo.addEventListener("reset", reset);
</script>
</body></html>
The form has the unique ID idForm, and the post value for the method
attribute is used to define the secure transmission method post, which
is selected in all examples. The value of the action attribute
references the responding PHP program, which we assume is in the
same directory.
The input field has the unique ID idText, which is required to access
the content of the field using JavaScript. The input field also has the
name attribute, here with the entry value, which is required to access
the content of the field using PHP.
The submit and reset values for the type attribute of the two input
elements identify them as buttons for submitting or resetting the form.
The fo variable references the form, and the addEventListener()
method is used to link the submit and reset events with the named
functions submit() and reset().
Those elements that have the name attribute in the form are
automatically turned into elements of the PHP field $_POST with their
attribute values. Here’s what it looks like: name="entry" becomes
$_POST["entry"].
alert(document.getElementById("idFamilyName").value
+ "\n" + document.getElementById("idPassword").value
+ "\n" + document.getElementById("idComment").value);
}
</script>
</head>
<body>
<form id="idForm" method="post" action="form_text.php">
<p><input id="idFamilyName" name="familyName"
required="required"> Family name (*)</p>
<p><input id="idPassword" name="password"
type="password" required="required"> Password (*)</p>
<p><textarea id="idComment" rows="3" cols="25"
name="comment">(Contents)</textarea> Comment</p>
<p><input type="submit"> <input type="reset"></p>
<p>(*) = required field</p>
</form>
<script>
document.getElementById("idForm").addEventListener
("submit", function(e) { return submit(e);});
</script>
</body></html>
The two input fields for the family name and password have the
required attribute with the required value, so the fields must not be
left blank. If neither of the two input fields is empty, the submit event
gets triggered.
The process in the event handler is as follows:
1. The addEventListener() method is called directly for the reference
to the form, and this abbreviated notation is also used in most of
the following examples.
2. The transfer of a parameter when linking an event to a function is
only possible using an anonymous function. In this example, the
parameter is a reference to the event object, which is
automatically available with more information when an event
occurs. According to convention, the reference is given the name
e.
if(r1.checked) tx = r1.value;
else if(r2.checked) tx = r2.value;
else if(r3.checked) tx = r3.value;
alert(tx);
}
</script>
</head>
<body>
<form id="idForm" method="post" action="form_check.php">
<p><input id="idEstonia" name="balticCountries" type="radio"
value="Estonia" checked="checked">Estonia
<input id="idLatvia" name="balticCountries" type="radio"
value="Latvia">Latvia
<input id="idLithuania" name="balticCountries" type="radio"
value="Lithuania">Lithuania</p>
<p><input id="idSweden" name="sweden" type="checkbox"
value="Sweden">Sweden</p>
<p><input id="idFinland" name="finland" type="checkbox"
value="Finland" checked="checked">Finland</p>
<p><input type="submit"> <input type="reset"></p>
</form>
<script>
const r1 = document.getElementById("idEstonia");
const r2 = document.getElementById("idLatvia");
const r3 = document.getElementById("idLithuania");
const c1 = document.getElementById("idSweden");
const c2 = document.getElementById("idFinland");
document.getElementById("idForm")
.addEventListener("submit", submit);
</script>
</body></html>
Note
You can find the u_radio exercise in bonus chapter 1, section 1.12,
in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
4.5 Selection Menus
You can use a single-selection menu to choose among different
options, and you can select several options simultaneously in a
multiple-selection menu using the (ª) and (Ctrl) keys. You can also
use JavaScript to determine which selection has been made.
When the form gets submitted, the values of the various selection
elements are displayed (see Figure 4.16). These don’t have to match
the displayed texts.
Figure 4.16 Values of Selection Elements
alert(tx);
}
</script>
</head>
<body>
<form id="idForm" method="post" action="form_select.php">
<p>Country 1: <select id="idSingle" name="single">
<option value="Italy" selected="selected">Italy</option>
<option value="Spain">Spain</option>
<option value="Romania">Romania</option>
</select></p>
<p>Countries 2 to 4:
<select id="idMultiple" name="multiple[]" multiple="multiple">
<option value="Belgium" selected="selected">Belgium</option>
<option value="Netherlands">Netherlands</option>
<option value="Luxembourg" selected="selected">Luxembourg</option>
</select></p>
<p><input type="submit"> <input type="reset"></p>
</form>
<script>
document.getElementById("idForm").addEventListener("submit", submit);
</script>
</body></html>
Note
You can find the u_select exercise in bonus chapter 1, section 1.13,
in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
4.6 Other Form Events
You have already become familiar with the click (on a button) and
submit and reset (of a form) events. The following program adds the
following events that can take place within a form:
click: Clicking on a radio button or a checkbox
change: Changing the content of an input field or the selection in a
selection menu
keyup: Releasing a key in an input field
focus: Accessing an element (i.e., it receives the input focus, and
the input cursor is positioned in the element)
blur:Exiting an element (i.e., it loses the input focus, and the input
cursor is no longer positioned in the element)
The responses to the various events are displayed in a read-only input
field. As an example, in Figure 4.17, you can see the output after
clicking on a checkbox to select it.
Figure 4.17 Response to Marking Checkbox
const id = e.target.id;
if(id == "idTrip")
if(document.getElementById(id).checked)
re.value += "Marker set";
else
re.value += "Marker deleted";
else
re.value += document.getElementById(id).value;
}
</script>
</head>
<body id="idBody">
<form method="post" action="form_event.php">
<p><input id="idMr" name="address" type="radio"
value="Mr" checked="checked"> Mr
<input id="idMs" name="address" type="radio"
value="Ms"> Ms</p>
<p><input id="idFamilyName" name="familyName"> Family name</p>
<p><input id="idFirstName" name="firstName"> First name</p>
<p><input id="idCity" name="city"> City</p>
<p><select id="idCountry" name="country">
<option value="Italy" selected="selected">Italy</option>
<option value="Spain">Spain</option>
<option value="Portugal">Portugal</option>
</select> Country</p>
<p><input id="idTrip" name="trip" type="checkbox"
value="OneWayOnly"> one-way only</p>
<p><input type="submit"> <input type="reset"></p>
<p><input id="idResponse"
readonly="readonly"> Response</p></p>
</form>
<script>
document.getElementById("idMr").addEventListener
("click", function(e) { value(e); });
document.getElementById("idMs").addEventListener
("click", function(e) { value(e); });
document.getElementById("idFamilyName").addEventListener
("change", function(e) { value(e); });
document.getElementById("idFirstName").addEventListener
("keyup", function(e) { value(e); });
document.getElementById("idCity").addEventListener
("focus", function(e) { value(e); });
document.getElementById("idCity").addEventListener
("blur", function(e) { value(e); });
document.getElementById("idCountry").addEventListener
("change", function(e) { value(e); });
document.getElementById("idTrip").addEventListener
("click", function(e) { value(e); });
</script>
</body></html>
The structure of the form and the event handler are as follows:
The readonly attribute with the readonly value ensures that no entry
is possible in the input field. It’s only used to output information.
The click event is triggered when one of the two radio buttons is
selected. The event is linked to an anonymous function that calls
the named function, value(), and the e parameter, which refers to
an event object, is passed on. Other events are linked to the
value() function in the same way.
In an input field, the change event takes place after the content of
the input field has changed and as soon as another element is
selected or clicked outside the input field.
The keyup event in an input field is triggered immediately after a key
has been released. This event occurs after a character has been
entered or deleted.
In a selection menu, the change event takes place as soon as the
selection changes. If a checkbox is checked or unchecked, the
click event gets triggered.
The focus and blur events take place in an input field as soon as
the input field is accessed or subsequently exited.
The process in the value() function is as follows:
1. The target property of the event object references the element for
which the event was triggered. The type property contains the
name of the event, and the id sub-property of the target property
corresponds to the unique ID of the element. In this way, you can
use one function for multiple elements or events.
2. The checked property of the checkbox is checked to determine
whether the checkmark was set or removed at the click event.
For all other elements, the current value of the element for which
the event was triggered gets displayed.
You should test the possible events once and note the time at which
the information appears and the content of the response.
4.7 Mouse Events
In addition to forms, there are events that can be triggered using the
mouse. The following program handles the following events:
click: Clicking on an element
mousemove: Moving the mouse over an element
mousedown: Pressing down a mouse button over an element
mouseup: Releasing a mouse button over an element
mouseover: Accessing an element
mouseout: Exiting an element
To illustrate this, these events take place in images. Information on
the events is displayed in a read-only input field (see Figure 4.18).
<script>
document.getElementById("idParadise").addEventListener
("click", function(e) { mouse(e); });
document.getElementById("idSolarEclipse").addEventListener
("mousemove", function(e) { mouse(e); });
document.getElementById("idWave").addEventListener
("mousedown", function(e) { mouse(e); });
document.getElementById("idWave").addEventListener
("mouseup", function(e) { mouse(e); });
document.getElementById("idWinter").addEventListener
("mouseover", function(e) { mouse(e); });
document.getElementById("idWinter").addEventListener
("mouseout", function(e) { mouse(e); });
You should test the possible events once and note the location of the
event, the associated information, and the time at which the
information appears.
Note
You can find the u_mouse exercise in bonus chapter 1, section 1.14,
in the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
4.8 Changing the Document
By default, hyperlinks are used to switch to another document in the
browser. Using the predefined location object and the appropriate
events, this can also be done differently.
In the following program (see Figure 4.19) this is done in three ways:
After clicking on a button
After clicking on an image
By changing the selection in a selection menu
The various types can contain a validation (i.e., a check of the element
when the form is submitted). This serves to avoid the transmission of
incorrect data, and this capability can supplement or even replace
form checks with JavaScript. Before creating a JavaScript program,
you should check whether the desired validation can’t already be
carried out using HTML in modern browsers.
<script>
document.getElementById("idForm").addEventListener("submit", submit);
</script>
</body></html>
After calling the program, the input cursor automatically appears in the
element for the family name, thanks to the autofocus attribute. You
can immediately start entering data there, and if there’s already text in
such an element, it will be highlighted in full.
It has long been possible to hide an input field using the hidden value
for the type attribute. In this way, additional data that should not be
visible during use can be transmitted to the web server.
The search value for the type attribute identifies an input field that can
be recognized as a typical search field. It can also contain an icon for
deleting the search term.
A search field can have the additional list attribute, whose value
corresponds to the ID of a list with predefined entries that can be
selected when operating the search field (see also Figure 4.21). As
soon as characters are entered in the search field, only those entries
appear in which these characters occur. Additional search terms can
also be entered. The list is created using a datalist container, and
each entry represents the value of the value attribute of an option tag.
The color value for the type attribute indicates an input field for setting
a color. Once the input field has been selected, a dialog box with
setting options opens (see also Figure 4.22).
Figure 4.22 Setting Color
An email address can be entered in an input field that has the email
value for the type attribute. The field can be empty, and if it’s not
empty, it must contain the minimum components of an email address
or the form won’t be submitted. If the multiple attribute with the
multiple value gets added, then multiple addresses can be entered,
separated by commas.
The situation is similar for an input field with the url value for the type
attribute. The field can be empty, and if it’s not empty, it must contain
the minimum components of a URL or the form won’t be submitted.
However, multiple URLs can’t be entered.
The element for entering a phone number has the tel value for the
type attribute. It can provide the option of accessing existing phone
lists.
The pattern attribute is used to validate an input using regular
expressions. If an entry is made that doesn’t match the pattern, the
form can’t be submitted.
In this case, we have an example with a license plate. We assume
that it starts with two digits, the first of which must not be 0. This is
followed by a capital letter, a space and another capital letter. This is
followed by 1 or 2 digits at the end of the license plate. You can find
out more about regular expressions in bonus chapter 2 in the
downloadable materials for this book at www.rheinwerk-
computing.com/5875.
Here’s the content of the form in the middle part of the file:
...
<body>
<form id="idForm" method="post" action="form_number.php">
<p><input id="idNumber" name="number" type="number"
min="32" max="58" step="2" value="42">
<input id="idPlus1" type="button" value="+1 increment">
<input id="idMinus1" type="button" value="-1 increment">
<input id="idPlus3" type="button" value="+3 increment">
<input id="idMinus3" type="button" value="-3 increment"></p>
<p>number (32 to 58, increment 2)</p>
<p><input id="idRequired" type="checkbox"> Input required</p><br>
<p><input id="idRange" name="range" type="range" min="3.2"
max="5.8" step="0.2" value="4.2"> range <input id="idRValue"
readonly="readonly" size="5" value="4.2"></p>
<p><input type="submit"> <input type="reset"></p><br>
The element for entering a numerical value has the number value for
the type attribute. If you place the cursor in the input field, an
additional up/down button is displayed, and you can change the
number by clicking on it. To set the element, you can enter values for
the min, max, step, and value attributes. These indicate the lower limit
of 32, the upper limit of 58, the increment of 2, and the displayed value
of 42. A value that is not within the limits or doesn’t match the
increment won’t be submitted, and in that case, an error message will
display (see Figure 4.24).
Figure 4.24 Not Permitted Number
You can also set the value via code. The +1 increment, –1
increment, +3 increment, and –3 increment buttons change the
value by the corresponding multiple of the increment of 2 (e.g., the +3
increment button changes the value from 42 to 42 + 3 × 2 = 48). The
required attribute can also be changed via code. If you select the
checkbox, the number field can no longer be left blank.
A slider is used to securely transmit a numerical value that originates
from a range. This element has the range value for the type attribute.
The value itself is not displayed, so a separate display is added here
in a read-only input field. The min, max, step, and value attributes are
also available for setting purposes, and numbers with decimal places
can also be set using the slider.
<script>
document.getElementById("idForm").addEventListener("submit", submit);
document.getElementById("idRange").addEventListener
("change", changeRange);
document.getElementById("idStart").addEventListener
("click", startProgress);
document.getElementById("idStop").addEventListener
("click", stopProgress);
</script>
</body></html>
function changeRange()
{
const range = parseFloat(
document.getElementById("idRange").value);
document.getElementById("idRValue").value
= range.toFixed(1);
}
function startProgress()
{
if(pValue<100)
{
pValue++;
document.getElementById("idProgress").value = pValue;
document.getElementById("idPValue").value = pValue + "%";
reference = setTimeout(startProgress, 20);
}
}
function stopProgress()
{
clearTimeout(reference);
}
</script>
</head>
...
The value of the number input field is output in the submit() function,
and the changeRange() function is used to output the numerical value
of the slider with one decimal place.
All elements have the input type with different values for the type
attribute: date, time, datetime-locale, month, and week. The min and
max attributes are used to limit the time range, and the value attribute
stands for the default setting of the time specification. For the five
different elements, the appropriate output format is used for this
default setting.
The first input element of the date type is used to set a date with a
clock time of 00:00 Coordinated Universal Time (UTC), which is
coordinated world time. You can select individual parts of the time
entry for input (see Figure 4.26).
The second input element of the time type is used to set a clock time
with the date of 01/01/1970.
The element of the datetime-locale type can be used to set both the
date and the clock time. Note the separate format for the value
attribute.
The month type is selected for the fourth element, in which only entire
months can be set. The fifth element has the week type, in which only
entire calendar weeks can be set.
const t = document.getElementById("idTime");
const tValue = t.value;
let tDate = t.valueAsDate;
tDate.setHours(tDate.getHours() - 1);
const tLocale = tDate.toLocaleTimeString();
One hour must be subtracted from the time so that the displayed
result matches the set value. The getHours() method returns the value
of the hour of a Date object, while the setHours() method changes this
value.
You can find out more about the Date object in Chapter 6, Section 6.4.
In Figure 4.28, you can see the display after the form has been
submitted.
Figure 4.28 Output of Time Data
In addition, you can use the Test button to determine whether the
input should be checked and whether it would pass a test. This can be
useful if the content of a form is set automatically, and you want to
know whether the current content would stand up to a check.
Here’s the lower part of the document with the two forms and the
event handlers:
...
<body>
<form id="idForm1" novalidate method="post" action="form_validation1.php">
<p><input id="idContents1" name="contents1" required>
<input id="idCheck1" type="checkbox"> Form is being checked</p>
<p><input type="submit"></p>
</form>
<p> </p>
<script>
document.getElementById("idForm1").addEventListener("submit", submit1);
document.getElementById("idCheck1").addEventListener("click", check1);
document.getElementById("idTest2").addEventListener("click", test2);
document.getElementById("idForm2").addEventListener
("submit", function(e) { return submit2(e);});
document.getElementById("idCheck2").addEventListener("click", check2);
</script>
</body></html>
function check1()
{
const f = document.getElementById("idForm1");
f.noValidate = !f.noValidate;
}
function test2()
{
const number = document.getElementById("idContents2");
alert("Would be tested: " + number.willValidate + "\n"
+ "Too large: " + number.validity.rangeOverflow + "\n"
+ "Too small: " + number.validity.rangeUnderflow + "\n"
+ "Would be valid: " + number.validity.valid);
}
function submit2(e)
{
const contents2 = document.getElementById("idContents2");
const number = contents2.valueAsNumber;
if(contents2.disabled)
{
alert("Entry is disabled");
e.preventDefault();
return false;
}
else
alert(number);
}
function check2()
{
document.getElementById("idContents2").disabled =
!document.getElementById("idContents2").disabled;
}
</script>
</head>
...
The check1() function is executed each time the checkbox of the first
form is clicked. This sets the value of the noValidate property of the
form to true or false. When you send the form, an error message only
appears as in Figure 4.30 if noValidate has the false value and the
input field is empty. You can therefore specify in your programs
whether a form should be checked or not.
The value 82 in the second form leads to the result in Figure 4.31. In
this case, the rangeOverflow property of the validity object returns
true.
For value 51, the result is as shown in Figure 4.32. Although the value
is within the permitted range, it’s odd and therefore not valid.
Each time you click the checkbox of the second form, the check2()
function is executed. This sets the value of the disabled property of
the form to true or false, and the form can only be operated if
disabled has the value false.
4.10 Dynamically Created Forms
In this section, a form is created dynamically using JavaScript. Using a
double for loop, the form then contains a larger number of input fields
within a table (see Figure 4.33). The values of the id (for JavaScript)
and name (for PHP) attributes and the event handlers are created using
variables.
You can set the number of rows and columns in the program code at
the beginning of the document. In this example, the table has five
rows and three columns (i.e., fifteen input fields).
function change(e)
{
const id = e.target.id;
document.getElementById("idResponse").value
= id + ": " + document.getElementById(id).value;
}
function submit()
{
let output = "";
for(let r=0; r<rows; r++)
{
for(let c=0; c<columns; c++)
output += document.getElementById("idInput"
+ r + c).value + " ";
output += "\n";
}
alert(output);
}
</script>
</head>
<body>
<form id="idForm" method="post" action="form_dynamic.php">
<table>
<script>
for(let r=0; r<rows; r++)
{
document.write("<tr>");
for(let c=0; c<columns; c++)
{
document.write("<td><input id='idInput" + r + c + "' name='input"
+ r + c + "' value='" + r + "/" + c + "' size='10'></td>");
document.getElementById("idInput" + r + c)
.addEventListener("change", function(e) { change(e); });
}
document.write("</tr>");
}
document.getElementById("idForm").addEventListener("submit", submit);
document.write("<input type='hidden' name='rows'"
+ " value='" + rows + "'>");
document.write("<input type='hidden' name='columns'"
+ "value='" + columns + "'>");
</script>
</table>
<p><input type="submit"> <input type="reset"></p>
<p><input id="idResponse" readonly="readonly"></p>
</form>
</body></html>
The r and c loop variables serve as components for the values of the
id and name attributes. Note the single quotation marks for the values
of the attributes within the string for document.write(), which is in
double quotation marks.
You can use hidden form elements to send the number of rows and
columns to the PHP file; in this way, you can dynamically determine
the values sent. However, you should not exceed one thousand input
fields, as otherwise, PHP will report an error in the default setting.
5 The Document Object Model
This chapter describes how you can access and change the elements
of an HTML document, which is a prerequisite for many of the
techniques covered in the following chapters.
Select the Elements tab in the upper area of the screen. As with a
directory tree, you can now show or hide the individual levels (see
Figure 5.2). If the tab for displaying the code is too small, you can drag
the entire developer tools area to the left and thus give the individual
tabs more space.
Note
You can obtain the attribute nodes of an HTML element via the
getAttribute() method of the relevant node object. The parameter of
the function is a string containing the name of the attribute being
searched for.
Here’s a sample program:
... <head> ...
<script>
function retrieve()
{
let output = "";
const parArray = document.getElementsByTagName("p");
for (let i=0; i<parArray.length; i++)
output += "Content: " + parArray[i].firstChild.nodeValue + "\n"
+ "Attribute Style: " + parArray[i].getAttribute("style") + "\n";
alert(output);
}
</script>
</head>
<body>
<p style="font-family:Tahoma;">First paragraph</p>
<p>Second paragraph</p>
<p style="font-weight:bold;">Third paragraph</p>
<form>
<input type="button" id="idRetrieve" value="Retrieve">
</form>
<script>
document.getElementById("idRetrieve").addEventListener("click", retrieve);
</script>
</body></html>
You can access the first child node of a node via the firstChild
property of a node object. Each of the three paragraphs has only one
text node as a child node.
The nodeType property contains the type of node. The value 1 stands
for an element node (here, for an HTML element node), and the value
3 stands for a text node. The text content of a text node is in the
nodeValue property, and the name of the tag of an HTML element node
is in the nodeName property.
We are looking at the part of the DOM tree that contains the first
paragraph, and the recursive nodes() function is called for the first
time with a reference to this paragraph.
The level variable represents the current level within the tree. The
child nodes of the paragraph are at level 0, their child nodes are at
level 1 and their child nodes are at level 2.
In the nodes() function, the field with the child nodes of the current
element is run through using a for loop, and a reference to the current
field element is set up within the loop to shorten it. If the field element
is a reference to a text node, its value will be output, but if it’s a
reference to an HTML element node, its name will be displayed.
If the current field element itself has child nodes, the number of the
level is incremented and the nodes() function is called again.
In this way, you can run through all branches of the DOM tree. At
some point, you’ll determine that each of these branches has no child
nodes, and the recursion will return. The number of the level will be
reduced again, and at the end, you’ll return to level 0.
The output of the program is shown in Figure 5.5.
Figure 5.5 Child Nodes of First Paragraph
5.4 Adding Nodes
In this section, we create text nodes, HTML element nodes, and
attribute nodes and add them to a document. We use the following
two methods of the document object:
The createTextNode() method, which creates a text node. The
parameter of the method is a string containing the text.
The createElement() method, which creates an HTML element
node. The parameter of the method is a string with the tag without
angle brackets.
In addition, three methods are used for a node object:
The appendChild() method, which adds a child node to a node, at
the end of the child node field. The parameter of the method is a
reference to the node that gets added.
The setAttribute() method, which sets the value of an attribute
node. If the attribute node doesn’t yet exist, it will be created first.
The parameters of the method are two strings containing the name
and the new value of the attribute.
The insertBefore() method, which adds a child node to a node
before another child node. The parameters of the method are two
references: a reference to the node that gets added and a reference
to the node before which it will be inserted.
function insert()
{
const text = document.createTextNode("Inserted");
const par = document.createElement("p");
par.appendChild(text);
par.setAttribute("style", "font-style:italic;");
document.getElementById("idBody").insertBefore(par,
document.getElementById("idParagraph3"));
}
</script>
</head>
<body id="idBody">
<p id="idParagraph1">First paragraph</p>
<p id="idParagraph2">Second paragraph</p>
<p id="idParagraph3">Third paragraph</p>
<p><input id="idAppend" type="button" value="Append">
<input id="idInsert" type="button" value="Insert"></p>
<script>
document.getElementById("idAppend")
.addEventListener("click", append);
document.getElementById("idInsert")
.addEventListener("click", insert);
</script>
</body></html>
The Append and Insert buttons are used to append or insert a new
HTML element node.
In the JavaScript function append(), a new text node with the text
Appended is created, and an HTML element node is then created for a
paragraph. The text node and an attribute node are added to the
HTML element node, and then, the HTML element node is appended
to the body node of the document at the end.
The innerHTML property of the element object is also very useful. It’s
used to retrieve or assign an HTML tag including text.
function htmlChange()
{
const par = document.getElementById("idParagraph4");
par.innerHTML = "Change <i>in fourth</i> paragraph";
}
function surround()
{
const par = document.getElementById("idParagraph3");
const italic = document.createElement("span");
italic.setAttribute("style", "font-style:italic;");
const replaced = par.replaceChild(italic, par.firstChild);
italic.appendChild(replaced);
}
function clone()
{
const par = document.getElementById("idParagraph3");
const copy = par.cloneNode(true);
document.getElementById("idBody").appendChild(copy);
}
</script>
</head>
<body id="idBody">
<p id="idParagraph1">First paragraph</p>
<p id="idParagraph2"></p>
<p id="idParagraph3"><span style="font-weight:bold;">Third</span> paragraph</p>
<p id="idParagraph4">Fourth paragraph</p>
<p><input id="idTextChange" type="button" value="Change text">
<input id="idTextCreate" type="button" value="Create text">
<input id="idHtml" type="button" value="Change text and HTML"></p>
<p><input id="idSurround" type="button" value="Surround node with HTML">
<input id="idClone" type="button" value="Clone node"></p>
<script>
document.getElementById("idTextChange").addEventListener(
"click", function() {textChange("idParagraph1");});
document.getElementById("idTextCreate").addEventListener(
"click", function() {textChange("idParagraph2");});
document.getElementById("idHtml").addEventListener("click", htmlChange);
document.getElementById("idSurround").addEventListener("click", surround);
document.getElementById("idClone").addEventListener("click", clone);
</script>
</body></html>
In Figure 5.9, you can see the document after each button has been
clicked once.
Figure 5.9 After Changing
5.6 Deleting Nodes
You can also delete nodes from the DOM tree, using the following
methods for a node object:
The removeChild() method, which deletes the child node of a node.
The parameter of the method is a reference to the child node that
gets deleted.
The removeAttribute() method, which deletes the attribute node of
a node. The parameter of the method is a string with the name of
the attribute to be deleted.
Here’s the program:
... <head> ...
<script>
function removeNode()
{
const par = document.getElementById("idParagraph2");
document.getElementById("idBody").removeChild(par);
}
function removeAttribute()
{
const par = document.getElementById("idParagraph3");
par.removeAttribute("style");
}
</script>
</head>
<body id="idBody">
<p id="idParagraph1">First paragraph</p>
<p id="idParagraph2">Second paragraph</p>
<p id="idParagraph3" style="font-weight:bold;">Third paragraph</p>
<p><input id="idNode" type="button" value="Remove node">
<input id="idAttribute" type="button" value="Remove attribute"></p>
<script>
document.getElementById("idNode")
.addEventListener("click", removeNode);
document.getElementById("idAttribute")
.addEventListener("click", removeAttribute);
</script>
</body></html>
Listing 5.6 dom_delete.htm File
The Remove node button deletes the second paragraph, and the
Remove attribute button deletes the style attribute of the third
paragraph.
In Figure 5.11, you can see the document after the deletions.
Within the outer loop, insert the HTML element nodes for the rows
into the HTML element nodes for the table.
Finally, embed the HTML element node for the table in the
document.
Here’s the program code:
... <head> ...
<script>
function createTable()
{
const table = document.createElement("table");
document.getElementById("idBody").appendChild(table);
}
</script>
</head>
<body id="idBody">
<p><input id="idTable" type="button" value="Table"></p>
<script>
document.getElementById("idTable")
.addEventListener("click", createTable);
</script>
</body></html>
After you click the Table button once, the document looks as shown in
Figure 5.12.
Based on what you learned in Chapter 3, you can create your own
objects. Very often, however, you also work in JavaScript with many
predefined objects and their numerous properties and methods. This
chapter describes the predefined objects—Array, String, Math, Number,
and Date—that are used to edit arrays, strings, numbers, and
mathematical problems as well as dates.
The values in an array usually have the same type (e.g., multiple
strings as city names or several numbers as temperature values).
However, you can also save values of different types in the same
array.
document.write("All elements:<br>");
document.write("for-Loop: ");
for(let i=0; i<person.length; i++)
document.write(i + ":" + person[i] + " ");
document.write("<br>");
document.write("for-of-Loop: ");
for(let p of person)
document.write(p + " ");
document.write("<br><br>");
...
There are two ways to access all elements of an array using a loop:
Via a for loop. The first element of the array has the index 0 and is
therefore called person[0], the second element has the index 1, the
third has the index 2, and so on. The elements of the array can be
changed within the for loop.
Via a for-of loop, which was introduced with ECMAScript 2015.
Each time the loop is run through, another element of the array gets
assigned to a variable (in this case, the p variable). As this variable
is a copy of an element, a change to this variable within the loop
would not result in a change to the element.
document.write("for-Loop: ");
for(let i=0; i<person.length; i++)
document.write(i + ":" + person.at(i) + " ");
document.write("<br>");
The at() method was introduced with ECMAScript 2022. This method
accesses the element that has the index that is passed as a
parameter. Unlike when you access the element using the square
brackets, you can also pass a negative value. The –1 refers to the last
element of the array, the –2 to the penultimate element, and so on.
document.write("Convert, check:<br>");
const tx = "Hello world";
const tf = Array.from(tx);
document.write("Array from string: " + tf + "<br>");
document.write(tx + " is array: " + Array.isArray(tx) + "<br>");
document.write(tf + " is array: " + Array.isArray(tf) + "<br><br>");
...
Listing 6.3 array_one_dim.htm File, Part 3 of 4
The Array.from() method is called for the Array class itself and not for
an existing array, as there’s no array at this point. For this reason, it’s
a static method, which creates a new array from a suitable object.
Each element of the object becomes an element of the array, and in
the case of a string, each individual character of the string becomes
an element, as you can see in Figure 6.3.
The Array.isArray() method is also called for the Array class. This
method checks whether an object is an Array object and returns a
truth value.
If you only know the size of an array but not its elements, you can
create a new Array object using new. The desired size is indicated
within the parentheses.
The fill() method is used to fill the array with a value, as follows:
If the method is only called with one parameter, all array elements
receive the specified value, which in this case is 42.
An optional second parameter represents the index from which the
array is filled with the specified value. which in this case is from
index 3.
An optional third parameter stands for the index up to which
(excluded) the array is filled with the specified value, which in this
case is from index 3 to before index 5.
If you access an element that has not yet been assigned a value,
undefined will be output, in this case for the za[0] element. If you
access an element outside the array, which here is za[7], undefined
will be output as well. You can see the output in Figure 6.4.
Figure 6.4 Filling Array
Note
ECMAScript 2024 introduces the option of making arrays immutable
using the # character. For example, let person = #["Ben", "Angela",
"Will"]; means that the values of the array can no longer be
changed. The person[0] = "Monica"; statement will then result in an
error.
document.write("<br>Method at():<br>");
for(let i=0; i<za.length; i++)
for(let k=0; k<za.at(i).length; k++)
document.write(i + "/" + k + ":" + za.at(i).at(k) + "<br>");
if(Array.prototype.flat)
{
const zb = za.flat();
document.write("<br>After conversion, length: "
+ zb.length + "<br><br>");
}
...
Listing 6.5 array_multi_dim.htm File, Part 1 of 2
The at() method introduced in ECMAScript 2022 can also be used for
multidimensional arrays. A chained call is used to output a single
element: in the subarray that is accessed via at(i), the element that is
accessed via at(k) gets accessed.
The flat() method was introduced with ECMAScript 2019. It can use
internal recursion to create a one-dimensional array from the elements
of a multidimensional array, and its number is output here.
Figure 6.5 Two-Dimensional Array
Note
Note
The number of elements in the various subarrays could also be
different.
if(Array.prototype.flat)
{
const zd = zc.flat(2);
document.write("<br>After conversion with depth 2, length: "
+ zd.length);
}
</script></p>
</body></html>
You can call the flat() method with an optional value to specify the
depth of the internal recursion up to which the elements of an array
are collected. The default value for the depth is 1, but to collect all
elements of a three-dimensional array, you must specify depth 2.
document.write("Array as parameter:<br>");
const za = [ 13, -2 ];
document.write(za + "<br>");
doubleOneDim(za);
document.write(za + "<br>");
...
There are some predefined methods for the Array object that use
callback functions. In this example, some named callback functions
are passed, and I have defined them close to their call for clarification.
You can also pass anonymous callback functions.
Here’s the program:
...
<body><p>
<script>
const za = [28.3, 16.5, 23.6, 16.7];
document.write("Array: " + za + "<br><br>");
document.write("Apply to function:<br>");
function multiple(value)
{ return value * this; }
const zc = za.map(multiple, 4);
document.write("New array with values * 4: " + zc + "<br>");
</script></p>
</body></html>
When calling one of the Array methods, you can also pass an
additional parameter in addition to the name of the callback function.
You can access this parameter within the callback function using this.
The callback function over() then checks whether some (or all)
elements have a value that is greater than the value of the additional
transferred parameter.
The Array method map() also creates a new Array object, which
contains copies of all elements of the original array. The multiple()
callback function is called here and multiplies the values of all
elements by a factor that is passed as an additional parameter.
city.push("Bern", "Brussels");
document.write("push: " + city + "<br>");
const p = city.pop();
document.write("pop: " + city + "<br>");
document.write("Removed: " + p + "<br><br>");
city.unshift("Madrid", "London");
document.write("unshift: " + city + "<br>");
const s = city.shift();
document.write("shift: " + city + "<br>");
document.write("Removed: " + s + "<br><br>");
city.splice(1, 2);
document.write("splice to delete: " + city + "<br><br>");
delete city[1];
document.write("delete: " + city);
</script></p>
</body></html>
An array with city names is created and changed several times. The
array is output after each change, as you can see in Figure 6.10.
You can use the push() method to append one or more elements to
the end, and you do this in the order in which the elements are
specified as parameters when called. The pop() method removes an
element from the end of the array and returns this element as the
return value.
The unshift() and shift() methods do the same thing with regard to
the start of the array. The shift() method returns the removed
element as the return value.
Figure 6.10 Adding and Removing Elements
The splice() method changes the array at any point. The first
parameter specifies the position at which the removal or addition of
elements begins, the second parameter determines how many
elements are removed, and from the third parameter forward, you can
specify one or more new elements to be added. If you don’t specify a
new element, only existing elements will be removed.
The toSpliced() method works like the splice() method, but the
result is saved in a new array (here, in the cityTwo array).
The delete operator only deletes the content of an element, not the
element itself. This creates a gap, so to avoid such a gap, you can use
splice() instead of delete.
You can sort an array alphabetically using the sort() method. The
order is based on the UTF-16 code, which is an encoding for Unicode
characters. Among other things, uppercase letters have priority over
lowercase letters, so the string oslo with a lowercase o would be
located at the end. I describe sorting an array by numerical values in
Section 6.1.8.
The reverse() method reverses the order of the elements. As the
array was previously sorted, it’s sorted in reverse in this case.
The join() method is used to convert an array into a string. The string
contains all elements of the array, and each element is separated by
the string that is specified as a parameter when join() gets called. For
the reverse operation (i.e., the conversion of a string into an array),
the split() method is available (Section 6.2.2).
The copyWithin() method can be called with up to three parameters.
The first parameter specifies the position from which the copied
elements overwrite the original elements. The elements copied there
are selected as follows: the second parameter specifies the position of
the first element, and the third parameter specifies the position after
the last element. If the third parameter is omitted, all elements from
the first element onwards are copied.
The already known array of city names serves as the original array.
The slice() method is used to create a new array that contains
specific (or all) elements of the original array, and the method has two
optional parameters that are used to specify the position.
If the method is called without parameters, the entire original array will
get copied to a new array. If the method is called with one parameter,
all elements from the specified position onwards are copied to a new
array. If a call is made with two parameters, the new array contains
the elements from the first specified position to before the second
specified position.
function descending(x, y)
{
if(y > x) return 1;
else return -1;
}
</script>
</head>
<body><p>
<script>
const za = [3, 28, 2, 14];
document.write("Unsorted: " + za + "<br>");
za.sort();
document.write("Sorted by characters: " + za + "<br>");
za.sort(ascending);
document.write("Sorted by numbers, ascending: " + za + "<br>");
za.sort(descending);
document.write("Sorted by number, descending: " + za + "<br><br>");
const zb = [3, 28, 2, 14];
const zc = zb.toSorted(ascending);
document.write("Sorted to a new array: " + zc);
</script></p>
</body></html>
When calling the sort() method, you can pass the reference to a
comparison function to be used for sorting. In the first case, you want
to sort by number in ascending order. Here, a reference to the
ascending() function is passed to the method, and this function is
used internally by the sort() method to compare two elements.
If the first element is larger than the second element in this
comparison, a positive value is returned; otherwise, a negative value
is returned. This results in the two compared elements being sorted in
ascending order. This is carried out repeatedly for two elements
during the internal sorting process of the sort() method, so at the end,
all elements of the array are sorted in ascending order.
function elementGreaterValue(element)
{ if(element > this) return element; }
document.write("First element > 10: "
+ za.find(elementGreaterValue, 10) + "<br>");
document.write("Last element > 10: "
+ za.findLast(elementGreaterValue, 10) + "<br>");
The array [12, 15, –6] is assigned to the three variables in a single
statement. If there are too few variables, the remaining variables are
assigned the undefined value. If there are too many variables, the
array elements expire.
Using the spread operator ..., you can make sure that surplus array
elements are assigned to another array (here, remainder).
You can also use the spread operator ... to make a function more
flexible. You can pass two or more parameters to the add() function,
which adds up all parameters. The first two parameters are stored in
the a and b variables, while all other parameters are stored in the
remainder array. If there are less than two parameters, an error will
occur.
toString()
{
return this.color + "/" + this.speed;
}
}
function descending(x, y)
{
if(y.speed > x.speed)
return 1;
else
return -1;
}
</script>
</head>
<body><p>
<script>
const vc = [new Car("Red", 50),
new Car("Blue", 85), new Car("Yellow", 65)];
document.write("unsorted: " + vc + "<br>");
vc.sort(descending);
document.write("sorted: " + vc);
</script></p>
</body</html>
The program creates three Car objects and saves the references to
these objects in an array named vc. The toString() method is
available for both Array objects and Cars, so the entire array can be
output using the document.write() method.
The array is sorted in descending order of speed, and when the sort()
method is called, the name of a callback function that makes this
possible gets transferred. The two elements of the array that are
compared internally are references to objects of the Car class. By
comparing the two values of the speed property, the callback function
returns a positive or negative value., which ensures that the array will
be sorted correctly. The result is shown in Figure 6.18.
Figure 6.18 Arrays of Objects with Sorting
Note
You can find the u_one_dimensional and u_multi_dimensional
exercises in bonus chapter 1, sections 1.15 and 1.16, in the
downloadable materials for this book at www.rheinwerk-
computing.com/5875.
6.2 Processing Strings
You’ve already become familiar with strings, which are similar to
arrays and are used to store texts that originate from input, for
example. The individual characters of a string are numbered, starting
at 0, and the number of a single character in a string is also referred to
as the index.
The String object with its properties and methods can be used to read
and change strings. In some examples, you’ve already seen the +
operator for concatenating strings and the combined assignment
operator += for extending a string.
You can use the split() method to split a string using a separator
string. An Array object is created with the individual parts of the string,
and in this context, the join() method of the Array object is shown
once again, and it in turn joins the array into a character string.
The individual characters are output together with the index in two
different ways, using two for loops.
The string is then split using the split() method based on the dots
(i.e., using the "." separator string). The individual array elements
don’t contain the separator string itself. Using the Array method join()
and the same separator string, a new string is created from the array
elements.
The String.fromCodePoint() method is called for the String object
itself and not for an existing string, as there’s no string at this point.
Such a method is referred to as a static method (see also
Section 6.1.1).
First, the program determines the position of the first or last character
i within the string.
In the indexOf() method, you can optionally specify the position from
which the search should start. Accordingly, the lastIndexOf() method
can optionally specify the position up to which the search should
continue.
You can use a while loop to search for the positions of all characters
i. The search for the next occurrence of the character i always starts
at the position where the last occurrence had previously been found.
If you specify only one parameter each for the substring() and
substr() methods, the part of the string that starts at the specified
position and extends to the end of the string will be returned. The
second, optional parameter in the substring() method specifies which
character is to be the first to no longer be copied. In contrast, you can
use the substr() method to specify the number of characters to be
copied.
You can see the output in Figure 6.21.
const ha = "Hello";
document.write("Repeat: " + ha.repeat(5) + "<br><br>");
document.write("Padding at the beginning: "
+ ha.padStart(10,"xy") + "<br>");
document.write("Padding at the end: " + ha.padEnd(10,"xy") + "<br><br>");
The "Hello" string is returned in five repetitions, and then, it’s filled
with up to ten characters using the "xy" string, once at the beginning
and once at the end. The "xy" string is repeated twice completely and
once incompletely.
Another string contains a total of thirteen characters before trimming.
There are some whitespace characters at the beginning and end:
space (code point 32), tabulator (code point 9), and the characters for
the end of the line (code point 10 or 13). After trimming at the
beginning, there are only ten characters left, after trimming at the end,
there are only eight characters left, and after all the trimming, there
are only 5 characters left. For clarification, the | character is displayed
before and after the string.
function submit(e)
{
const pw = document.getElementById("idPassword").value;
if(pw.length < 8 || pw.length > 12)
{
alert("Password must contain 8-12 characters");
e.preventDefault();
return false;
}
let l = 0, u = 0, n = 0, s = 0;
for(let i = 0; i < pw.length; i++)
{
if(lowercase.includes(pw[i])) l++;
if(uppercase.includes(pw[i])) u++;
if(number.includes(pw[i])) n++;
if(special.includes(pw[i])) s++;
}
if(l < 2 || u < 2 || n < 2 || s < 1)
{
alert("Minimum number not met");
e.preventDefault();
return false;
}
return true;
}
</script>
</head>
<body>
<form id="idForm" method="post" action="string_password.php">
<p><input id="idPassword" type="password" name="password"> Password</p>
<p><input type="submit"> <input type="reset"></p>
<p>min. 8 characters, max. 12 characters<br>
min. 2 lowercase letters<br>
min. 2 uppercase letters<br>
min. 2 digits<br>
min. 1 special character from "!&?+-*"</p>
</form>
<script>
document.getElementById("idForm").addEventListener
("submit", function(e) {return submit(e);});
</script>
</body></html>
If the string is of the correct length, some counters are first declared
and set to 0. The individual characters of the input are then run
through using a for loop, and the includes() method is used to
determine whether they are one of the permitted characters. If they
are, the corresponding counter will be increased.
After the loop, it’s determined whether any of the counter values don’t
meet the criteria for the respective minimum number. If it doesn’t, a
corresponding error message will also be displayed here, the function
will terminate, and the form will not be submitted.
If all criteria are met, the form gets submitted.
Note
In the following program, some characters from the field of emojis are
shown, and then some tests are carried out:
...
<body><p>
<script>
document.write("\uD83D\uDE00" + "\uD83D\uDE0F" + "<br>");
document.write("\uD83D\uDE10" + "\uD83D\uDE1F" + "<br>");
document.write("\uD83D\uDE20" + "\uD83D\uDE2F" + "<br>");
document.write("\uD83D\uDE30" + "\uD83D\uDE3F" + "<br>");
document.write("\uD83D\uDE40" + "\uD83D\uDE4F" + "<br>");
document.write("Hello".isWellFormed() + "<br>");
document.write("\uDE00".isWellFormed() + "<br>");
document.write("\uD83D\uDE00".isWellFormed() + "<br>");
</script></p>
</body></html>
const c = 64;
output += "c:" + c + " sqrt(c):" + Math.sqrt(c)
+ " cbrt(c):" + Math.cbrt(c) + "<br>"
+ "pi:" + Math.PI + " e:" + Math.E + "<br>"
+ "exp(4.6):" + Math.exp(4.6)
+ " log(100):" + Math.log(100) + "<br>"
+ "pow(10,3):" + Math.pow(10,3)
+ " log10(1000):" + Math.log10(1000);
document.write(output);
</script></p>
</body></html>
The abs() method calculates the absolute value of a number (i.e., the
number without its sign). The sign() method has been available since
ECMAScript 2015, and it determines the sign of a number and returns
the value 1 for a positive number, –1 for a negative number, and 0 for
the number 0. The max() and min() methods return the largest or
smallest number of any given set of numbers.
The sqrt() method determines the square root of a number, while the
cbrt() method determines the cube root. The PI and E properties
contain the mathematical constant for Pi (π) or Euler’s number e (i.e.,
the base of the exponential function).
The sin(), cos(), and tan() methods of the Math object calculate the
values based on an angle in radians. The 360 degrees of a full circle
correspond to 2 π in radians, so an angle in degrees must be
converted into radians using the factor π/180.
The first step consists of the conversion to radians. The results are
rounded to three decimal places using the toFixed() method of the
Number object, but unfortunately, the conversion from 90 degrees (=
π/2) to radians is not mathematically exact, so the tangent of 90
degrees doesn’t have a value of infinity.
The output of the program is shown in Figure 6.26.
Figure 6.26 Table with Trigonometric Functions
document.write(output);
</script></p>
</body></html>
For example, the output of the program can look like the one shown in
Figure 6.27.
Figure 6.27 Random Generators and Typed Arrays
There are different types of typed arrays, both for integers and for
numbers with decimal places. At this point, only the Int32Array and
Uint32Array types are described. Using these types, you can store
integers in a storage area with a size of 32 bits = 4 bytes.
A number from –2,147,483,648 to +2,147,483,647 can be stored in an
element of an Int32Array. The U stands for unsigned, and only positive
numbers from 0 to +4,294,967,295 can be stored in an element of a
Uint32Array.
6.3.4 Integers
The Number object provides properties and methods related to the
precision and the size of the number range in JavaScript. In addition, I
present the BigInt object for very large integers. Here’s a sample
program:
...
<body><p>
<script>
let nu = Number.MAX_SAFE_INTEGER;
document.write("Largest safe integer: " + nu + "<br>");
document.write("Safe integer: "
+ Number.isSafeInteger(nu) + "<br>");
nu = nu + 1;
document.write("+1: Safe integer: "
+ Number.isSafeInteger(nu) + "<br>");
nu = Number.MIN_SAFE_INTEGER;
document.write("Smallest safe integer: " + nu + "<br><br>");
nu = Number.MAX_SAFE_INTEGER;
x = nu + 1;
y = nu + 2;
document.write("Error without BigInt:<br>" + x + "<br>" + y + "<br><br>");
nu = Number.MIN_VALUE;
document.write("Number with the lowest value: " + nu + "<br>");
nu = nu / 10;
document.write("Reduced: " + nu + "<br><br>");
nu = Number.EPSILON;
document.write("ε = Smallest distance: " + nu + "<br>");
nu = 1 + Number.EPSILON;
document.write("1 + ε: " + nu.toFixed(40) + "<br>");
nu = 1 + Number.EPSILON / 2;
document.write("1+ ε/2: " + nu.toFixed(40) + "<br><br>");
ar = new Float64Array(2);
ar[0] = 1000 / 7;
document.write("Float64: " + ar[0].toFixed(20) + "<br>");
</script></p>
</body></html>
Note
You can call the method with one or two parameters. The first
parameter stands for the desired total number of digits in the output,
and you can pass a string as the second parameter, which serves as
a padding character. If there’s no second parameter, it’s filled with
zeros.
The method is defined in an external file named number.js. You can fill
this file with other useful methods for numbers, and after integrating
the file, you can use those methods.
Here’s the program that uses the method from the external file:
... <head> ...
<script src="number.js"></script>
</head>
<body>
<script>
const x = 15;
document.write(x.format(5) + "<br>");
document.write(x.format(5, "#"));
</script>
</body></html>
The program assigns a number with two digits. For the output, it’s
filled up to five digits. For the first output, the character 0 is used for
padding; for the second output, the hash character #.
You can see the output in Figure 6.30.
You can transfer one or two parameters. The first parameter specifies
the desired number of digits, and the second parameter corresponds
to the padding character, and it has the character 0 as the default
value. A different padding character can be passed as the second
parameter during the call.
You can access the Number object for which the method is called via
this. The content of the object (i.e., the number) is converted into a
string using the toString() method.
As long as the length of this string is less than the required number of
digits, the padding character is added at the beginning. The filled
string then gets returned.
6.4 Using Time Specifications
The Date object provides numerous options for calculating and
outputting date and time information.
The point of origin for the Date object is January 1, 1970, at 00:00
Greenwich Mean Time (GMT), although today, we use Coordinated
Universal Time (UTC). Date objects had negative values before that
point in time and positive values afterward. The times are calculated in
milliseconds.
The Date objects created are output in the default format, which
contains the abbreviated English name of the day of the week and the
month, as well as the day of the month, the year, and the time. Further
output options can be found in Section 6.4.2.
GMT+0100 means that the local time is one hour ahead of GMT, due
to a time difference of one hour compared to London-Greenwich. In
the case of a further time difference of one hour due to daylight saving
time, GMT+0200 would appear. The names Central European
Standard Time (CET) and Central European Summer Time (CEST)
are also displayed.
The other time specifications are generated using transferred values.
The entries 2024, 6, 10 become July 10, 2024, and the time is set to
00:00:00 or 17:08:03.
Here are some values to convert: February 30, 2024, becomes March
1, 2024, and 17:68 becomes 18:08. If we subtract 3 seconds, it
becomes 18:07:57.
When calling the dateFormat() method, you can pass a string with
format specifications that provide the following information:
w: weekday
d: day of the month, two digits
M: number of the month, two digits
y: year, four digits
h: hour, two digits
m: minute, two digits
s: second, two digits
i: milliseconds
The two-digit values are output with a leading zero if necessary. Other
characters, such as : (colon) or . (dot), are not converted but are
simply output at the relevant position.
Here’s the program:
... <head> ...
<script src="number.js"></script>
<script src="date.js"></script>
</head>
<body><p>
<script>
const time = new Date();
const output = "Time object: " + time + "<br>"
+ "Local format: " + time.toLocaleString() + "<br>"
+ "UTC format: " + time.toUTCString() + "<br>"
+ "Local offset from UTC: "
+ (time.getTimezoneOffset()/60) + " hour(s)<br>"
+ "Default format: " + time.dateFormat("M/d/y h:m:s") + "<br>"
+ "With weekday: " + time.dateFormat("w, M/d/y") + "<br>"
+ "With milliseconds: " + time.dateFormat("h:m:s,i") + "<br>";
document.write(output);
</script></p>
</body></html>
Two extensions are integrated: first, the extension of the Number object
from the number.js file (Section 6.3.5), and second, the extension of
the Date object from the date.js file (Section 6.4.3). Internally, the
dateFormat() method uses the format() method from the number.js
file to output the leading zeros.
You can access the Date object for which the method is called via
this. Within the for loop, each individual character is examined using
a switch branch. The different format characters lead to calls of
various methods of the Date object: getDay(), getDate(), getMonth(),
getFullYear(), getHours(), getMinutes(), getSeconds(), and
getMilliseconds(). There are a few special features you should note:
The getMonth() method returns a value between 0 and 11, so the
value 1 must be added for the output.
For some values, the format() method of the Number object is called
to output a leading zero.
The last method is defined in the external date.js file, just like the
separate dateFormat() method.
Two Date objects are created, each with predefined data for date and
time. To calculate the date and time difference between them, you can
simply subtract one from another. A time specification in a Date object
is stored internally as the number of milliseconds since the point of
origin (i.e., since January 1, 1970, 00:00). Consequently, the
difference is a number as well. To represent it in minutes, divide it by
60,000.
The program queries the current system time twice, once before a
program section and once after. In this way, the program calculates
the amount of time the computer needs to process a program section
in which a for loop is run very frequently. The period depends on the
performance and the current utilization of the computer.
Note
The parentheses within the linked condition are not necessary and
are for clarification purposes only. The logical AND operator takes
precedence over the logical OR operator.
First, the program generates a single time entry, and then, it creates
the second Date object using the dateAdd() method. This is another
self-written extension of the Date object. Internally, a number of
predefined methods are used (Section 6.4.5).
When you initiate the call, you can transfer a time value with a total of
six partial values that will be added or subtracted. The six partial
values represent the year, month, day, hour, minute, and second in
sequence.
A new Date object (time) is also created, and a reference to this object
is returned as the result of the calculation. A total of six of the
predefined set methods are called to calculate the individual partial
values for the new object, and to calculate each partial value, the
corresponding parameter is added to the corresponding partial value
of the current object. If the partial values are too large or too small,
they’ll be converted accordingly. This applies, for example, to an entry
such as –10 minutes or +50 days.
Note
You can find the u_time_estimate exercise in bonus chapter 1,
section 1.17, in the downloadable materials for this book at
www.rheinwerk-computing.com/5875.
6.5 Time-Controlled Processes
You’ve already become familiar with the alert(), prompt(), and
confirm() methods of the window object. There are also the following
other methods of the window object that enable you to control time
processes, as required for games or simulations, for example:
The setTimeout() method, which ensures that statements are called
once after a certain time
The setInterval() method, which makes sure the scheduled
repeated call of statements after a certain time gets carried out
function timeoutDateTime()
{
const d = new Date();
document.getElementById("idTimeout").firstChild.nodeValue =
"With Timeout: " + d.dateFormat("M/d/y h:m:s");
setTimeout(timeoutDateTime, 100);
}
</script>
</head>
<body>
<p id="idInterval">With Interval:</p>
<p id="idTimeout">With Timeout:</p>
<script>
setInterval(intervalDateTime, 100);
setTimeout(timeoutDateTime, 100);
</script>
</body></html>
First, the time at which the process starts is saved in the timeA
variable, and then the setInterval() method is called. The latter also
has a return value that references the process, and this reference is
saved in a variable.
The countdown() function determines the time difference from the start
in seconds, and it then calculates and outputs the remaining time. If
this is less than 0.06 seconds, the clearInterval() method gets called
to end the process. The saved reference of the setInterval() method
is used as a parameter.
Note
function output()
{
document.getElementById("idOutput").firstChild.nodeValue += "x";
}
function start()
{
if(intervalActive)
return;
intervalActive = true;
intervalReference = setInterval(output, 500);
}
function pause()
{
intervalActive = false;
clearInterval(intervalReference);
}
function stop()
{
intervalActive = false;
clearInterval(intervalReference);
document.getElementById("idOutput").firstChild.nodeValue = "Output: ";
}
</script>
</head>
<body>
<p><input id="idStart" type="button" value="Start">
<input id="idPause" type="button" value="Pause">
<input id="idStop" type="button" value="Stop">
<span id="idOutput">Output: </span></p>
<script>
document.getElementById("idStart").addEventListener("click", start);
document.getElementById("idPause").addEventListener("click", pause);
document.getElementById("idStop").addEventListener("click", stop);
</script>
</body></html>
Listing 6.44 process_control.htm File
You can click the <<, <, >, or >> button to immediately display the next
image, the previous image, the first image, or the last image
(respectively). The number of the current image and the total number
of images are displayed. You can also start and stop a slideshow
using the Start slideshow and End slideshow buttons.
Let’s first look at the lower part of the program:
...
<body>
<p><img id="idImage" src="im_paradise.jpg" alt="Paradise"></p>
<p><input id="idFirst" type="button" value="<<">
<input id="idBack" type="button" value="<">
<input id="idNext" type="button" value=">">
<input id="idLast" type="button" value=">>">
<script>
document.write("<span id='idOutput'>1/" + image.length + "</span></p>");
</script>
<p><input id="idStart" type="button" value="Start slideshow">
<input id="idStop" type="button" value="End slideshow"></p>
<script>
document.getElementById("idFirst").addEventListener
("click", function() { move(0); } );
document.getElementById("idBack").addEventListener
("click", function() { move(imageIndex - 1); } );
document.getElementById("idNext").addEventListener
("click", function() { move(imageIndex + 1); } );
document.getElementById("idLast").addEventListener
("click",function(){move(image.length - 1);});
document.getElementById("idStart").addEventListener("click", start );
document.getElementById("idStop").addEventListener("click", stop );
</script>
</body></html>
The first four buttons use an anonymous function to call the move()
function. A different parameter is passed each time the move() function
is called. The image variable references an array that contains the
names of the image files, and the imageIndex variable contains the
index of the currently displayed image within the image array.
To display the next image, imageIndex gets increased by 1, and to
display the preceding image, it gets decreased by 1. The first image
has the index 0, and the last image has the index image.length - 1 .
The next two buttons call the start() and stop() functions to start and
stop the slideshow.
Now, here’s the first part of the program:
... <head> ...
<script>
const image = ["im_paradise.jpg", "im_solar_eclipse.jpg",
"im_wave.jpg", "im_winter.jpg", "im_tree.jpg"];
let imageIndex = 0, timeoutReference, timeoutActive = false;
function move(number)
{
imageIndex = number;
if(imageIndex >= image.length)
imageIndex = 0;
else if(imageIndex < 0)
imageIndex = image.length - 1;
document.getElementById("idImage").src = image[imageIndex];
document.getElementById("idOutput").firstChild.nodeValue =
(imageIndex+1) + "/" + image.length;
if(timeoutActive)
timeoutReference = setTimeout(function() {move(imageIndex+1);}, 1000);
}
function start()
{
if(timeoutActive)
return;
timeoutActive = true;
timeoutReference = setTimeout(function() {move(imageIndex+1);}, 500);
}
function stop()
{
clearTimeout(timeoutReference);
timeoutActive = false;
}
</script>
</head>
...
First, the program creates the array with the names of all image files.
If you want to display your own images in a slideshow, you only need
to change this part of the program.
The current index for this array is set to 0, and the timeoutReference
variable references the time-controlled process. The timeoutActive
variable stands for the information: the slideshow is currently active, or
the slideshow is not running.
If the slideshow is currently active, the move() function calls itself again
after a short time using an anonymous function. The parameter is then
determined using the current value of imageIndex.
If the slideshow is already running, the start() function gets exited
immediately. Otherwise, the first image change for the slideshow is
caused here. The slideshow is ended in the stop() function.
6.6 Other Data Structures
Data structures are used to store a group of thematically related data
under a common name. The data consists of individual elements that
usually have the same type but can also have different types.
In Section 6.1, you learned about the array data structure. In this
section, we add more data structures called sets and maps.
6.6.1 Sets
A set is a collection of unique values, which means that each value
only occurs once within a set. In JavaScript, the Set object is used to
create a set. A set has properties and methods that you can use to
access its values.
The following program shows some typical operations with sets:
...
<body><p>
<script>
const s = new Set([23, 5, 28, -4, 5, 8, 23]);
s.add(5);
s.add(36);
let v = 23;
if(s.has(v))
document.write("Value " + v + " is in the set<br>");
v=24;
if(!s.has(v))
document.write("Value " + v + " is not in the set<br>");
You can fill a set with values when it gets created. For this purpose,
an enumeration is passed in square brackets containing the possible
values. If one of the values occurs twice, it’s only added to the set
once.
The add() method is used to add individual values. Here, the same
principle applies: if the value already exists, it will not be added.
You can use the forEach() method to run through a set completely, in
the order in which the values are added. The reference to a function
that is executed for each value serves as a parameter. Here, it’s the
output() method for displaying the value. What is not visible to us is
that the forEach() method makes sure the value gets passed to the
output() method as the first parameter.
6.6.2 Maps
In JavaScript, the Map object is used to create an associative array,
which we refer to as a map for short. A map consists of a collection of
key-value pairs, and you can access the individual values by using a
key that consists of a string, for example. In comparison, with a
standard array (Section 6.1), you access the individual values using a
numerical index. A map has properties and methods with which you
can access its keys and values, and there are some parallels to the
properties and methods of a set.
let s = "Julia";
if(m.has(s))
document.write("Key:" + s + ", Value:" + m.get(s) + "<br>");
s = "Ben";
if(!m.has(s))
document.write("Key:" + s + ", not available" + "<br>");
You can fill a map when you create it. For this purpose, an
enumeration of key-value pairs is transferred. Each pair and the list as
a whole are enclosed in square brackets.
The set() method is used to add individual pairs. If the key for the pair
already exists, the corresponding value will be overwritten.
You can use the forEach() method to run through a map completely in
the order in which the pairs are added. The reference to a function
that is executed for each value serves as a parameter, and here, it’s
the output() method for displaying the pairs. What is not visible to us
is that the forEach() method makes sure that the key and value are
passed as parameters to the output() method.
The values() method returns an iterator (Section 6.6.1) with all the
values of the map, and an iterator with all the keys of the map is
provided by the keys() method. The entries() method returns an
iterator with all pairs of the map, and the value of the size property
corresponds to the number of pairs in a map. In a for loop, these
relationships can be used to output all values, keys, or pairs of the
map. The delete() method is used to delete a specific pair from the
map, and the clear() method deletes all pairs from the map.
You can find the u_map exercise in bonus chapter 1, section 1.18, in
the downloadable materials for this book at www.rheinwerk-
computing.com/5875.
6.7 JavaScript Object Notation
JavaScript Object Notation (JSON) is the name for a compact notation
that can be applied to objects and arrays. We refer to this notation as
JSON format.
Modern browsers are familiar with the JSON object, which has static
methods that you can use to convert an object that is in JSON format
to a transportable string. In turn, you can create an object in JSON
format from such a string.
const tx = JSON.stringify(dodge);
document.write(tx + "<br>");
Note
toString()
{
return this.color + " " + this.speed;
}
}
</script>
</head>
<body><p>
<script>
const dodge = new Car("Red", 50.2);
document.write(dodge + "<br>");
const tx = JSON.stringify(dodge);
document.write(tx + "<br>");
const x = JSON.parse(tx);
const renault = new Car(x.color, x.speed);
document.write(renault + "<br>");
renault.speed += 2.5;
document.write("Faster: " + renault.speed);
</script></p>
</body></html>
const tx = JSON.stringify(city);
document.write(tx + "<br>");
const tx = JSON.stringify(cr);
document.write(tx + "<br>");
document.write("<br>Faster: ");
for(let i=0; i<crNew.length; i++)
{
crNew[i].speed += 2.5;
document.write(crNew[i].speed + " # ");
}
</script></p>
</body></html>
toString()
{
return this.color + " " + this.speed;
}
}
</script>
</head>
<body><p>
<script>
const cr = [new Car("Red", 50.2),
new Car("Blue", 85.3), new Car("Yellow", 65.1)];
for(let i=0; i<cr.length; i++)
document.write(cr[i] + " # ");
document.write("<br>");
const tx = JSON.stringify(cr);
document.write(tx + "<br>");
document.write("<br>Faster: ");
for(let i=0; i<crNew.length; i++)
{
crNew[i].speed += 2.5;
document.write(crNew[i].speed + " # ");
}
</script></p>
</body></html>
The program converts the array of objects of the custom Car class into
a transportable string using the stringify() method of the JSON class.
The output of the program also looks like the one shown in
Figure 6.43. JSON files are imported in Chapter 7, Section 7.4.
Note
All documents in this chapter are accessed via a web server from
which you request data to insert into the pages. In this chapter, I
retrieve the documents via my domain theisweb.de from the jse
directory. The data can be available on the web server in various
forms:
As a PHP program (Section 7.1)
As text in a text file (as shown at the end of Section 7.1)
As an XML file (Section 7.3)
As a JSON object in a text file (Section 7.4)
Let’s first look at the HTML document with the JavaScript code:
... <head> ...
<script>
function request()
{
const req = new XMLHttpRequest();
req.open("get", "ajax_hello.php", true);
// req.open("get", "ajax_hello.txt", true);
req.onreadystatechange = evaluate;
req.send();
}
function evaluate(e)
{
if(e.target.readyState == 4 && e.target.status == 200)
document.getElementById("idParagraph")
.firstChild.nodeValue = e.target.responseText;
}
</script>
</head>
<body>
<p><a id="idLink" href="#">Please click</a></p>
<p id="idParagraph"> </p>
<script>
document.getElementById("idLink").addEventListener("click", request);
</script>
</body></html>
The ajax_hello.htm file contains the hyperlink and the paragraph, and
clicking on the hyperlink calls the request() function. First, a new
XMLHttpRequest object is created.
The open() method of this object opens communication with another
file on the web server, in this case, with ajax_hello.php and the GET
method. The third parameter of the open() method is usually set to
true. This ensures that communication is handled asynchronously,
which means other processes don’t have to wait for the end of the
request.
Note
When you click the first hyperlink, the system requests data on the
person with personnel number 6714, and when you click the second
hyperlink, it requests the data for the person with personnel number
81343. The data on the person then appears below the hyperlinks
(see Figure 7.4 and Figure 7.5).
Figure 7.4 All Data for Personnel Number 6714
Let’s first look at the HTML document with the JavaScript code:
... <head> ...
<script>
function request(personnel_number, scope)
{
const req = new XMLHttpRequest();
req.open("get", "ajax_parameters.php?pno=" + personnel_number
+ "&scope=" + scope, true);
req.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
req.onreadystatechange = evaluate;
req.send();
}
function evaluate(e)
{
if(e.target.readyState == 4 && e.target.status == 200)
document.getElementById("idParagraph").firstChild.nodeValue =
e.target.responseText;
}
</script>
</head>
<body>
<p>Personnel number:</p>
<p><a id="idLink0" href="#">6714</a></p>
<p><a id="idLink1" href="#">81343</a></p>
<p id="idParagraph"> </p>
<script>
document.getElementById("idLink0").addEventListener
("click", function() { request(6714, "all"); });
document.getElementById("idLink1").addEventListener
("click", function() { request(81343, "part"); });
</script>
</body></html>
if($_GET["pno"] == 6714)
{
if($_GET["scope"] == "all")
echo "6714, Mayer, John, $3,500, born on 03/15/1962";
else
echo "6714, Mayer, John";
}
else if($_GET["pno"] == 81343)
{
if($_GET["scope"] == "all")
echo "81343, Smith, Peter, $3,750, born on 04/12/1958";
else
echo "81343, Smith, Peter";
}
?>
Modern browsers can display the structure of an XML file directly, and
this holds true regardless of whether the XML file is retrieved via a
web server or directly from the local hard disk. In Figure 7.6, you can
see the specified XML file.
An XML file can be linked to another file for formatting the XML data,
but this is not the case in the examples in this chapter. For this
reason, the note with the text This XML file does not appear … is
provided in Figure 7.6.
The first line of the XML code identifies the content as XML. The
following character encoding specifies the character set used, as for
HTML documents. There’s a main element or root node in every XML
file, and you can freely choose the names of the individual nodes
yourself, as long as you adhere to the XML rules and remain true to
your own structure.
The root node here is the car element. A car has three properties with
values that are defined as child nodes, and the third child node has
two attributes, each of which has values. If a value is a number with
decimal places, these must be separated by a decimal point.
This data is read from the XML file after you click on the hyperlink
using Ajax, and it fills the paragraph (see Figure 7.7).
Figure 7.7 Data of Single Object
function evaluate(e)
{
if(e.target.readyState == 4 && e.target.status == 200)
{
const response = e.target.responseXML;
const kcolor = response.getElementsByTagName("color")[0];
const kpower = response.getElementsByTagName("power")[0];
document.getElementById("idData").firstChild.nodeValue =
"Color: " + kcolor.firstChild.nodeValue
+ ", Power: " + kpower.firstChild.nodeValue
+ ", Displacement: " + kpower.getAttribute("displacement")
+ ", Cylinders: " + kpower.getAttribute("cylinders");
}
}
</script>
</head>
<body>
<p>Car data:</p>
<p><a id="idLink" href="#">Dodge</a></p>
<p id="idData"> </p>
<script>
document.getElementById("idLink").addEventListener("click", request);
</script>
</body></html>
The first child node of the first element is output using the firstChild
property of a node object. The value of two different attributes is then
determined and output using the getAttribute() method of a node
object.
In Figure 7.8, you can see the XML file without formatting.
There are several nodes of the car type. Another node must be added
as a parent so that there’s still only one main element or root node.
This element has the collection tag and two child nodes of the car
type described earlier.
After you click on one of the hyperlinks in Figure 7.9, the initially empty
paragraph is filled using Ajax.
function evaluate(e, x)
{
if(e.target.readyState == 4 && e.target.status == 200)
{
const response = e.target.responseXML;
const kcolor = response.getElementsByTagName("color")[x];
const kpower = response.getElementsByTagName("power")[x];
document.getElementById("idData").firstChild.nodeValue =
"Color: " + kcolor.firstChild.nodeValue
+ ", Power: " + kpower.firstChild.nodeValue
+ ", Displacement: " + kpower.getAttribute("displacement")
+ ", Cylinders: " + kpower.getAttribute("cylinders");
}
}
</script>
</head>
<body>
<p>Car data:</p>
<p><a id="idLink0" href="#">Dodge</a></p>
<p><a id="idLink1" href="#">Renault</a></p>
<p id="idData"> </p>
<script>
document.getElementById("idLink0").addEventListener
("click", function() { request(0); } );
document.getElementById("idLink1").addEventListener
("click", function() { request(1); } );
</script>
</body></html>
In Figure 7.10, you can see the result after you enter “B” into the
search field. The entry is extended to “Be” in Figure 7.11, and if you
delete the “e,” it will again look like Figure 7.10.
In Figure 7.12, you can see the XML file without any formatting.
The main element or the root node has the countries tag and child
nodes of the country type. For each object of the country type, there
are the name, area, and city child nodes.
The paragraph with the idParagraph ID contains two child nodes: the
search field with the idInput ID and the Search for EU countries text.
There’s also an event handler for the keydown event that is triggered
when you press a key.
Let’s now take a look at the upper part of the program with the
JavaScript functions:
... <head> ...
<script>
function request()
{
const req = new XMLHttpRequest();
req.open("get", "ajax_eu.xml", true);
req.onreadystatechange = evaluate;
req.send();
}
function evaluate(e)
{
if(e.target.readyState == 4 && e.target.status == 200)
{
const par = document.getElementById("idParagraph");
while(par.childNodes.length > 2)
par.removeChild(par.lastChild);
if(document.getElementById("idInput").value == "")
return;
if(search == name.substr(0,search.length))
{
const area = response.getElementsByTagName(
"area")[i].firstChild.nodeValue;
const city = response.getElementsByTagName(
"city")[i].firstChild.nodeValue;
Note
A text file contains data in JSON format, which is saved using PHP,
for example. We’ll integrate this data into an existing HTML document
using Ajax.
The text file contains the entire object in JSON format. Its data is read
from the text file using Ajax after the hyperlink has been clicked and
fills the paragraph (see Figure 7.13).
function evaluate(e)
{
if(e.target.readyState == 4 && e.target.status == 200)
{
const response = JSON.parse(e.target.responseText);
document.getElementById("idOutput").firstChild.nodeValue
= "Color: " + response.color
+ ", increased speed: " + (response.speed + 2.5);
}
}
</script>
</head>
<body>
<p>Car data:</p>
<p><a id="idLink" href="#">Dodge</a></p>
<p id="idOutput"> </p>
<script>
document.getElementById("idLink").addEventListener("click", request);
</script>
</body></html>
After you click one of the hyperlinks in Figure 7.14, Ajax fills the text
field below it.
function evaluate(e, x)
{
if(e.target.readyState == 4 && e.target.status == 200)
{
const response = JSON.parse(e.target.responseText);
document.getElementById("idOutput").firstChild.nodeValue
= "Color: " + response[x].color + ", increased speed: "
+ (response[x].speed + 2.5);
}
}
</script>
</head>
<body>
<p>Car data:</p>
<p><a id="idLink0" href="#">Dodge</a></p>
<p><a id="idLink1" href="#">Nissan</a></p>
<p><a id="idLink2" href="#">Renault</a></p>
<p id="idOutput"> </p>
<script>
for(let i=0; i<3; i++)
document.getElementById("idLink" + i).addEventListener
("click", function() { request(i); } );
</script>
</body></html>
When you click one of the hyperlinks, the program passes the
corresponding value as a parameter to the request() function. This
value then gets passed further on to the evaluate() function, using an
anonymous function. In this way, the appropriate element can be
determined from the array. The data of this element is returned and
fills the paragraph, after a calculation if necessary.
8 Design Using Cascading Style
Sheets
The three files in this section do not include the js5.css file, which
ensures a uniform display using style sheets in most of the examples
in this book. Instead, this section uses its own style sheets to
demonstrate the effects.
The external file contains a comment and a style sheet with an HTML
selector. This HTML selector has the effect that the contents of all b
containers have an overline in all files that include this external file.
The two paragraphs are overlined due to the HTML selector in the file
header. Within the list, there are two elements to which the over class
is assigned: the span element in the first list entry and the entire third
list entry. The assignment is made using the class attribute.
The second cell in the table has the idTop ID, and within the third cell,
an inline specification is made using the style attribute. Curly brackets
are not required here, and each CSS specification should end with a
semicolon. In the fourth cell, there’s a bold area that is overlined
according to the external CSS.
8.1.2 Combinations
You can combine selectors and classes with each other, and you can
see some examples of this in this section. In addition to the overline,
you can use the CSS property font-weight with the value bold for bold
formatting in the document. You can also use the CSS property font-
style with the value italic for italics.
<table>
<tr>
<td>First cell</td>
<td>Second cell</td>
</tr>
</table>
<p class="over slanted">The last paragraph</p>
</body>
</html>
First, the external style sheets from the css_extern.css file are
integrated, as you already know from Section 8.1.1. This section
states that bolded areas are overlined.
8.2.1 Position
The CSS position, top, and left properties are used to define the
position of an element as follows:
The CSS position property stands for the type of positioning. The
absolute value is often used in this context, and it stands for an
absolute positioning that refers to the edge of the document.
The CSS top and left properties define the distance between the
top left corner of the positioned element and the top left corner of
the document. The value is usually given in the px unit, for pixels.
Let’s now take a look at the document in Figure 8.4.
Figure 8.4 Positioned Elements
Here’s the lower part of the program with the structure of the
document:
...
<body>
<p><input id="idChange" type="button" value="Change">
<input id="idAnimate" type="button" value="Animate"></p>
<p>Text outside of positioned elements</p>
<p>Another text outside of positioned elements</p>
<script>
const image = document.getElementById("idImage");
document.getElementById("idChange").addEventListener("click", change);
document.getElementById("idAnimate").addEventListener("click",
function() { reference = setInterval(animate, 20); } );
</script>
</body></html>
The image has the style attribute. Without the CSS position property,
the CSS top and left properties would have no effect. The div
container also has values for the CSS width and height properties for
the width and height, respectively, and background-color for the
background color.
Positioned elements are independent of nonpositioned elements in the
document. They can conceal other elements. The div container has
been defined in the code after the image, and for this reason and also
because of its position and size, the div container partially conceals
the image.
The image variable references the image. The first event handler
connects clicking on the Change button with the change() function,
and clicking on the Animate button starts a time-controlled process
that moves the image in an animated manner.
Now, here’s the upper part of the program with the JavaScript code:
... <head> ...
<script>
function change()
{
image.style.top = "130px";
image.style.left = "220px";
}
function animate()
{
if (imTop >= 130)
clearInterval(reference);
else
{
imTop++;
imLeft += 2;
image.style.top = imTop + "px";
image.style.left = imLeft + "px";
}
}
</script>
</head>
...
The change() function gives new values to the top and left
subproperties of the style property. These values are located within a
string and contain the px unit. In this way, the image gets moved from
the default position to another position.
The reference variable references the time-controlled process. The
imTop and imLeft variables contain the two numerical values for the
imTop and imLeft subproperties, and their initial values correspond to
the values agreed in the style attribute of the image.
After the position is changed, the image is at the bottom right, as you
can see in Figure 8.5.
Figure 8.5 After Changing Position
8.2.2 Size
The CSS width and height properties are used to define the size of an
element. There’s a positioned image in the document (see Figure 8.6).
After the size is changed, the image looks as shown in Figure 8.7.
Figure 8.6 Positioned Element
The image is displayed in the size of 160 × 120 pixels using the CSS
properties width and height. In this case, this also corresponds to the
original size. The reference to the image and the event handlers
corresponds to that in Section 8.2.1.
Now, here’s the upper part of the program with the JavaScript code:
... <head> ...
<script>
function change()
{
image.style.width = "240px";
image.style.height = "180px";
}
function animate()
{
if (imWidth >= 240)
clearInterval(reference);
else
{
imWidth++;
imHeight += 0.75;
image.style.width = imWidth + "px";
image.style.height = imHeight + "px";
}
}
</script>
</head>
...
The change() function gives new values to the imWidth and imHeight
subproperties of the style property.
For the animation, the imWidth and imHeight variables contain the
numerical values for the two subproperties. In the animate() function,
the imWidth variable is changed in increments of 1 up to the value 240.
To maintain the aspect ratio of the image, the imHeight variable is only
changed in steps of 0.75.
The order in which the div containers are defined is irrelevant, as they
all have a value for the z-index CSS property. The area variable
references the div container with the value 2 for the z-index CSS
property, and a click on the Change button calls the change() function.
Now, here’s the upper part of the program with the JavaScript code:
... <head> ...
<script>
function change()
{
area.style.zIndex = 4;
area.firstChild.nodeValue = "4 4 4 4 4 4 4 4 4 4";
}
</script>
</head>
...
The change() function gives the new value 4 to the zIndex subproperty
of the style property. This means that this div container is at the very
front and may conceal other div containers and nonpositioned
elements of the program (see Figure 8.9). The text content of the div
container is changed accordingly.
Figure 8.9 After Changing Position in Z Direction
8.2.4 Transparency
You can set the transparency of an element by using the opacity CSS
property. You can enter any value between 0.0 and 1.0. The default
value of 1.0 means that the element is opaque (i.e., not transparent),
which means that an element behind it in the z direction will not be
recognizable. The closer the value of opacity gets to 0.0, the more
transparent it becomes: at 0.0, it’s completely transparent and
therefore invisible.
In Figure 8.10, you can see writing in a div container for which no
transparency has been set. In addition, three other div containers
appear for which different values have been set for transparency.
Figure 8.10 Transparency with Opacity
The three square div containers are given the values 0.2, 0.5, and 1.0
for the opacity CSS property.
The area variable references the div container with the text No setting
for transparency , and clicking the Change and Animate buttons calls
the change() and animate() functions, respectively.
Now, here’s the upper part of the program with the JavaScript code:
... <head> ...
<style>
div {position:absolute; width:80px; height:80px;
background-color:#c0c0c0;}
<style>
<script>
function change()
{
area.style.opacity = 0.4;
}
function animate()
{
if (value <= 0.0)
clearInterval(reference);
else
{
value -= 0.01;
area.style.opacity = value;
area.firstChild.nodeValue = "Transparency " + value.toFixed(2);
}
}
</script>
</head>
...
First, some common CSS properties are set for the div containers.
The change() function assigns the value 0.4 to the opacity
subproperty of the style property (see Figure 8.11).
Figure 8.11 After Change in Transparency
For the animation, the value variable contains the numerical value for
this subproperty. The animate() function changes the value variable
from 1.0 to 0.0 in increments of 0.01. At the beginning of the
animation, the element is completely opaque, and at the end, it’s
entirely transparent. The current transparency is displayed as the text
content of the div container.
8.2.5 Visibility
A transparency of 0.0 makes an element invisible, and you can also
influence the visibility of the element by using the visibility CSS
property. The visible value makes an element visible, while the
hidden value makes it invisible. An invisible element occupies space in
the document but doesn’t respond to events such as mouseover or
mouseout.
The document in the following example (see Figure 8.12) contains two
images. The first image is invisible at the beginning. When the mouse
is positioned over a visible image, the other image is made invisible. If
the mouse leaves the area of a visible image, the other image
becomes visible (see Figure 8.13).
Figure 8.12 Visibility Using Visibility Property
Note
The value variable is used to set the opacity subproperty of the style
property of both images. The value of the variable starts at 1.0,
decreases in increments of 0.01, and ends at 0.0. The value is
assigned directly to the first image, and in the second image, the
transparency changes in the opposite direction. The difference 1 -
value is therefore calculated beforehand.
function subOff(x)
{
if(x==1)
sub1.style.visibility = "hidden";
else
sub2.style.visibility = "hidden";
}
</script>
<style>
div {position:absolute; width:200px; height:20px; padding:5px;
border-top:solid 1px #000000; border-bottom:solid 1px #000000;
background-color:#f0f0f0;}
.sub {visibility:hidden; height:60px; border-top-width:0px;}
a:link {color:#000000; text-decoration:none;}
a:hover {background-color:#d0d0d0;}
<style>
</head>
<body>
<div id="idMenu1" style="top:20px; left:20px;">Menu 1</div>
<div id="idMenu2" style="top:20px; left:220px;">Menu 2</div>
<div id="idSub1" class="sub" style="top:50px; left:20px;">
<a href="#">Submenu 1 A</a><br>
<a href="#">Submenu 1 B</a><br>
<a href="#">Submenu 1 C</a>
</div>
<div id="idSub2" class="sub" style="top:50px; left:220px;">
<a href="#">Submenu 2 A</a><br>
<a href="#">Submenu 2 B</a><br>
<a href="#">Submenu 2 C</a>
</div>
<script>
const menu1 = document.getElementById("idMenu1");
menu1.addEventListener("mouseover", function() { subOn(1); });
menu1.addEventListener("mouseout", function() { subOff(1); });
const menu2 = document.getElementById("idMenu2");
menu2.addEventListener("mouseover", function() { subOn(2); });
menu2.addEventListener("mouseout", function() { subOff(2); });
const sub1 = document.getElementById("idSub1");
sub1.addEventListener("mouseover", function() { subOn(1); });
sub1.addEventListener("mouseout", function() { subOff(1); });
const sub2 = document.getElementById("idSub2");
sub2.addEventListener("mouseover", function() { subOn(2); });
sub2.addEventListener("mouseout", function() { subOff(2); });
</script>
</body></html>
Both the main menu items and the submenus are in div containers
with the following properties:
A size of 200 × 20 pixels
One upper and one lower edge
A light-gray background color
An inner distance of five pixels to the edge
The properties of the submenus are added or overwritten using the
sub class: the height is changed to 60 pixels, the top border is
removed, and most importantly, they are invisible.
The menu items in the submenus are hyperlinks that can be used to
call other pages, for example. For these hyperlinks, the following
definitions are made: text color black, no underlining. If the mouse is
positioned over one of the hyperlinks, the background color changes.
Menus and submenus are positioned to match each other in the
document itself, and the sub class is assigned to the submenus.
Hovering over a main menu item with the mouse calls the subOn()
function, and the value 1 or the value 2 is transmitted as a parameter.
Within the subOn() function, the visibility subproperty of the style
property of the corresponding submenu is set to visible.
Conversely, leaving a main menu item with the mouse leads to the
function subOff() being called with the same parameters. Within the
subOff() function, the visibility subproperty of the style property of
the corresponding submenu is set to hidden.
Hovering or leaving one of the submenus leads to the same results.
Otherwise, the relevant submenu disappears as soon as the mouse is
moved from the associated main menu item to the submenu.
The positions are selected in such a way that the submenus slightly
overlap the main menu items. Otherwise, the submenus would
become invisible as soon as the mouse left the associated main menu
item.
For this purpose, you can adjust the angle of the throw to the ground
and the ball’s speed at the start of the throw. The program takes into
account the influence of gravity so that the ball’s trajectory resembles
a parabola. You can also place the target at a fixed or a random
location (see Figure 8.21).
The trajectory of the ball in the x and y directions after a time is
determined by two physical formulas (see also
https://en.wikipedia.org/wiki/Projectile_motion):
sx(t) = s0 + v0 * cos(w) * t
sy(t) = s0 + v0 * sin(w) * t + 0.5 * a0 * t * t
You can embed SVG code directly into your HTML code or save it in a
separate SVG file, which provides a (possibly animated) image that
you can insert as a multimedia object anywhere in different HTML
documents using the object HTML tag. This chapter shows only some
of the extensive possibilities of SVG.
The programs in this chapter are based on the SVG 1.1 standard,
which the World Wide Web Consortium (W3C) published in August
2011. A draft for the SVG 2 standard has been available since
September 2016, although it has only been partially implemented by
the individual browsers to date.
9.1 Creating an SVG File
In this section, we create a first SVG file and embed it in an HTML
document. The SVG has a frame for clarification and contains a black
rectangle at a specific position (see Figure 9.1).
The svg tag introduces the SVG code. According to the DOM from
Chapter 5, this tag is the root node in which all child nodes (the
graphic elements) are embedded. The child nodes can in turn have
child nodes and so on, and each node can also have attribute nodes.
The xmlns attribute uses the specified value to define the XML
namespace from which the SVG elements used originate, and the
width and height attributes determine the width and height of the
graphic in pixels. Comments are created as in HTML, using <!-- and -
->.
The rect tag creates a rectangle, which is positioned using the x and y
coordinates of its top left-hand corner. The values refer to the top left-
hand corner of the graphic, and the size of the rectangle is defined
using the width and height attributes. If no further information is
provided, an SVG element is filled and appears black.
Now, here’s the code of the HTML file:
...
<body>
<p>Above the SVG graphic</p>
<p><object data="svg_insert.svg" type="image/svg+xml"
width="400" height="150" style="border:1px solid black;">
Here is an SVG graphic
</object></p>
<p>Below the SVG graphic</p>
</body></html>
You can insert the SVG file into an HTML document as a multimedia
object by using the object HTML tag. You could also use the img
HTML tag, but then, existing animations would not be executed.
The data attribute references the SVG file, and the type attribute
refers to the type of file. The width and height attributes define the
size of the multimedia object within the HTML document, which here
has a thin black frame for clarity. The “Here is an SVG graphic” text is
only visible if the browser can’t display the SVG graphic.
9.2 Basic Shapes
The first basic shapes are displayed in the svg_basic_shapes.htm file:
rectangles (see Figure 9.2), circles, and ellipses (see Figure 9.3) as
well as lines, polylines, and polygons (see Figure 9.4).
The SVG code is directly embedded in the HTML code in this and the
other examples in this chapter. You can save the SVG code of all
graphics with the knowledge you gained in Section 9.1 but also in
separate SVG files.
9.2.1 Rectangles
Here’s the code for the rectangles from Figure 9.2:
...
<body>
<svg xmlns="https://www.w3.org/2000/svg" width="600"
height="300" style="border:1px solid black;">
<rect x="50" y="20" width="100" height="50" />
<rect x="200" y="20" width="100" height="50" rx="10" ry="10"
fill="#bbbbbb" stroke="#000000" stroke-width="2" />
<rect x="350" y="20" width="100" height="50" />
<rect x="355" y="25" width="50" height="25" fill="#ffffff" />
...
</svg>
</body></html>
You give the second rectangle rounded corners by using the rx and ry
attributes. You can make the rounding different in the x and y
directions, and you can use the fill attribute to define a fill color for
SVG elements. The color in our example is gray, but the default color
is black.
The stroke attribute defines the color of the frame line (which is black
here) for SVG elements and also ensures that it gets displayed. The
frame line of an SVG element has a default stroke width of one pixel;
you can select a value of two pixels by using the stroke-width
attribute.
The circle tag creates a circle, and you position it using the cx and cy
attributes for the x and y coordinates of its center. You define the size
of the circle by using the r attribute for the radius, and if you make no
further entries, this SVG element will also be filled and black, as you
can see in the first circle.
The fill color and the color and line width of the frame line are defined
for the second circle.
You create the “crescent moon” by covering the right part of a black
circle with a white circle.
The ellipse tag creates an ellipse. As with the circle, you carry out
positioning by using the cx and cy attributes for the coordinates of the
center of the ellipse. An ellipse has two radii, and you determine the
radius in the x direction by using the rx attribute and the radius in the y
direction by using the ry attribute. If both radii are the same, then the
ellipse is actually a circle.
The line tag creates a line that starts at x1 and y1 and ends at x2 and
y2. The stroke attribute defines the color of the line and ensures that it
gets displayed. By default, the line has a stroke width of one pixel, but
you can select a value of two pixels here by using the stroke-width
attribute.
The polyline tag creates a polyline, and the polygon tag creates a
polygon. You position the individual points by using the points
attribute, and you specify the pairs of x and y coordinates one after the
other. Spaces and commas are permitted as separators, and I
recommend separating the x and y coordinates of a pair with a comma
for clarity. You should separate the different pairs with a space.
If no further information is entered, this SVG element is also filled and
black, as you can see in the first polyline.
The third polyline has a gray filling and, independently of this, a black
line.
The polygon on the far right is constructed like the neighboring third
polyline, but the third point is automatically connected to the first point
so that the line is closed.
You can fill paths with a color, just like the basic shapes. The
svg_path.htm file contains some examples.
The path tag creates a path, and you position the individual points
using the d attribute. The following specifications can appear in the
value for the d attribute:
M,for move to: This specification moves the drawing pen to the next
pair of absolute coordinates without drawing a line. It can be used to
achieve an interruption in the line.
m:This specification is like M, but it moves the drawing pen to the
next pair of relative coordinates.
L,for line to: This specification draws a line to the next pair of
absolute coordinates.
l: This specification is like L, but it draws a line to the next pair of
relative coordinates.
V,for vertical: This specification draws a vertical line to the next
absolute y coordinate.
v: This specification is like V, but it draws a vertical line to the next
relative y coordinate. A positive value moves the drawing pen
downward, and a negative value moves it upward.
H,for horizontal: This specification draws a horizontal line to the
next absolute x coordinate.
h: This specification is like H, but it draws a horizontal line to the
next relative x coordinate. A positive value moves the drawing pen
to the right, and a negative value moves it to the left.
The four filled paths have the same appearance but are structured
differently:
The first path uses absolute coordinates using M or L.
The second path uses absolute coordinates using M, L, V, and H.
The third path uses M for the starting point and l for the subsequent
relative coordinates.
The fourth path uses M for the starting point and l, v, and h, for the
subsequent relative coordinates.
The fourth path is easier to read than the previous paths. If no further
information is entered, this SVG element is also filled and black.
You use the g tag to group elements to which you want to assign
common attributes and values. The none value for the fill attribute
indicates that the three unfilled paths and other elements in this group
are not filled. You use the stroke attribute to make the polyline visible.
The three paths have a similar structure, but there are small
differences:
The first path uses M for the starting point and l, v, and h for the
subsequent relative coordinates.
The second path ends with the z specification, and you use this to
close the polyline (i.e., to make a connection from the last element
to the starting point).
In the third path, you use the m specification once instead of the v
specification. This ensures that the pen moves to the next point
without drawing a line and that the path is therefore shown with an
interruption. However, it’s still a logically connected path.
The following is the code of the path from Figure 9.7, which contains a
total of three curves:
...
<g stroke="#000000" stroke-width="2" fill="none">
...
<path d="M 50,120
v 60
a 30,30 0 0 0 60,0
v -30
a 30,30 0 0 1 30,-30
h 60
a 30,30 0 1 1 -30,30" />
</g>
...
The individual entries for the d attribute are placed one below the other
for clarity. The A or a specification creates an elliptical arc. A total of
seven values are required:
The two radii of the ellipse in the x and y directions (see also
Section 9.2.2). To simplify matters, the values for the two radii of all
three ellipses are chosen to be the same, so that the curves are
circular.
The rotation of the arc around the x direction, given in degrees. This
specifies by how many degrees the arc is tilted around its x axis. To
simplify matters, the value 0 is selected for all three curves so that
none of the curves are tilted.
A value that determines whether the arc is drawn over a short path
(value = 0) or a long path (value = 1) from the starting point to the
end point. Here, the long path is only selected for the third arc so
that it’s drawn across three quarters of the arc.
A value that defines the direction of rotation (i.e., whether the arc
runs anticlockwise [value = 0] or clockwise [value = 1] from the
starting point to the end point. Here, the second and third curves
run clockwise.
The absolute (arc with A) or relative (arc with a) coordinates of the
end point of the arc.
9.4 Animations
You can change the properties of an SVG element using animation.
As in a movie, this involves a continuous change in position, size,
color, transparency, or other properties.
9.4.1 Procedure
The svg_animation.htm file contains some examples of animations.
First, you’ll see two rectangles with gray filling (see Figure 9.8). After a
short time, an animation for the first rectangle begins. It moves
downward, and then it moves back up to the starting position and
becomes higher at the same time. Finally, it becomes almost
transparent (see Figure 9.9).
The two rectangles are in a group in which the properties for the fill
color and the frame line are defined. Each of the four different
animations is subordinate to the rectangle as child nodes.
You use the animate tag to create an animation for the parent element,
and you use the attributeName attribute to define the property that you
want to animate. In this case, these are the attributes y for the vertical
position, height for the height, and opacity for the transparency.
You can also specify a time value can with an abbreviation for a
specific unit. For example, h is possible for the hours unit (e.g., 2h), min
for the minutes unit (e.g., 0.5min), s for the seconds unit (e.g., 3.5s), or
ms for the milliseconds unit (e.g., 500ms).
You use the from and to attributes to determine the absolute start and
end values for the animated property. To avoid a sudden change at
the start of the animation, you should take the value that the property
already has as the start value. In this example, the value for y is
changed continuously from 20 to 120 during the first animation, and
the value for opacity is changed continuously from 1.0 to 0.2.
You use the by attribute to determine the relative change in the
animated property. In this example, the end value for height is 100
greater than the start value, while the end value for y in the third
continuous animation is 100 less than the start value.
The fill attribute for the animation has nothing to do with a filling, and
the freeze value ensures that the value gets frozen at the end of the
animation. If you omit the attribute or set the default remove value, the
start value for the property will be set again.
The rectangle has the id attribute, here with the re2 value. A unique
ID is required to assign events to SVG elements. Each of the two
different animations is subordinate to the rectangle as child nodes.
The begin attribute has the re2.click value for both animations, so a
click on the rectangle with the corresponding ID starts both animations
simultaneously. In addition to click, there are mouseover, mouseout,
and other designations for events, as you have already seen in
Chapter 4, but without the on prefix.
Both animations last 2 seconds. The repeatCount attribute defines the
number of animation sequences, so a value of 2 doesn’t mean that the
animation will run two more times after it has finished but that it will run
twice in total.
Within the svg_rotation.htm file, you’ll see three rectangles with a gray
fill after loading the document (see Figure 9.11).
Figure 9.11 Fixed Rotation of Front Left Rectangle
The two rectangles on the left-hand side are on top of each other. The
upper rectangle is rotated by 30 degrees in relation to the lower
rectangle, and the pivot point (or more precisely, the point at which the
axis of rotation pierces the screen) is the center of the rectangle. You
can also define other pivot points (e.g., a corner of the rectangle or the
zero point of the SVG graphic at the top left).
After a short time, an animation starts for the rectangle on the right-
hand side. It rotates continuously from the start position at 0 degrees
to the end position of 30 degrees (see Figure 9.12).
The two rectangles are in a group in which the properties for the fill
and the frame line are defined.
The upper rectangle on the left-hand side has the transform attribute.
The type of transformation is specified as the value for this attribute,
such as translate, scale, skewX, skewY, or (as in this case) rotate,
followed by values in parentheses. In a rotation, this is the value for
the rotation angle in degrees, followed by the coordinates of the pivot
point.
All rectangles then undergo similar animations: they are moved to the
right, rotated by 90 degrees with a time delay (see Figure 9.14), and
then given a different random color.
In the create() function, some variables are set first. The svgCont
variable references the SVG container, while the ns variable
references the namespace required for the SVG elements. The
colorArray field contains the four possible colors for selection using
the random generator.
You assign the attributes and their values to the newly created SVG
element using the setAttribute() method. You determine the values
of the x and y coordinates depending on the current row and the
current column within the 6 × 6 grid, and you determine the value for
the color using the random generator and the previously created field.
Once you have assigned all attributes, the newly created rectangle
gets subordinated as a child node to the SVG container using the
appendChild() method.
animFill.setAttribute("fill", "freeze");
re.appendChild(animFill);
...
You’ll find the u_svg exercise in bonus chapter 1, section 1.19, in the
downloadable materials for this book at www.rheinwerk-
computing.com/5875.
10 Three-Dimensional Graphics
and Animations Using Three.js
We look at the cube with the camera from the top right. In this way,
three faces of the cube are visible in different colors.
10.1.1 3D Coordinate System
To clarify the structure of the 3D graphic, we must first deal with the
basics of a 3D coordinate system. Each point in 3D space can be
uniquely identified using its x coordinate, y coordinate, and z
coordinate.
The coordinates x=0, y=0, and z=0 denote the center of the system.
This item is located in the center of the screen by default.
The value of the x coordinate is positive to the right of the center
and negative to the left of the center. The x axis of our system runs
from left to right in the screen plane.
Above the center, the value of the y coordinate is positive, and
below the center, it’s negative. The y axis of our system runs from
bottom to top in the screen plane.
The z axis of our system runs vertically through the screen plane,
from a point behind the screen toward us as the viewer. The value
of the z coordinate is positive in front of the screen and negative
behind the screen.
In Figure 10.2, you can see the 3D coordinate system with the three
axes. To illustrate the 3D space in the two-dimensional (2D) plane of
the book, I choose a perspective representation, in which objects that
are farther away from the viewer appear smaller. For the same
reason, I use a perspective camera in my examples to display the 3D
graphics in the 2D plane of the screen.
Figure 10.2 3D Coordinate System with X, Y, and Z Axes
/* Camera */
const camera = new THREE.PerspectiveCamera(45,
innerWidth/innerHeight, 1, 100000);
camera.position.set(200, 250, 500);
camera.lookAt(object.position);
/* Drawing area */
const drawing = new THREE.WebGLRenderer();
drawing.setSize(innerWidth*0.8, innerHeight*0.8);
drawing.setClearColor(0xc0c0c0);
/* 3D scene */
const scene = new THREE.Scene();
scene.add(object);
drawing.render(scene, camera);
At the beginning of the program, the library in the three.min.js file gets
included.
You can use a Three.js object of the Mesh type to create the 3D object
in the desired geometry and with the desired surface material. No
position is specified for the object, so its position is at the origin of the
coordinate system.
10.1.4 Camera
There are different types of cameras. A Three.js object of the
PerspectiveCamera type provides a camera that gives us a perspective
view of the graphic scene. This camera stands on the top of a
pyramid, which has a rectangular base.
Here, we only show the part of the code that has been added
compared to the program from Section 10.1:
...
<body id="idBody">
<form>
<script>
...
let cx = 200, cy = 250, cz = 500;
camera.position.set(cx, cy, cz);
...
</script>
<p>
<input id="posPX" type="button" value="+X">
<input id="posMX" type="button" value="-X">
<input id="posPY" type="button" value="+Y">
<input id="posMY" type="button" value="-Y">
<input id="posPZ" type="button" value="+Z">
<input id="posMZ" type="button" value="-Z"></p>
</form>
<script>
document.getElementById("posPX").addEventListener
("click", function(){move(50,0,0)});
document.getElementById("posMX").addEventListener
("click", function(){move(-50,0,0)});
document.getElementById("posPY").addEventListener
("click", function(){move(0,50,0)});
document.getElementById("posMY").addEventListener
("click", function(){move(0,-50,0)});
document.getElementById("posPZ").addEventListener
("click", function(){move(0,0,50)});
document.getElementById("posMZ").addEventListener
("click", function(){move(0,0,-50)});
The middle section of the document introduces the cx, cy, and cz
variables, which specify the position of the camera.
In the move() function in the lower part of the document, the three
transferred move values are added to the current position values of
the camera. The camera receives the newly determined position, and
then the program makes sure that the camera looks at the cube again.
Finally, the drawing must be rerendered.
As the absolute value of the camera’s position values increases, the
distance of the camera from the object increases as well so that the
object becomes smaller and smaller from the viewer’s perspective.
Here, we only show the part of the code that has been added
compared to the program from Section 10.1:
...
<body id="idBody">
<script>
...
function rotate()
{
object.rotation.y += 0.2 * Math.PI / 180;
drawing.render(scene, camera);
if(object.rotation.y <= 90 * Math.PI / 180)
requestAnimationFrame(rotate);
}
rotate();
</script>
</body></html>
You can use a Three.js object of the rotation type to access the
rotation angle of a Three.js object. You can call or set this angle
individually around the relevant axes using the x, y, or z properties of
the rotation object, and you must specify the angle in radians. If you
specify it in degrees, you must first multiply it by the factor π / 180.
The value of the angle of rotation around the y axis is increased by 0.2
degrees, which seems very small at first. The graphic scene is then
redrawn using the render() method, and if the rotation angle around
the y axis is less than or equal to 90 degrees, then the
requestAnimationFrame() method of the window object gets called,
again with a reference to the rotate() method. This ensures that the
rotate() method will be called again. Then, the object is rotated again
by 0.2 degrees, the graphic scene is drawn again, and so on. This is
how you create the animation. By default, these actions take place
sixty times per second, which results in a rotation of 12 degrees per
second. The final value of 90 degrees is therefore reached after 7.5
seconds. If you remove the branch, the 3D object continues to rotate
endlessly.
10.4 Various Shapes
In the downloadable materials for this book, you can find the
th_multiple.htm program as a bonus. This program can be used to
display a total of eight different shapes in grid view: a sphere, a
cuboid, a cone, a tetrahedron, an octahedron, a capsule, a torus, and
a torus knot (see Figure 10.5). The camera looks at the origin, which
is in the center of the drawing. This is why the four shapes on the far
left and far right are distorted in perspective.
Note
jQuery is the most widely used JavaScript library, and it’s used as the
basis for many content management systems and web frameworks,
such as Joomla and WordPress. jQuery provides convenient,
browser-independent methods using CSS, Ajax, and animations,
among other things. I have used the current (as of August 2024)
version 3.7.1 for the examples in this chapter.
The jquery-3.7.1.min.js file with the entire library is only 86 KB in size.
You can find it together with the sample files in the downloadable
materials for this book.
If you want to download any more recent versions, you should visit
https://jquery.com. You can find the file used here on the Download
page via the compressed production term. In the following examples, I
assume that the file is located in the same directory as the sample
files.
11.1 Structure
Using an initial example, I will explain different ways of using jQuery.
Figure 11.1 shows a file that contains a paragraph with text and three
paragraphs with a hyperlink.
After clicking on the various hyperlinks, the content of the first
paragraph changes due to the use of jQuery, as you can see in
Figure 11.2, Figure 11.3, and Figure 11.4.
<script>
$("#idLink2").click(function(){
$("#idParagraph").html("After click 2"); });
jQuery("#idLink3").click(function(){
jQuery("#idParagraph").html("<b><i>After click 3</i></b>"); });
</script>
</body></html>
The document contains a paragraph with the sample text Hello. Below
this, there are three paragraphs, each of which contains a hyperlink.
The ready() method is called in the upper JavaScript area, and it has
a reference to a callback function as a parameter. Internally, the
ready() method ensures that the callback function is only called after
the file has been loaded with all elements in the browser. Otherwise,
an element that doesn’t yet exist could be accessed.
You can call a jQuery statement using both the $ function and the
jQuery function. Both calls lead to the same result.
Note
Let’s take a look at the code now, initially without the content of the
ready() method:
... <head> ...
<script src="jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() { ... });
</script>
<style>
div {width:250px; height:20px; background-color:#c0c0c0;}
#idBright {background-color:#e0e0e0;}
.dark {background-color:#a0a0a0;}
<style>
</head>
<body>
<div>div element, without id, without class</div>
<div id="idBright">Element with 'idBright' id</div>
<div class="dark">1. div element with 'dark' class</div>
<div class="dark">2. div element with 'dark' class</div>
I explain the content of the ready() method next. The following are
general settings for all div elements:
They have a size of 250 × 20 pixels and are medium gray.
The element with the idBright ID is light gray.
All elements of the dark CSS class are dark gray.
Here are the four div elements:
The first element has no ID and is not assigned to any CSS class.
The second element has the idBright ID.
The third and fourth elements have the CSS dark class assigned to
them.
This is followed by eight paragraphs, each of which contains a link.
The links have the IDs idLink1 through idLink8.
After you click the first hyperlink, the css() method gets executed for
all div elements and changes the CSS properties. Here, the width of
the elements is set to 350 pixels, and you specify a property-value pair
in JSON format. The second hyperlink changes the element with the
idBright ID, and the third hyperlink changes all elements with the dark
CSS class.
You can combine multiple selectors in one collection. The fourth
hyperlink changes the element with the idBright ID and all elements
with the dark CSS class.
In Figure 11.6, you can see a div element with a series of hyperlinks
below it. Triggering an event on a link leads to an animated expansion
of the element, and you can use the animate() method to create an
animation. You can find out more about animations in Section 11.4.
Now, let’s take a look at the code, initially without the content of the
ready() method:
... <head> ...
<script src="jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() { ... });
</script>
</head>
<body>
<div id="idRect" style="width:200px; height:100px;
background-color:#c0c0c0;">Rectangle</div>
<p><a id="idLink1" href="#"> 1: click</a></p>
<p><a id="idLink2" href="#"> 2: dblclick</a></p>
<p><a id="idLink3" href="#"> 3: mouseenter</a></p>
<p><a id="idLink4" href="#"> 4: mouseleave</a></p>
<p><a id="idLink5" href="#"> 5: mousemove</a></p>
<p><a id="idLink6" href="#"> 6: mousedown</a></p>
<p><a id="idLink7" href="#"> 7: mouseup</a></p>
<p><a id="idLink8" href="#"> 8: hover</a></p>
<p><a id="idLink9" href="#"> 9: mousedown and mouseup</a></p>
<p><a id="idLink10" href="#">10: click (with arguments)</a></p>
</body></html>
The div element has the idRect ID, a size of 200 × 100 pixels, and a
gray color.
Now, here’s the content of the ready() method:
$("#idLink1").click(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink2").dblclick(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink3").mouseenter(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink4").mouseleave(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink5").mousemove(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink6").mousedown(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink7").mouseup(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink8").hover(function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink9").bind("mousedown mouseup", function(){
$("#idRect").animate({"width":"+=20px"}); });
$("#idLink10").click(function(e){
$("#idRect").html("Event: " + e.type
+ "<br>Position X: " + e.pageX + " , Y: " + e.pageY
+ "<br>Time: " + Date(e.timeStamp)); });
You use the jQuery bind() method to bind events to methods. In fact,
the other methods in this section are actually specializations of the
bind() method in abbreviated form. You use the ninth hyperlink to bind
the mousedown and mouseup events, so you trigger the animation by
both pressing down and releasing a mouse button.
Information about each of the events is provided in an event object.
With jQuery, this event object is standardized for all browsers, and you
can access it via a reference that you pass to the method as a
parameter. When you click on the tenth hyperlink, some information
gets displayed: in this case, the type, location, and time of the event,
using the type, pageX, pageY, and timeStamp properties. The timeStamp
is specified in milliseconds, and you can convert it using the Date()
jQuery method.
11.4 Animations
This section describes various options for animating elements. For this
purpose, you use the animate() method, which (as in a movie) creates
the impression of an even sequence of individual images.
In Figure 11.7, you can see a positioned div element. You can use the
first eleven hyperlinks to start different animations for this element. For
clarification, you should restore the initial state after loading the page
after each animation. You can do this by using the twelfth hyperlink.
Now, let’s take a look at the code, initially without the content of the
ready() method:
... <head> ...
<script src="jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() { ... });
</script>
</head>
<body>
<div id="idRect" style="position:absolute; width:100px; height:100px;
left:300px; top:100px; background-color:#c0c0c0;">Rectangle</div>
<p><a id="idLink1" href="#"> 1: One property (width)</a></p>
<p><a id="idLink2" href="#"> 2: Two properties (width/height)</a></p>
<p><a id="idLink3" href="#"> 3: Position (left/top)</a></p>
<p><a id="idLink4" href="#"> 4: Transparency (opacity)</a></p>
<p><a id="idLink5" href="#"> 5: Time (duration)</a></p>
<p><a id="idLink6" href="#"> 6: Easing: linear instead of swing</a></p>
<p><a id="idLink7" href="#"> 7: Callback: next animation</a></p>
<p><a id="idLink8" href="#"> 8: Relative change (+=, -=)</a></p>
<p><a id="idLink9" href="#"> 9: Chain</a></p>
<p><a id="idLink10" href="#">10: Chain, with delay</a></p>
<p><a id="idLink11" href="#">11: Simultaneously (queue)</a></p>
<p><a id="idLink12" href="#">12: Initial state</a></p>
</body></html>
The div element has the idRect ID, an initial size of 100 × 100 pixels,
an initial position of 300 × 100 pixels, and a gray color. Positioning is
only necessary when the position gets animated.
Now, here’s the content of the ready() method:
$("#idLink1").click(function(){
$("#idRect").animate({"width":"200px"}); });
$("#idLink2").click(function(){
$("#idRect").animate({"width":"200px", "height":"50px"}); });
$("#idLink3").click(function(){
$("#idRect").animate({"left":"400px", "top":"200px"}); });
$("#idLink4").click(function(){
$("#idRect").animate({"opacity":"0.5"}); });
$("#idLink5").click(function(){
$("#idRect").animate({"width":"200px"}, {"duration":2000});});
$("#idLink6").click(function(){
$("#idRect").animate({"left":"400px"},
{"duration":2000, "easing":"linear"}); });
$("#idLink7").click(function(){
$("#idRect").animate({"left":"400px"},
function(){$("#idRect").animate({"left":"300px"}) }); });
$("#idLink8").click(function(){
$("#idRect").animate({"left":"+=100px", "opacity":"-=0.3"});});
$("#idLink9").click(function(){
$("#idRect").animate({"left":"+=100px"})
.animate({"left":"-=100px"}); });
$("#idLink10").click(function(){
$("#idRect").animate({"left":"+=100px"})
.delay(1000).animate({"left":"-=100px"}); });
$("#idLink11").click(function(){
$("#idRect").animate({"width":"200px"}, {"duration":1000})
.animate({"height":"50px"}, {"duration":2000, "queue":false}); });
$("#idLink12").click(function(){
$("#idRect").animate({"width":"100px", "height":"100px",
"left":"300px", "top":"100px", "opacity":1.0}); });
The first hyperlink animates the width up to the target value of 200
pixels. You can change multiple properties at the same time. The
second hyperlink animates the width up to the target value of 200
pixels and the height up to the target value of 50 pixels.
You can achieve animated movement by changing the property values
for left and top. The third hyperlink moves the rectangle to the target
point of 400 pixels/200 pixels. You can use the fourth hyperlink to
change the transparency to the value of 0.5 via the opacity property
(see also Chapter 8, Section 8.2.4).
If you enter the linear value for the easing property in the second
parameter of the animate() method, the animation runs at a constant
speed, which doesn’t look that natural. Easing plug-ins, which you can
find on the internet, provide further options for easing functions. The
sixth hyperlink is used to move the element linearly to the target value
within 2 seconds.
You can add a delay within an animation using the delay() method.
The tenth hyperlink leads to the same movement as the ninth
hyperlink, but there’s a one-second wait between the two partial
animations.
In a chain, the individual parts of an animation run one after the other
by default, but you can use the queue parameter to ensure that they
take place at the same time. The eleventh hyperlink animates the
width to the target value of 200 pixels within one second. The height is
animated to the target value of 50 pixels, but within 2 seconds and at
the same time. This is achieved with the Boolean value false (without
quotation marks) for the queue property. The default value is true.
The twelfth hyperlink is used to restore the initial state of a total of five
properties: width, height, left, top, and opacity.
The events are buffered. If you click on a hyperlink before a running
animation has finished, the corresponding action will be executed
afterward.
The following methods don’t provide any additional options, but they
can be used as a shortcut:
The slideDown(), slideUp(), and slideToggle() methods for
changing the height property
The fadeIn(), fadeOut(), fadeToggle(), and fadeTo() methods for
changing the opacity property
The show(), hide(), and toggle() methods for simultaneously
changing the width, height, and opacity properties
11.5 Example: Sinusoidal Movement
So far, only jQuery methods have been called in the individual
functions, but we shouldn’t forget that the rest of JavaScript is also
available to us. The following example shows a combination of jQuery
and the rest of JavaScript. After clicking on the small square on the far
left, it moves along a sine curve. In addition, there are some auxiliary
lines (see Figure 11.8).
The auxiliary lines are generated using a for loop, and the pTop
property assumes values of 10, 60, 110, 160, and 210 pixels.
Within the sine() function, the angle variable takes on the values from
10 to 360 in steps of 10. The image moves evenly to the right in the x
direction, and the sin() method of the Math object from JavaScript is
used for the movement in the y direction. It expects the angle in
radians, so this must be converted beforehand. The values for the
target point calculated in this way are saved in the pLeft and pTop
variables, and these variables can in turn be used as target values for
the left and top properties as parameters of the animate() method.
11.6 jQuery and Ajax
Ajax allows you to reload document parts, as we described in
Chapter 7. There are a number of methods in jQuery that use Ajax
technology internally, and you can work independently of browser and
version, as you are used to with jQuery.
In this example, you use the load() and post() methods to load the
content from text files, HTML files, PHP programs, and XML files into
the current document without having to rebuild the rest of the page.
You can see some examples in the following program. The start status
of the page is shown in Figure 11.9, and you’ll need to load the page
via a web server (see also Chapter 7).
function xmlOutput()
{
$.post("jq_ajax_test.xml",
function(result) { xmlData(result); });
}
$(document).ready(function()
{
$("#idLink1").click(function() {
$("#idOutput").load("jq_ajax_test.txt"); });
$("#idLink2").click(function() {
$("#idOutput").load("jq_ajax_test.htm"); });
$("#idLink3").click(function() {
$("#idOutput").load("jq_ajax_test.htm #t1"); });
$("#idLink4").click(function() {
$.post("jq_ajax_test.php", function(result) {
$("#idOutput").html(result); }); });
$("#idLink5").click(function() {
$.post("jq_ajax_test_data.php", {number1:12.2, number2:25.5},
function(result) {$("#idOutput").html(result);}); });
$("#idLink6").click(function() {
$.post("jq_ajax_test.xml", function(result) {
$("#idOutput").html(result.getElementsByTagName("nodeA")[0]
.firstChild.nodeValue + ", " + result.getElementsByTagName(
"nodeB")[0].getAttribute("attributeA")); }); });
$("#idLink7").click( xmlOutput );
});
The first hyperlink loads the entire text from the jq_ajax_test.txt text
file into the paragraph using the load() method.
This is the text from the text file
The second hyperlink loads the entire content of the HTML file
jq_ajax_test.htm with the tags into the paragraph.
The third hyperlink only loads the content of the element with the t1 ID
from the HTML file into the paragraph, also including the tags. Pay
attention to the separating space between the file name and the hash
character of the ID in the parameter of the load() method.
...
<body>
<p><b>Text in HTML file</b></p>
<p id="t1"><i>Part 1 in HTML file</i></p>
<p id="t2"><i>Part 2 in HTML file</i></p>
</body></html>
The sixth hyperlink calls the jq_ajax_test.xml file, as well as the post()
jQuery method. The value of the nodeA node and the value of the
attributeA attribute of the nodeB node are determined and added to
the content of the paragraph.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<nodeA>Node A value</nodeA>
<nodeB attributeA = "Attribute A from node B"
attributeB = "Attribute B from node B">
Node B value</nodeB>
</root>
<script>
document.getElementById("idHello").addEventListener("click",
function(){ons.notification.alert("Hello");});
</script>
</body></html>
The header of the file contains the links to the online versions of the
required CSS and JS files I mentioned at the beginning of this chapter.
This looks the same in the other files, so the header of the file is no
longer shown there.
Onsen UI components are integrated into the document like HTML
containers. A component of the ons-page type forms the basic element
of a page, which extends across the entire screen and serves as a
container for other elements.
In the left and right areas, the padding CSS property was used to
create small gaps before the left and right edges.
You can use components of the ons-list and ons-list-item types to
create a list or a list entry. More details on lists will follow in
Section 12.1.2.
You can display buttons in the content area using a component of the
ons-button type. You create the connection to JavaScript in the usual
way, via the id attribute and the getElementById() and
addEventListener() methods.
<ons-list-title>Title</ons-list-title>
<ons-list-header>Countries</ons-list-header>
<ons-list-item>
<div class="left">No. 1</div>
<div class="center">Rome</div>
<div class="right">R</div>
</ons-list-item>
<ons-list-item>
<div class="left">No. 2</div>
<div class="center">Naples</div>
<div class="right">N</div>
</ons-list-item>
</ons-list>
</ons-page>
<script>
document.getElementById("idSpain").addEventListener("click",
function(){ons.notification.alert("Madrid");});
document.getElementById("idItaly").addEventListener("click",
function(){ons.notification.alert("Rome");});
document.getElementById("idFrance").addEventListener("click",
function(){ons.notification.alert("Paris");});
</script>
</body></html>
A list consists of at least one component of the ons-list type for the
entire list and of entries that are created using components of the ons-
list-item type. A list can have an overall title within a component of
the ons-list-title type, and you use components of the ons-list-
header type to group elements and as group titles. You divide a single
element into three areas as in a navigation bar using the left, center,
and right classes.
Tapping a list item corresponds to an event that you can use to trigger
actions (see Figure 12.4). The tappable attribute ensures a visible
reaction of the list item itself.
Figure 12.4 After Tapping List Item
<ons-row>
<ons-col width="80px"
style="background-color:#c0c0c0; padding:10px;">1/1</ons-col>
<ons-col style="background-color:#e0e0e0; padding:10px;">1/2</ons-col>
<ons-col width="80px"
style="background-color:#c0c0c0; padding:10px;">1/3</ons-col>
</ons-row>
<ons-row>
<ons-col width="40%"
style="background-color:#b0b0b0; padding:10px;">2/1</ons-col>
<ons-col width="60%"
style="background-color:#f0f0f0; padding:10px;">2/2</ons-col>
</ons-row>
<ons-row>
<ons-col style="background-color:#c0c0c0; padding:10px;">3/1</ons-col>
<ons-col style="background-color:#e0e0e0; padding:10px;">3/2</ons-col>
</ons-row>
</ons-page>
</body></html>
The cells here have different background colors for a clearer display.
Within a cell, you can use the padding CSS property to create a space
between the content and the edge of the cell.
In the first row, there are two cells on the left and right, each with a
fixed width of 80 pixels. The third cell in the middle takes up the
remaining space in the row. The second row contains two cells that
take up 40% and 60% of the width of the row. There are also two cells
in the third row, and as their width is not specified, each of them takes
up half the width of the row.
12.2 Elements within a Page
In this section, you’ll get to know various elements that enable
interaction. These include icons, dialog boxes, input fields, and
selection fields.
<ons-list-item>
<ons-icon icon="md-face"></ons-icon>
<ons-icon icon="md-home"></ons-icon>
<ons-icon icon="md-zoom-in"></ons-icon>
</ons-list-item>
<ons-list-item>
<ons-icon icon="fa-file"></ons-icon>
<ons-icon icon="fa-cog"></ons-icon>
<ons-icon icon="fa-car"></ons-icon>
</ons-list-item>
<ons-list-item>
<ons-icon style="color:#ff0000;" icon="md-home"></ons-icon>
<ons-icon style="color:#00ff00;" icon="md-home"></ons-icon>
<ons-icon style="color:#0000ff;" icon="md-home"></ons-icon>
</ons-list-item>
<ons-list-item>
<ons-icon size="10px" icon="md-home"></ons-icon>
<ons-icon size="20px" icon="md-home"></ons-icon>
<ons-icon size="30px" icon="md-home"></ons-icon>
</ons-list-item>
<ons-list-item>
<ons-icon spin icon="md-spinner"></ons-icon>
<ons-icon rotate="90" icon="md-home"></ons-icon>
<ons-icon rotate="180" icon="md-home"></ons-icon>
<ons-icon rotate="270" icon="md-home"></ons-icon>
</ons-list-item>
<ons-list-item>
<ons-button id="idHome">
<ons-icon icon="md-home"></ons-icon> Home
</ons-button>
</ons-list-item>
</ons-list>
<script>
document.getElementById("idHome").addEventListener("click",
function(){ons.notification.alert("Home");});
document.getElementById("idAction").addEventListener("click",
function(){ons.notification.alert("Action");});
</script>
</body></html>
In Figure 12.9, you can see an application with four buttons that you
can use to call the four different standard dialogs.
Now, let’s take look at the program for the evaluation of the entries:
...
<body>
<ons-page>
<ons-list>
<ons-list-item>
<ons-button id="idInfo">Information</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idConfirmation">Confirmation</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idInput">Input</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idToast">Toast</ons-button>
</ons-list-item>
</ons-list>
</ons-page>
<script>
function info()
{
ons.notification.alert("Info");
}
function toast()
{
ons.notification.toast("The toast", {timeout: 2000});
}
document.getElementById("idInfo").addEventListener("click", info);
document.getElementById("idConfirmation")
.addEventListener("click", confirmation);
document.getElementById("idInput").addEventListener("click", input);
document.getElementById("idToast").addEventListener("click", toast);
</script>
</body></html>
Listing 12.5 onsen_dialog.htm File
The buttons for calling the dialogs are displayed here within list items.
The text of the input is then returned and can also be evaluated using
the then() method. Here, the text is also passed on as a parameter to
an anonymous function. If the text is an empty string, a corresponding
message gets displayed; otherwise, the string itself will be displayed.
You can pass the timeout parameter with a value in milliseconds to
the ons.notification.toast() method. In this case, the message will
disappear again after the specified time; otherwise, it will remain. In
Figure 12.12, you can see the representation of a toast.
You can also add designations and placeholders with help texts. Once
you select one of the input fields, a corresponding keyboard or
selection option will be displayed.
In Figure 12.13 and in Figure 12.14, you can see examples of the
types we’ve mentioned with placeholders.
There are two buttons at the end of the input form. You can use the
first button to display the entered values for checking, as in
Figure 12.15. The second button sends the data to a PHP program.
Figure 12.13 Input Fields, Upper Part
Figure 12.14 Input Fields, Lower Part
function submit()
{
document.getElementById("idForm").submit();
}
</script>
</head>
<body>
<form id="idForm" method="post" action="onsen_input.php">
<ons-page>
<ons-list>
<ons-list-item>
<label for="se" class="left">Search:</label>
<ons-search-input id="se" name="se" modifier="underbar"
placeholder="search_term"></ons-search-input>
</ons-list-item>
<ons-list-item>
<label for="no" class="left">Number:</label>
<ons-input id="no" modifier="underbar" type="number"
placeholder="123.456" name="no"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="dt" class="left">Date:</label>
<ons-input id="dt" modifier="underbar" type="date"
name="dt"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="ti" class="left">Time:</label>
<ons-input id="ti" modifier="underbar" type="time"
name="ti"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="ph" class="left">Phone:</label>
<ons-input id="ph" modifier="underbar" type="phone"
placeholder="#49-1234-567890" name="ph"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="ul" class="left">URL:</label>
<ons-input id="ul" modifier="underbar" type="url"
placeholder="www..." name="ul"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="em" class="left">Email:</label>
<ons-input id="em" modifier="underbar" type="email"
placeholder="...@..." name="em"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="co" class="left">Color:</label>
<ons-input id="co" name="co" modifier="underbar" type="color"
value="#c0c0c0" style="width:150px;"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="tx" class="left">Text:</label>
<ons-input id="tx" modifier="underbar" placeholder="Enter text"
name="tx"></ons-input>
</ons-list-item>
<ons-list-item>
<label for="pw" class="left">Password:</label>
<ons-input id="pw" modifier="underbar" type="password"
placeholder="(invisible)" name="pw"></ons-input>
</ons-list-item>
<ons-list-item>
<ons-button id="idValues">Values</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idSubmit">Submit</ons-button>
</ons-list-item>
</ons-list>
</ons-page>
<script>
document.getElementById("idValues").addEventListener("click", values);
document.getElementById("idSubmit").addEventListener("click", submit);
</script>
</form>
</body></html>
The underbar value for the modifier attribute displays a line below the
component for orientation purposes. You can use the value of the
placeholder attribute for a placeholder with an explanation of the input
field, and the name attribute is required to identify the input field in the
responding PHP program.
At the end of the input form, there are two buttons for displaying the
values (see Figure 12.18) and sending the form.
output += document.getElementById("ov").checked
? "With" : "Without";
output += " Ocean view<br>";
output += document.getElementById("gy").checked
? "With" : "Without";
output += " Gym access<br>";
output += document.getElementById("do").checked
? "One double bed<br>" : "Two single beds<br>";
output += document.getElementById("sm").checked
? "Smoker<br>" : "Non-smoker<br>";
ons.notification.alert(output);
}
function submit()
{
document.getElementById("idForm").submit();
}
</script>
</head>
<body>
<form id="idForm" method="post" action="onsen_selection.php">
<ons-page>
<ons-list>
<ons-list-header>Board</ons-list-header>
<ons-list-item>
<ons-select select-id="bo" name="bo">
<select>
<option value="Breakfast only"
selected="selected">Breakfast only</option>
<option value="Half board">Half board</option>
<option value="Full board">Full board</option>
</select>
</ons-select>
</ons-list-item>
<ons-list-header>Options</ons-list-header>
<ons-list-item>
<label for="mb">With ocean view</label>
<label class="left">
<ons-checkbox id="ov" name="ov"></ons-checkbox>
</label>
</ons-list-item>
<ons-list-item>
<label for="gy">Gym access</label>
<label class="left">
<ons-switch id="gy" name="gy" checked="checked"></ons-switch>
</label>
</ons-list-item>
<ons-list-header>Beds</ons-list-header>
<ons-list-item>
<label for="do">One double bed</label>
<ons-radio class="left" id="do" name="bed"
value="One double bed" checked="checked">
</ons-radio>
</ons-list-item>
<ons-list-item>
<label for="tw">Two single beds</label>
<ons-radio class="left" id="tw" name="bed" value="Two single beds">
</ons-radio>
</ons-list-item>
<ons-list-header>Smoker</ons-list-header>
<ons-list-item>
<label for="sm">Smoker</label>
<ons-radio class="left" id="sm" name="smoker" value="Smoker">
</ons-radio>
</ons-list-item>
<ons-list-item>
<label for="ns">Non-smoker</label>
<ons-radio class="left" id="ns" name="smoker"
value="Non-smoker" checked="checked">
</ons-radio>
</ons-list-item>
<ons-list-item>
<ons-button id="idValues">Values</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idSubmit">Submit</ons-button>
</ons-list-item>
</ons-list>
</ons-page>
<script>
document.getElementById("idValues").addEventListener("click", values);
document.getElementById("idSubmit").addEventListener("click", submit);
</script>
</form>
</body></html>
The checkboxes, switches, and radio buttons are identified using the
id attribute. You use the ?: ternary operator to evaluate the checked
property.
The name attribute is required for grouping the radio buttons in HTML,
while the name and value attributes are required for the evaluation in
PHP.
You can define the limits of the number range, the preset value, and
the increment for a change. You can operate the slider by dragging or
tapping, and in Figure 12.19, you can see an example with three
sliders.
Figure 12.19 Selection from Number Range
function submit()
{
document.getElementById("idForm").submit();
}
</script>
</head>
<body>
<form id="idForm" method="post" action="onsen_range.php">
<ons-page>
<ons-list>
<ons-list-item>
<label for="rg1" class="left">0</label>
<ons-range id="rg1" name="rg1" value="25" style="width:100%;">
</ons-range>
<label for="rg1" class="right">100</label>
</ons-list-item>
<ons-list-item>
<label for="rg2" class="left">10</label>
<ons-range id="rg2" name="rg2" min="10"
max="20" step="2" value="12" style="width:100%;">
</ons-range>
<label for="rg2" class="right">20</label>
</ons-list-item>
<ons-list-item>
<label for="rg3" class="left">1.5</label>
<ons-range id="rg3" name="rg3" min="1.5"
max="5.5" step="0.1" value="2.1" style="width:100%;">
</ons-range>
<label for="rg3" class="right">5.5</label>
</ons-list-item>
<ons-list-item>
<ons-button id="idValues">Values</ons-button>
</ons-list-item>
<ons-list-item>
<ons-button id="idSubmit">Submit</ons-button>
</ons-list-item>
</ons-list>
</ons-page>
<script>
document.getElementById("idValues").addEventListener("click", values);
document.getElementById("idSubmit").addEventListener("click", submit);
</script>
</form>
</body></html>
Note
You use the <msqrt> container to display a square root. You write the
so-called radicand under a root symbol; it is the value from which the
root is taken. In the case of the square root, this can be a single
identifier or a number, but it can also be a longer expression whose
elements are written one after the other.
You use the <mroot> container to display a general root. In contrast to
the <msqrt> container, we must write two elements within the <mroot>
container: first, the radicand (here, b), and then, the root exponent
(here, 3). If one of the two elements consists of a longer expression,
this expression must first be summarized. You can do that using the
<mrow> container (Section 13.3), and this rule for summarizing also
applies to many containers in the examples that follow.
<p>Logic:<br>
<math> <mo>∧</mo> </math> Logical And<br>
<math> <mo>∨</mo> </math> Logical Or<br>
<math> <mo>¬</mo> </math> Logical Not<br>
<math> <mo>→</mo> </math> If, then (conditional)<br>
<math> <mo>↔</mo> </math> Exactly when (biconditional)</p>
...
<p>Angles:<br>
<math> <mo>°</mo> </math> Degree<br>
<math> <mo>′</mo> </math> Degree minute<br>
<math> <mo>″</mo> </math> Degree second<br>
<math> <mo>α</mo> </math> Alpha<br>
<math> <mo>β</mo> </math> Beta<br>
<math> <mo>γ</mo> </math> Gamma</p>
<p>Other symbols:<br>
<math> <mo>Δ</mo> </math> Delta, difference<br>
<math> <mo>π</mo> </math> Pi<br>
<math> <mo>µ</mo> </math> Micro<br>
<math> <mo>±</mo> </math> Plus-minus<br>
<math> <mo>∞</mo> </math> Infinity</p>
</body></html>
Listing 13.6 mm_symbols.htm File, Part 2 of 2
Data is entered in the first document and is then sent to the second
document using JavaScript, where it will be fully available when the
document gets loaded. The data can then be inserted into the
mathematical expressions, again using JavaScript.
You can apply the technique shown here to more complex examples.
These could include a scalar product of two vectors with any number
of components or the multiplication of two matrixes.
location.href = "mm_sp_output.htm"
+ "?v10=" + v1[0] + "&v11=" + v1[1] + "&v12=" + v1[2]
+ "&v20=" + v2[0] + "&v21=" + v2[1] + "&v22=" + v2[2];
}
</script>
</head>
<body>
<p>Three components of the first vector, separated by
spaces:<br><input size="20" id="idVector1"></p>
<p>Three components of the second vector, separated by
spaces:<br><input size="20" id="idVector2"></p>
<p><input type="button" id="idCalculate"
value="Calculate and display scalar product"></p>
<script>
document.getElementById("idCalculate").addEventListener("click", calculate);
</script>
</body></html>
document.write("<mo>⋅</mo>");
let sp = 0;
for(let i=0; i<3; i++)
sp += v[i] * v[i+3];
document.write("<mo>=</mo> <mn>" + sp.toFixed(2) + "</mn>");
document.write("</math>");
</script>
</body></html>
When you click the Calculate button, the program checks the input
values and uses them to calculate the body mass index and the
training heart rates according to the Karvonen formula (see
Figure 14.2).
In a registration for a real fun run, the data is then stored in a MySQL
database using PHP, and you can view a list of registered participants
for checking purposes. Organizers have the option of exporting all
data from the database to a CSV file in a protected area (e.g., for
Microsoft Excel). This simplifies the evaluation of the runs and the
printing of the results lists and certificates.
In Figure 14.5, you can see the cards in the original order, which is
generated randomly at the beginning of the game. After clicking the
Rules button, you’ll see a description of the process (see Figure 14.6).
Player 2 has already found three pairs of cards, while player 1 has
only found one pair. It’s player 1’s turn now, and he has just flipped
the second card. Both players now have 2 to 3 seconds to memorize
the position of the two face-up cards, and you can see the backs of all
remaining cards. After you click the Rules button, a description of the
process gets displayed (see Figure 14.8).
Figure 14.8 Game Rules
As soon as the snake reaches its prey, one point gets added to the
score. Then the prey appears at a new, random position, and the
snake tries to reach it again. At the same time, the snake becomes
faster and therefore more difficult to control, so that the game runs
towards its natural end as the number of points increases.
15 Media, Drawings, and
Sensors
<script>
const audioHidden =
document.getElementById("idAudioHidden");
document.getElementById("idHidden").addEventListener
("click", function(){audioHidden.play();});
There are a total of five buttons, a checkbox, and a group of two radio
buttons that you use to operate the third player. A reference to this
player is also set in the JavaScript section. Clicking the Play and
Pause buttons calls the play() and pause() methods. You use the
currentTime property to determine or set the current position of the
playback process, and you use the checkbox to activate or deactivate
the Boolean property loop for endless repetition.
You can’t preset the volume value. Initially, you’ll hear the sound at
the volume that is currently set on the output device (PC, smartphone,
etc.), which corresponds to the value 1 for the volume property. You
call the volume() function by using the Volume up and Volume down
buttons. A value that changes the volume is passed as a parameter,
and the changed volume can only be between 0 and 1. You use the
two radio buttons to select the audio file you want to play.
function change(filename)
{
audioThree.src = filename + ".wav";
audioThree.load();
audioThree.play();
}
</script>
</head>
...
You can change the value of the volume property in the volume()
function, and that will change the percentage of the volume that you
could hear at the beginning from 100% to a new percentage.
In the change() function, you give the src property a new value by
using the function parameter. This property contains the name of the
audio file for the player, and the load() method loads the
corresponding audio file, which you can play via play().
An area with a size of 400 × 300 pixels is reserved and defined for the
video. There can be multiple source tags within a video container, and
in each case, you want to use the src attribute to specify the name of
the file being played.
Specifying the MIME type, including the codec, makes it easier for the
browser to assign the file. The codec identifies the process by which
the analog video recording is digitized.
15.2 Canvas
The canvas tag creates a blank canvas within a website. You can then
use JavaScript to create drawings, insert images, or output formatted
text, for example.
15.2.1 Drawings
In Figure 15.3, you can see some drawing elements that are
generated each time you click one of the buttons. You use the Clear
button to clear the canvas.
document.getElementById("idRect").addEventListener("click", rectangle);
document.getElementById("idArc").addEventListener("click", arc);
document.getElementById("idLine").addEventListener("click", line);
document.getElementById("idClear").addEventListener("click", clear);
document.getElementById("idGradientLin").addEventListener("click", gLinear);
document.getElementById("idGradientRad").addEventListener("click", gRadial);
</script>
</body></html>
Let’s now look at the upper part of the document with the functions for
creating a rectangle, an arc, and a line:
... <head> ...
<script>
function rectangle()
{
ct.fillRect(5, 25, 75, 100);
ct.strokeRect(5, 25, 75, 100);
}
function pathArc()
{
ct.beginPath();
ct.arc(150, 75, 50, 0, 1.5 * Math.PI);
ct.fill();
ct.stroke();
}
function pathLine()
{
ct.beginPath();
ct.moveTo(225, 25);
ct.lineTo(300, 50);
ct.lineTo(300, 100);
ct.lineTo(225, 125);
ct.fill();
ct.stroke();
}
...
The arc() method creates a circular arc. The first two parameters
represent the x and y coordinates of the center around which you
draw the arc, and the next parameter specifies the radius of the arc.
The last two parameters indicate the start angle and the end angle.
The angle is given in radians, and in the example, the start angle is 0
degrees. As usual, this is at 03:00 a.m., and it goes clockwise to an
angle of 270 degrees (i.e., 12:00 p.m.). A radian value of 2 π
corresponds to an angle of 360 degrees.
The fill() method creates the outline of a path and fills it, and you
draw the fill between the start and end points of the path. The
stroke() method only draws the outline of a path.
Now, here’s the middle part of the document with the functions for
creating gradients and deleting the canvas:
...
function gLinear()
{
const gr = ct.createLinearGradient(5, 150, 80, 250);
gr.addColorStop(0.0, "#000000");
gr.addColorStop(0.5, "#ffffff");
gr.addColorStop(1.0, "#000000");
ct.fillStyle = gr;
ct.fillRect(5, 150, 75, 100);
ct.fillStyle = "#f0f0f0";
}
function gRadial()
{
const gr = ct.createRadialGradient(150, 200, 0, 150, 200, 50);
gr.addColorStop(0.0, "#000000");
gr.addColorStop(0.5, "#ffffff");
gr.addColorStop(1.0, "#000000");
ct.fillStyle = gr;
ct.beginPath();
ct.arc(150, 200, 50, 0, 2 * Math.PI);
ct.fill();
ct.fillStyle = "#f0f0f0";
}
function clear()
{
ct.clearRect(0, 0, 400, 300);
}
</script>
</head>
...
The method returns an object that provides access to the line. The
addColorStop() method assigns specific colors to individual points on
the line, and the first parameter identifies the point on the line. The 0.0
point stands for the starting point of the line, the 1.0 point stands for
the end point of the line, and all values between 0 and 1 are in
between (for example, the 0.5 point is at the halfway point).
Once you create the gradient, it gets assigned to the fillStyle
property as the current fill type and color. To illustrate the color
gradient, you can create a filled rectangle that uses the gradient, and
you can select the coordinates of the gradient so that the top left
corner of the rectangle is at the starting point of the gradient and the
bottom right corner is at the end point. You can then set a different fill
type and color for graphic objects you subsequently create.
To illustrate the color gradient, you can create an arc that uses the
gradient. The arc is complete from 0 degrees to 360 degrees, and you
select the coordinates of the gradient so that the center of the arc lies
on the first circle and the edge of the arc lies on the second circle.
15.2.2 Images
You can use the drawImage() method to display an image in a canvas.
You can add the image to the drawing in its original size or scaled, or
you can add a section of the image (see the example in Figure 15.4).
Figure 15.4 Displaying Image Multiple Times
document.getElementById("idOriginal").addEventListener
("click", function() {ct.drawImage(image, 5, 25);} );
document.getElementById("idScale").addEventListener
("click", function()
{ct.drawImage(image, 205, 25, 120, 90);} );
document.getElementById("idSection").addEventListener
("click", function()
{ct.drawImage(image, 0, 60, 80, 60, 205, 150, 80, 60);} );
</script>
</body></html>
In the JavaScript section, you create a new HTML element of the img
type by using the createElement() method, and a reference to it is
assigned to the image variable. The name of the image file is assigned
to the property src of the HTML element, but the image is not yet
added to the document.
You can display the HTML element in the document by using the
drawImage() method. Three event handlers follow, and they lead to
different calls of the method. You can call the method with three, five,
or nine parameters that do the following:
The first parameter references an HTML element of the img type
that gets displayed.
Parameters 2 and 3 represent the x and y coordinates of the top
left-hand corner of the image.
Parameters 4 and 5 specify the visible width and height of the
image. The aspect ratio should be maintained.
If nine parameters are passed during the call, parameters 2 to 5 are
moved backwards. The new parameters 2 and 3 mark the top left-
hand corner of the image section within the image, and the size of
the image section follows in parameters 4 and 5. As with
parameters 2 to 5 of the method call before, parameters 6 to 9
represent the coordinates and the visible size of the image.
document.getElementById("idFilled").addEventListener("click", filled);
document.getElementById("idFramed").addEventListener("click", framed);
</script>
</body></html>
ct.textBaseline = "alphabetic";
ct.fillText("Alp", 10, 100);
ct.textBaseline = "top";
ct.fillText("Top", 110, 100);
ct.textBaseline = "middle";
ct.fillText("Mid", 210, 100);
ct.textBaseline = "bottom";
ct.fillText("Bot", 310, 100);
}
function framed()
{
ct.textBaseline = "alphabetic";
ct.strokeText("Framed", 10, 200);
}
</script>
</head>
...
In the filled() function, a ledger line is first drawn to clarify the text
baseline. Different values are then used for the different distances to
the text baseline, and an inscription is generated in each case. The
first parameter of the fillText() method contains the output text, and
the next two parameters contain the position of the bottom left corner
of the first character.
15.3.1 Geolocation
You can use the geolocation property of the navigator object to
access geolocation data, and you can use this data as figures or on a
map.
Note
You may have to call a sensor program several times before the
sensor reacts and supplies data for the first time.
You can enter the decimal values for latitude and longitude in the
Google Maps search field, for example, separating them with a space.
The geolocation will then be displayed on the map.
Let’s first look at the lower part of the program:
...
<body>
<div id="idInfo"> </div>
<script>
const info = document.getElementById("idInfo");
if(navigator.geolocation)
navigator.geolocation.getCurrentPosition(receive, error);
else
error();
</script>
</body></html>
You use the div area with the idInfo ID to output the geolocation data.
The info variable references this div area.
The next step is to check whether the browser recognizes the
geolocation property. In the standard case, you are also asked
whether the geolocation data may be retrieved during use. If the
browser recognizes the property and you give consent to retrieve the
geolocation data, the geolocation data is requested using the
getCurrentPosition() method. Two references to functions are
passed as parameters.
function error()
{
info.innerHTML = "No positioning";
}
</script>
</head>
...
The sample values in Figure 15.6 are positive for the latitude and
negative for the longitude. It’s a northern latitude (north of the equator)
and a western longitude (west of Greenwich, UK). The degree values
have decimal places, which are converted here for the usual
representation: in degrees, arc minutes, and arc seconds. The latitude
with the value 0 degrees lies on the equator, the North Pole lies at a
latitude of 90 degrees, and the South Pole lies at a latitude of –90
degrees. The longitude with the value 0 degrees is also called the
prime meridian, and it runs through Greenwich and, like all longitudes,
through the two poles. The longitude with the value of 180 degrees or
–180 degrees runs near New Zealand, among other places, and it
represents the international date line.
We explain the conversion to arc minutes and arc seconds below for
the sample value of 42.2757 degrees from Figure 15.6. The decimal
value (dv) results in the following: Auxiliary value x = dv * 60, bm =
integer part of x, bs = (decimal places of x) * 60. With dv = 0.2757
follows: x = dv * 60 = 16.542, bm = 16, bs = 0.542 * 60 = 32.52,
rounded to one decimal place = 32.5 (i.e., 42°16'32.5''). Conversely,
the decimal places for a decimal degree can be calculated as follows:
(bm * 60 + bs) / 3600. The result for this example is (16 * 60 + 32.52) /
3600 = 0.2757.
15.3.2 Waytracking
You can use the collection of geolocation data to determine the
temporal and geographical course of a route you take on foot, by bike,
or in a car. In the downloadable material for this book, you’ll find the
waytracking.htm program as a bonus with many explanatory
comments.
In addition to the data you already know from Section 15.3.1, you can
use the coords.altitude subproperty to record the altitude above sea
level. The program also takes into account the possibility that this
value may not be recorded in the mobile device.
After you click the Start button, the geolocation data is recorded every
five seconds and added as an additional row to the table. When you
click the Stop button, the recording and output will end. You can see
the header of the table in Figure 15.7.
Note
Make sure that the display of your mobile device doesn’t switch off
before the end of the automatic detection because you are no
longer operating the mobile device. That could cause no more data
to be recorded.
The position information comprises three values: the alpha, beta, and
gamma angles. The alpha angle indicates the angle between the
longitudinal axis of the mobile device and the north pole (see
Figure 15.9). Values between 0 and 360 degrees are possible here.
Figure 15.8 Position Data, Output, and Control
function position(e)
{
clearTimeout(moveReference);
document.getElementById("idAlpha").value = Math.round(e.alpha);
document.getElementById("idBeta").value = Math.round(e.beta);
document.getElementById("idGamma").value = Math.round(e.gamma);
move(e.beta, e.gamma);
}
function move(b,g)
{
if(b > 3 && pTop < 450) pTop += 5;
else if(b < -3 && pTop > 300) pTop -= 5;
rect.style.top = pTop + "px";
To change the position of the dark square, it’s first necessary to store
the initial values for the position in the pTop and pLeft variables.
If the checks are successful, the dark square gets moved by five
pixels. The function then calls itself again fifty milliseconds later with
the same position data so that the movement continues smoothly. If
the mobile device is given a different position in the meantime, the
time-controlled process in the position() function terminates and the
new position change gets initiated.
You can output this acceleration data and process it further (e.g., for a
game or a simulation). In the following motion.htm program, an object
is controlled on the screen (see Figure 15.10), but different
movements of the mobile device are required than for processing the
position data.
A Note on Physics
If an object moves in a certain direction, it has a speed that is
measured in meters per second. If this speed changes (for instance,
when a vehicle starts or brakes), this is called acceleration, which
corresponds to the change in speed per unit of time and is
measured in meters per second squared (which we write as m/s2).
In Figure 15.10, you can see values for x, y, z, alpha, beta, and gamma.
To clarify these values, it’s best to first place your mobile device flat in
front of you on a table with a smooth surface and then do the
following:
Move your device rapidly to the right on the table and slow it down
until it stops. A positive x value is measured briefly at the beginning
of the process, and a negative x value is measured briefly at the
end.
Move your device rapidly away from you on the table and slow it
down until it stops. A positive y value is measured briefly at the
beginning of the process, and a negative y value is measured briefly
at the end.
Pick up your device, lift it toward the ceiling rapidly, and slow it
down until it stops. At the start of the sequence, a z value greater
than 9.81 is measured for a short time, as the acceleration you have
applied is added to the gravitational acceleration. At the end, a z
value of less than 9.81 is measured for a short time.
These accelerations are used to change the position and size of the
dark square in the light rectangle.
All six of these accelerations only occur for a short time, and for this
reason, the maximum values are permanently displayed in the
program.
function acceleration(e)
{
const acc = e.accelerationIncludingGravity;
document.getElementById("idX").value = acc.x.toFixed(1);
document.getElementById("idY").value = acc.y.toFixed(1);
document.getElementById("idZ").value = acc.z.toFixed(1);
if(e.rotationRate)
{
const rot = e.rotationRate;
document.getElementById("idAlpha").value = Math.round(rot.alpha);
document.getElementById("idBeta").value = Math.round(rot.beta);
document.getElementById("idGamma").value = Math.round(rot.gamma);
The initial values for the position and size of the dark square are
recorded in the pTop, pLeft, and size variables. In addition, the six
maximum values are set to 0, with one exception: the maximum value
for z is given the starting value of 9.81.
If the checks have a positive result, the dark square gets moved by
fifty pixels or becomes twenty pixels larger or smaller.
For all six acceleration values, the system checks whether the current
value is greater than the value of the relevant maximum. If it is
greater, then the new maximum will be saved and output.
Operating the program correctly (i.e., moving the square in a specific
direction) is not easy, so you should initiate the movement slowly and
end it abruptly. An acceleration of more than 3 m/s2 only occurs during
deceleration, and that makes the effect easier to see.
One last tip: hold the mobile device firmly when testing the
accelerations.
A Installation and Keywords
You can reach the start page of the local web server in your browser
via the localhost address, and you can save your HTML files and PHP
programs in the C:\xampp\htdocs directory and in the directories
below it.
Stop the Apache web server in the XAMPP Control Panel application,
then close the application.
You can confirm the suggested installation options, and XAMPP will
be installed in the /opt/lampp directory.
At the end of the installation, you can leave the Launch Xampp
checkbox checked. This opens a dialog box for managing the various
servers. On the Manage Servers tab, you have the option of selecting
the Apache web server and starting and stopping it using the button
on the right. You can also call the dialog box for managing the servers
directly as follows:
sudo /opt/lampp/manager-linux-x64.run
After that, you can start the Apache web server of XAMPP as
described previously.
To avoid having to close the Apache web server of Ubuntu Linux after
every system start, you can deactivate the automatic start of the
associated service with the following command:
sudo update-rc.d apache2 disable
You can exit the nano editor as follows: press (Ctrl)+(X), confirm
with Yes at the bottom under Save, and confirm the suggested file
name. The PHP program looks as follows:
<?php
phpinfo();
?>
At the end of the installation, you can leave the Launch Xampp
checkbox checked. This opens a dialog box for managing the various
servers. On the Manage Servers tab, you have the option of selecting
the Apache web server and starting and stopping it using the button
on the right. You can also call the dialog box for managing the various
servers via Applications/XAMPP/manager-osx.
You can reach the start page of the local web server in your browser
via the localhost address, and you can save your HTML files and PHP
programs in the Applications/XAMPP/htdocs directory and in the
directories below it.
To test whether the installation was successful, you can use the
following PHP program. You can write and save it in the
Applications/XAMPP/htdocs/phpinfo.php file using the TextWrangler
editor from the App Store.
The PHP program looks like this:
<?php
phpinfo();
?>
await, abstract, boolean, break, byte, case, catch, char, class, const,
continue, debugger, default, delete, do, double, else, enum, export,
extends, false, final, finally, float, for, function, get, goto, if,
implements, import, in, instanceof, int, interface, let, long, native,
new, null, package, protected, private, prototype, public, return, set,
short, super, static, switch, synchronized, this, throw, throws, true,
transient, try, typeof, undefined, var, void, volatile, while, with, and
yield.
B The Author
↓A ↓B ↓C ↓D ↓E ↓F ↓G ↓H ↓I ↓J ↓K ↓L ↓M ↓N ↓O ↓P ↓Q ↓R
↓S ↓T ↓U ↓V ↓W ↓X ↓Z
A⇑
a [→ Section 1.6]
abs() [→ Section 2.3] [→ Section 6.3]
add()
Set [→ Section 6.6]
Three.js [→ Section 10.1]
alpha
acceleration [→ Section 15.3]
position [→ Section 15.3]
altitude [→ Section 15.3]
at()
array [→ Section 6.1] [→ Section 6.1]
string [→ Section 6.2]
Attribute node [→ Section 5.1] [→ Section 9.1]
B⇑
Background color [→ Section 1.6] [→ Section 1.6] [→ Section
8.2] [→ Section 8.2]
MathML [→ Section 13.1]
Bar chart [→ Section 4.9]
Block
of statements [→ Section 2.3]
static [→ Section 3.6]
validity [→ Section 2.6]
blur [→ Section 4.6]
br [→ Section 1.6]
break
loop [→ Section 2.4]
switch [→ Section 2.3]
C⇑
Calculation [→ Section 2.2]
click
event [→ Section 4.1] [→ Section 4.6]
mouse [→ Section 4.7]
click() [→ Section 11.1]
D⇑
Data structure [→ Section 6.6]
delete
array [→ Section 6.1]
property [→ Section 3.10]
delete()
Map [→ Section 6.6]
Set [→ Section 6.6]
Derived class [→ Section 3.9]
element
addEventListener() [→ Section 4.1]
innerHTML [→ Section 5.5]
External
CSS file [→ Section 1.6] [→ Section 8.1]
JavaScript [→ Section 1.9] [→ Section 2.6]
F⇑
fadeIn() [→ Section 11.4]
fadeOut() [→ Section 11.4]
fill()
array [→ Section 6.1]
path [→ Section 15.2]
Filling level [→ Section 4.9]
for
label [→ Section 12.2]
loop [→ Section 2.4]
forEach()
array [→ Section 6.1]
Map [→ Section 6.6]
Set [→ Section 6.6]
Form [→ Section 4.1] [→ Section 4.1]
dynamically created [→ Section 4.10]
mandatory field [→ Section 4.3]
operability [→ Section 4.9]
resetting [→ Section 4.2]
submitting [→ Section 4.2]
target [→ Section 4.2]
validation [→ Section 4.3] [→ Section 4.9] [→ Section 4.9]
G⇑
g (SVG) [→ Section 9.3]
gamma
acceleration [→ Section 15.3]
position [→ Section 15.3]
geolocation [→ Section 15.3]
Geolocation data [→ Section 15.3]
Graphic
2D [→ Section 9.1]
3D [→ Section 10.1]
Greenwich Mean Time (GMT) [→ Section 6.4]
H⇑
has()
Map [→ Section 6.6]
Set [→ Section 6.6]
href
a [→ Section 1.6]
link [→ Section 1.6] [→ Section 8.1]
location [→ Section 4.8]
HTML [→ Section 1.6] [→ Section 1.6]
changing using jQuery [→ Section 11.2]
document [→ Section 1.6]
element node [→ Section 5.5]
file [→ Section 1.6]
selector [→ Section 8.1]
I⇑
ID [→ Section 4.1]
in [→ Section 3.10]
includes()
array [→ Section 6.1]
string [→ Section 6.2]
J⇑
JavaScript [→ Section 1.1] [→ Section 1.8]
embedding [→ Section 1.8]
external [→ Section 1.9]
keywords [→ Section A.2]
statement [→ Section 1.8]
switching off [→ Section 1.11]
switching on [→ Section 1.2]
JavaScript Object Notation (JSON) [→ Section 6.7] [→ Section
7.4]
format [→ Section 6.7]
object [→ Section 6.7]
parse() [→ Section 6.7]
stringify() [→ Section 6.7]
K⇑
Key
pressing [→ Section 7.3]
releasing [→ Section 4.6]
L⇑
label [→ Section 12.2]
Landscape format [→ Section 15.3]
li [→ Section 1.6]
Line break
dialog box [→ Section 2.1]
document [→ Section 1.6]
lineTo() [→ Section 15.2]
load()
audio [→ Section 15.1]
jQuery [→ Section 11.6]
Local web server [→ Section 4.2]
M⇑
macOS [→ Section A.1]
Mandatory field [→ Section 4.3]
changing [→ Section 4.9]
mi [→ Section 13.1]
MIME type [→ Section 15.1]
mo [→ Section 13.1]
Mobile device
app [→ Section 12.1]
formatting [→ Section 1.6]
Mouse
coordinates [→ Section 4.7]
event [→ Section 4.7]
mousedown [→ Section 4.7]
mousemove [→ Section 4.7]
multiple
email input [→ Section 4.9]
selection menu [→ Section 4.5]
Multiplication [→ Section 2.2]
sign [→ Section 13.3]
N⇑
name [→ Section 4.4]
new
array [→ Section 6.1]
object [→ Section 3.1]
O⇑
Object [→ Section 3.1] [→ Section 6.1]
array from [→ Section 6.1]
checking [→ Section 6.1]
checking a class [→ Section 3.10]
converting (JSON) [→ Section 6.7]
copying [→ Section 3.11]
current [→ Section 3.1]
extending [→ Section 6.3] [→ Section 6.4] [→ Section 6.4]
in object [→ Section 3.8]
JSON format [→ Section 6.7]
member [→ Section 3.10]
outputting [→ Section 3.2]
parameters [→ Section 3.10]
reference [→ Section 3.1]
return value [→ Section 3.10]
SVG [→ Section 9.1]
Three.js [→ Section 10.1]
type [→ Section 3.10]
OR
logical [→ Section 2.3]
short circuit [→ Section 2.6]
Output
dialog box [→ Section 2.1]
object [→ Section 3.2]
Overlay [→ Section 8.1]
P⇑
p [→ Section 1.6]
padding [→ Section 1.6]
padEnd() [→ Section 6.2]
Position
CSS [→ Section 8.2]
jQuery [→ Section 11.4]
sensor [→ Section 15.3]
z-direction [→ Section 8.2]
post [→ Section 4.2]
Q⇑
queue [→ Section 11.4]
Quotation marks [→ Section 1.8]
R⇑
Radians [→ Section 6.3]
Radio button [→ Section 4.4] [→ Section 4.4]
Onsen UI [→ Section 12.2]
selected [→ Section 4.6]
S⇑
Sample projects [→ Section 14.1]
Search
input field [→ Section 4.9]
list [→ Section 4.9]
string [→ Section 13.6]
term [→ Section 4.9]
Semicolon
CSS [→ Section 8.1]
JavaScript [→ Section 1.8]
style
attribute [→ Section 8.1]
tag [→ Section 8.1]
T⇑
Table [→ Section 1.6]
DOM [→ Section 5.7]
HTML [→ Section 1.6]
JavaScript [→ Section 2.4]
MathML [→ Section 13.2]
Tablet [→ Section 12.1]
Text
canvas [→ Section 15.2]
changing using jQuery [→ Section 11.2]
Text editor [→ Section 1.1]
Text node [→ Section 5.1]
this
callback [→ Section 6.1]
object [→ Section 3.1]
Three.js [→ Section 10.1]
object [→ Section 10.1]
Three-dimensional graphic [→ Section 10.1]
toString()
array [→ Section 6.1]
converting numbers [→ Section 2.2]
object [→ Section 3.2]
toUpperCase() [→ Section 6.2]
Transparency
CSS [→ Section 8.2] [→ Section 8.3]
SVG [→ Section 9.4]
U⇑
Ubuntu Linux [→ Section A.1]
Uint32Array [→ Section 6.3]
ul [→ Section 1.6]
V⇑
valid [→ Section 4.9]
Validation form [→ Section 4.9] [→ Section 4.9]
W⇑
Watch [→ Section 2.5]
Waveform Audio File Format (WAV) [→ Section 15.1]
Web server
internet [→ Section 4.2]
local [→ Section 4.2]
WebGL [→ Section 10.1]
window
alert() [→ Section 2.1]
clearInterval() [→ Section 6.5]
clearTimeout() [→ Section 4.9]
confirm() [→ Section 2.3]
innerHeight [→ Section 10.1]
innerWidth [→ Section 10.1]
prompt() [→ Section 2.1]
requestAnimationFrame() [→ Section 10.3]
setInterval() [→ Section 6.5]
setTimeout() [→ Section 4.9] [→ Section 6.5]
with() [→ Section 6.1]
X⇑
XAMPP [→ Section 4.2] [→ Section A.1]
installation [→ Section A.1]
XML [→ Section 7.3]
namespace [→ Section 9.1]
Z⇑
z-index [→ Section 8.2]
The following sections contain notes on how you can contact us. In
addition, you are provided with further recommendations on the
customization of the screen layout for your e-book.
Supplements
If there are supplements available (sample code, exercise materials,
lists, and so on), they will be provided in your online library and on the
web catalog page for this book. You can directly navigate to this page
using the following link: https://www.rheinwerk-computing.com/5875.
Should we learn about typos that alter the meaning or content errors,
we will provide a list with corrections there, too.
Technical Issues
If you experience technical issues with your e-book or e-book account
at Rheinwerk Computing, please feel free to contact our reader
service: support@rheinwerk-publishing.com.
Please note, however, that issues regarding the screen presentation
of the book content are usually not caused by errors in the e-book
document. Because nearly every reading device (computer, tablet,
smartphone, e-book reader) interprets the EPUB or Mobi file format
differently, it is unfortunately impossible to set up the e-book
document in such a way that meets the requirements of all use cases.
In addition, not all reading devices provide the same text presentation
functions and not all functions work properly. Finally, you as the user
also define with your settings how the book content is displayed on the
screen.
The EPUB format, as currently provided and handled by the device
manufacturers, is actually primarily suitable for the display of mere text
documents, such as novels. Difficulties arise as soon as technical text
contains figures, tables, footnotes, marginal notes, or programming
code. For more information, please refer to the section Notes on the
Screen Presentation and the following section.
To perform searches in the e-book, the index of the book will reliably
guide you to the really relevant pages of the book. If the index doesn't
help, you can use the search function of your reading device.
This section contains the detailed and legally binding usage conditions
for this e-book.
Copyright Note
This publication is protected by copyright in its entirety. All usage and
exploitation rights are reserved by the author and Rheinwerk
Publishing; in particular the right of reproduction and the right of
distribution, be it in printed or electronic form.
© 2025 by Rheinwerk Publishing Inc., Boston (MA)
Digital Watermark
This e-book copy contains a digital watermark, a signature that
indicates which person may use this copy.
If you, dear reader, are not this person, you are violating the copyright.
So please refrain from using this e-book and inform us about this
violation. A brief email to info@rheinwerk-publishing.com is sufficient.
Thank you!
Trademarks
The common names, trade names, descriptions of goods, and so on
used in this publication may be trademarks without special
identification and subject to legal regulations as such.
All products mentioned in this book are registered or unregistered
trademarks of their respective companies.
Limitation of Liability
Regardless of the care that has been taken in creating texts, figures,
and programs, neither the publisher nor the author, editor, or
translator assume any legal responsibility or any liability for possible
errors and their consequences.
The Document Archive