JS Basics

Recently, I have some interests in the programming language JavaScript (JS).

I know the power of JS. In my web, I used the blueimp Gallery for my gallery. The responsive and customizable gallery was one of the favorite of siyuvision website.

I highly recommend to go through the javascript.info. It is straightforward and it has different language versions, for example, English Version and Chinese Version.

The JavaScript language

To learn it in a better understanding, code and practices will make it easier.

Three simple examples:
1
2
3
4
// A message box of "Hello World!
let message = 'Hello World!'; 

alert(message); 
1
2
3
4
// How old are you?
let age = prompt('How old are you?', 100);

alert(`You are ${age} years old!`); // You are 100 years old!
1
2
3
4
5
6
// Confirm with Visitor
let userName = prompt("Your name?", "Jack");
let isCoffeeWanted = confirm("Do you want a cup of coffee?");

alert( "Visitor: " + userName ); // Jack
alert( "Coffee wanted: " + isCoffeeWanted ); // true
A good comment should be like:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * Returns x raised to the n-th power.
 *
 * @param {number} x The number to raise.
 * @param {number} n The power, must be a natural number.
 * @return {number} x raised to the n-th power.
 */
function pow(x, n) {
  ...
}
Objects

Objects are used to store keyed collections of various data and more complex entities.

1
2
3
4
5
6
7
8
9
let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

alert( user.sizes.height ); // 182
Summary for Objects

Objects are associative arrays with several special features.

They store properties (key-value pairs), where:

  • Property keys must be strings or symbols (usually strings).
  • Values can be of any type.

To access a property, we can use:

  • The dot notation: obj.property.
  • Square brackets notation obj["property"]. Square brackets allow to take the key from a variable, like obj[varWithKey].

Additional operators:

  • To delete a property: delete obj.prop.
  • To check if a property with the given key exists: "key" in obj.
  • To iterate over an object: for (let key in obj) loop.
Object methods, "this"

To access the object, a method can use the this keyword.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let user = {
  name: "John",
  age: 30,

  sayHi() {
    // "this" is the "current object"
    alert(this.name);
  }

};

user.sayHi(); // John
Arrays
1
let fruits = ["Apple", "Orange", "Plum"];

One of the oldest ways to cycle array items is the for loop over indexes

1
2
3
4
5
let arr = ["Apple", "Orange", "Pear"];

for (let key in arr) {
  alert( arr[key] ); // Apple, Orange, Pear
}
Object.keys, values, entries

For plain objects, the following methods are available:

For instance:

1
2
3
4
let user = {
  name: "John",
  age: 30
};
  • Object.keys(user) = ["name", "age"]
  • Object.values(user) = ["John", 30]
  • Object.entries(user) = [ ["name","John"], ["age",30] ]
Date and time

Let’s meet a new built-in object: Date. It stores the date, time and provides methods for date/time management.

Access date components

There are methods to access the year, month and so on from the Date object:

Summary
  • Date and time in JavaScript are represented with the Date object. We can’t create “only date” or “only time”: Date objects always carry both.
  • Months are counted from zero (yes, January is a zero month).
  • Days of week in getDay() are also counted from zero (that’s Sunday).
  • Date auto-corrects itself when out-of-range components are set. Good for adding/subtracting days/months/hours.
  • Dates can be subtracted, giving their difference in milliseconds. That’s because a Date becomes the timestamp when converted to a number.
  • Use Date.now() to get the current timestamp fast.
JSON methods, toJSON

Let’s say we have a complex object, and we’d like to convert it into a string, to send it over a network, or just to output it for logging purposes.

JavaScript provides methods:

  • JSON.stringify to convert objects into JSON.
  • JSON.parse to convert JSON back into an object.

For instance, here we JSON.stringify a student:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  wife: null
};

let json = JSON.stringify(student);

alert(typeof json); // we've got a string!

alert(json);
/* JSON-encoded object:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "wife": null
}
*/

To decode a JSON-string, we need another method named JSON.parse.

The syntax:

1
let value = JSON.parse(str, [reviver]);
1
2
3
4
5
let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';

let user = JSON.parse(userData);

alert( user.friends[1] ); // 1
Class basic syntax

The basic class syntax looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class MyClass {
  prop = value; // property

  constructor(...) { // constructor
    // ...
  }

  method(...) {} // method

  get something(...) {} // getter method
  set something(...) {} // setter method

  [Symbol.iterator]() {} // method with computed name (symbol here)
  // ...
}

MyClass is technically a function (the one that we provide as constructor), while methods, getters and setters are written to MyClass.prototype.

Modules, introduction

As our application grows bigger, we want to split it into multiple files, so called “modules”. A module usually contains a class or a library of functions.

A module is just a file. One script is one module.

Modules can load each other and use special directives export and import to interchange functionality, call functions of one module from another one:

  • export keyword labels variables and functions that should be accessible from outside the current module.
  • import allows the import of functionality from other modules.

To summarize, the core concepts are:

  1. A module is a file. To make

    import/export
    

    work, browsers need

    <script type="module">
    

    . Modules have several differences:

    • Deferred by default.
    • Async works on inline scripts.
    • To load external scripts from another origin (domain/protocol/port), CORS headers are needed.
    • Duplicate external scripts are ignored.
  2. Modules have their own, local top-level scope and interchange functionality via import/export.

  3. Modules always use strict.

  4. Module code is executed only once. Exports are created once and shared between importers.

When we use modules, each module implements the functionality and exports it. Then we use import to directly import it where it’s needed. The browser loads and evaluates the scripts automatically.

Browser: Document, Events, Interfaces

Document

Here we’ll learn to manipulate a web-page using JavaScript.

To make the div show up, we need to insert it somewhere into document. For instance, in document.body.

There’s a special method append for that: document.body.append(div).

Here’s the full code:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";

  document.body.append(div);
</script>
Introduction to Events

An introduction to browser events, event properties and handling patterns.

An event is a signal that something has happened. All DOM nodes generate such signals (but events are not limited to DOM).

Here’s a list of the most useful DOM events, just to take a look at:

Mouse events:

  • click – when the mouse clicks on an element (touchscreen devices generate it on a tap).
  • contextmenu – when the mouse right-clicks on an element.
  • mouseover / mouseout – when the mouse cursor comes over / leaves an element.
  • mousedown / mouseup – when the mouse button is pressed / released over an element.
  • mousemove – when the mouse is moved.

Keyboard events:

  • keydown and keyup – when a keyboard key is pressed and released.

Form element events:

  • submit – when the visitor submits a <form>.
  • focus – when the visitor focuses on an element, e.g. on an <input>.

Document events:

  • DOMContentLoaded – when the HTML is loaded and processed, DOM is fully built.

CSS events:

  • transitionend – when a CSS-animation finishes.
HTML-attribute

A handler can be set in HTML with an attribute named on<event>.

For instance, to assign a click handler for an input, we can use onclick, like here:

1
2
3
4
<!doctype html>
<body>
<input value="Click me" onclick="alert('Click!')" type="button">
</body>
Form properties and methods

Forms and control elements, such as <input> have a lot of special properties and events.

Working with forms will be much more convenient when we learn them.

When we have a form, then any element is available in the named collection form.elements.

For instance:

<form name="my">
  <input name="one" value="1">
  <input name="two" value="2">
</form>

<script>
  // get the form
  let form = document.forms.my; // <form name="my"> element

  // get the element
  let elem = form.elements.one; // <input name="one"> element

  alert(elem.value); // 1
</script>
Event loop: microtasks and macrotasks

Browser JavaScript execution flow, as well as in Node.js, is based on an event loop.

Understanding how event loop works is important for optimizations, and sometimes for the right architecture.

The more detailed algorithm of the event loop (though still simplified compare to the specification):

  1. Dequeue and run the oldest task from the macrotask queue (e.g. “script”).

  2. Execute all

    microtasks

    :

    • While the microtask queue is not empty:
      • Dequeue and run the oldest microtask.
  3. Render changes if any.

  4. If the macrotask queue is empty, wait till a macrotask appears.

  5. Go to step 1.

To schedule a new macrotask:

  • Use zero delayed setTimeout(f).

That may be used to split a big calculation-heavy task into pieces, for the browser to be able to react on user events and show progress between them.

Also, used in event handlers to schedule an action after the event is fully handled (bubbling done).

To schedule a new microtask

  • Use queueMicrotask(f).
  • Also promise handlers go through the microtask queue.

Additional articles

Frames and windows

Basically, you just run:

1
window.open('https://javascript.info/')

Let’s add normal positioning options and reasonable width, height, left, top coordinates:

1
2
3
4
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;

open('/', 'test', params);
Network requests
Fetch

A typical fetch request consists of two await calls:

1
2
let response = await fetch(url, options); // resolves with response headers
let result = await response.json(); // read body as json

Or, without await:

1
2
3
fetch(url, options)
  .then(response => response.json())
  .then(result => /* process result */)
Sending a simple form

Let’s send a simple form first.

As you can see, that’s almost one-liner:

<form id="formElem">
  <input type="text" name="name" value="John">
  <input type="text" name="surname" value="Smith">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>

In this example, the server code is not presented, as it’s beyond our scope. The server accepts the POST request and replies “User saved”.

Fetch API

Here’s the full list of all possible fetch options with their default values (alternatives in comments):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
let promise = fetch(url, {
  method: "GET", // POST, PUT, DELETE, etc.
  headers: {
    // the content type header value is usually auto-set
    // depending on the request body
    "Content-Type": "text/plain;charset=UTF-8"
  },
  body: undefined // string, FormData, Blob, BufferSource, or URLSearchParams
  referrer: "about:client", // or "" to send no Referer header,
  // or an url from the current origin
  referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin...
  mode: "cors", // same-origin, no-cors
  credentials: "same-origin", // omit, include
  cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached
  redirect: "follow", // manual, error
  integrity: "", // a hash, like "sha256-abcdef1234567890"
  keepalive: false, // true
  signal: undefined, // AbortController to abort request
  window: window // null
});
Cookies, document.cookie

Cookies are small strings of data that are stored directly in the browser. They are a part of HTTP protocol, defined by RFC 6265 specification.

1
2
3
// At javascript.info, we use Google Analytics for statistics,
// so there should be some cookies
alert( document.cookie ); // cookie1=value1; cookie2=value2;...

The value of document.cookie consists of name=value pairs, delimited by ;. Each one is a separate cookie.

To find a particular cookie, we can split document.cookie by ;, and then find the right name. We can use either a regular expression or array functions to do that.

CSS-animations

CSS animations allow to do simple animations without JavaScript at all.

JavaScript can be used to control CSS animation and make it even better with a little of code.

CSS transitions

The idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation.

That is: all we need is to change the property. And the fluent transition is made by the browser.

For instance, the CSS below animates changes of background-color for 3 seconds:

1
2
3
4
.animated {
  transition-property: background-color;
  transition-duration: 3s;
}
Event transitionend

When the CSS animation finishes the transitionend event triggers.

It is widely used to do an action after the animation is done. Also we can join animations.

For instance, the ship in the example below starts to swim there and back on click, each time farther and farther to the right:

The animation is initiated by the function go that re-runs each time when the transition finishes and flips the direction:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
boat.onclick = function() {
  //...
  let times = 1;

  function go() {
    if (times % 2) {
      // swim to the right
      boat.classList.remove('back');
      boat.style.marginLeft = 100 * times + 200 + 'px';
    } else {
      // swim to the left
      boat.classList.add('back');
      boat.style.marginLeft = 100 * times - 200 + 'px';
    }

  }

  go();

  boat.addEventListener('transitionend', function() {
    times++;
    go();
  });
};
Keyframes

We can join multiple simple animations together using the @keyframes CSS rule.

It specifies the “name” of the animation and rules: what, when and where to animate. Then using the animation property we attach the animation to the element and specify additional parameters for it.

Here’s an example with explanations:

<div class="progress"></div>

<style>
  @keyframes go-left-right {        /* give it a name: "go-left-right" */
    from { left: 0px; }             /* animate from left: 0px */
    to { left: calc(100% - 50px); } /* animate to left: 100%-50px */
  }

  .progress {
    animation: go-left-right 3s infinite alternate;
    /* apply the animation "go-left-right" to the element
       duration 3 seconds
       number of times: infinite
       alternate direction every time
    */

    position: relative;
    border: 2px solid green;
    width: 50px;
    height: 20px;
    background: lime;
  }
</style>
JavaScript animations

JavaScript animations can handle things that CSS can’t.

For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas.

Using setInterval

An animation can be implemented as a sequence of frames – usually small changes to HTML/CSS properties.

For instance, changing style.left from 0px to 100px moves the element. And if we increase it in setInterval, changing by 2px with a tiny delay, like 50 times per second, then it looks smooth. That’s the same principle as in the cinema: 24 frames per second is enough to make it look smooth.

The pseudo-code can look like this:

1
2
3
4
let timer = setInterval(function() {
  if (animation complete) clearInterval(timer);
  else increase style.left by 2px
}, 20); // change by 2px every 20ms, about 50 frames per second

Here’s the helper animate function to setup most animations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function animate({timing, draw, duration}) {

  let start = performance.now();

  requestAnimationFrame(function animate(time) {
    // timeFraction goes from 0 to 1
    let timeFraction = (time - start) / duration;
    if (timeFraction > 1) timeFraction = 1;

    // calculate the current animation state
    let progress = timing(timeFraction);

    draw(progress); // draw it

    if (timeFraction < 1) {
      requestAnimationFrame(animate);
    }

  });
}

Options:

  • duration – the total animation time in ms.
  • timing – the function to calculate animation progress. Gets a time fraction from 0 to 1, returns the animation progress, usually from 0 to 1.
  • draw – the function to draw the animation.
Web components
Template element

A built-in <template> element serves as a storage for HTML markup templates. The browser ignores it contents, only checks for syntax validity, but we can access and use it in JavaScript, to create other elements.

We can put styles and scripts into <template> :

<template>
  <style>
    p { font-weight: bold; }
  </style>
  <script>
    alert("Hello");
  </script>
</template>

To summarize:

  • <template> content can be any syntactically correct HTML.
  • <template> content is considered “out of the document”, so it doesn’t affect anything.
  • We can access template.content from JavaScript, clone it to reuse in a new component.

The <template> tag is quite unique, because:

  • The browser checks HTML syntax inside it (as opposed to using a template string inside a script).
  • …But still allows use of any top-level HTML tags, even those that don’t make sense without proper wrappers (e.g. <tr>).
  • The content becomes interactive: scripts run, <video autoplay> plays etc, when inserted into the document.
Regular expressions

A regular expression (also “regexp”, or just “reg”) consists of a pattern and optional flags.

There are two syntaxes that can be used to create a regular expression object.

The “long” syntax:

1
regexp = new RegExp("pattern", "flags");

And the “short” one, using slashes "/":

1
2
regexp = /pattern/; // no flags
regexp = /pattern/gmi; // with flags g,m and i (to be covered soon)

Slashes /.../ tell JavaScript that we are creating a regular expression. They play the same role as quotes for strings.

In both cases regexp becomes an instance of the built-in RegExp class.

Escaping, special characters

As we’ve seen, a backslash \ is used to denote character classes, e.g. \d. So it’s a special character in regexps (just like in regular strings).

There are other special characters as well, that have special meaning in a regexp. They are used to do more powerful searches. Here’s a full list of them: [ \ ^ $ . | ? * + ( ).

Don’t try to remember the list – soon we’ll deal with each of them separately and you’ll know them by heart automatically.

Greedy and lazy quantifiers

Quantifiers have two modes of work:

  • Greedy

    By default the regular expression engine tries to repeat the quantifier as many times as possible. For instance, \d+ consumes all possible digits. When it becomes impossible to consume more (no more digits or string end), then it continues to match the rest of the pattern. If there’s no match then it decreases the number of repetitions (backtracks) and tries again.

  • Lazy

    Enabled by the question mark ? after the quantifier. The regexp engine tries to match the rest of the pattern before each repetition of the quantifier.

As we’ve seen, the lazy mode is not a “panacea” from the greedy search. An alternative is a “fine-tuned” greedy search, with exclusions, as in the pattern "[^"]+".

At last, a PDF version of this note can be found at: LINK

updatedupdated2020-06-152020-06-15