Lesson: Web Programming(5)ce.sharif.edu/courses/96-97/1/ce419-1/resources/root/ch05/wp-5.pdf · So...
Transcript of Lesson: Web Programming(5)ce.sharif.edu/courses/96-97/1/ce419-1/resources/root/ch05/wp-5.pdf · So...
Lesson: Web Programming(5)Omid Jafarinezhad
Sharif University of Technology
JavaScript
JavaScript was initially created to “make webpages alive”.
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Recursion and stackThe information about a function run is stored in its execution context
The execution context is an internal data structure that contains details about the execution of a function: where the control flow is now, the current variables, the value of this (we don’t use it here) and few other internal details
The maximal recursion depth is limited by JavaScript engine (about 10000, some engines allow more)
Rest parameters ...
A function can be called with any number of arguments, no matter how it is defined
The rest parameters must be at the end
The “arguments” variable
Arrow functions do not have "arguments"If we access the arguments object from an arrow function, it takes them from the outer “normal” function
As we remember, arrow functions don’t have their own this. Now we know they don’t have the special arguments object either
Spread operatorWhen ... occurs in a function call or alike, it’s called a “spread operator” and expands an array into the list
ClosureThe function sayHi uses an external variable name. When the function runs, which value of these two it’s going to use?
Such situations are common in both browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request
Closure (2)
Lexical EnvironmentIn JavaScript, every running function, code block and the script as a whole have an associated object named Lexical Environment
The Lexical Environment object consists of two parts:
● Environment Record – an object that has all local variables as its properties (and some other information like the value of this)
● A reference to the outer lexical environment, usually the one associated with the code lexically right outside of it (outside of the current figure brackets)
Lexical Environment (2)So, a “variable” is just a property of the special internal object, Environment Record. “To get or change a variable” means “to get or change the property of that object”
This is a so-called global Lexical Environment, associated with the whole script. For browsers, all <script> tags share the same global environment
Lexical Environment (3)
To summarize:
● A variable is a property of a special internal object, associated with the currently executing block/function/script
● Working with variables is actually working with the properties of that object
Lexical Environment (4)Function Declarations are special. Unlike let variables, they are processed not when the execution reaches them, but when a Lexical Environment is created. For the global Lexical Environment, it means the moment when the script is started
“Bye”
Lexical Environment (5)when a function runs, a new function Lexical Environment is created automatically. That’s a general rule for all functions. That Lexical Environment is used to store local variables and parameters of the call
Lexical Environment (6)During the function call we have two Lexical Environments: the inner one (for the function call) and the outer one:● The inner Lexical Environment corresponds to the current execution of
say. It has a single variable: name, the function argument. We called say("John"), so the value of name is "John".
● The outer Lexical Environment (in this example) is the global Lexical Environment
if a function is called multiple times, then each invocation will have its own Lexical Environment
Lexical Environment (7)
When a code wants to access a variable – it is first searched in the inner Lexical Environment, then in the outer one, then the more outer one and so on until the end of the chain
Lexical Environment (8)For every call to makeCounter() a new function Lexical Environment is created, with its own counter. So the resulting counter functions are independent.
Code blocks and loops
Lexical Environment and Garbage collection
Lexical Environment and Garbage collection (2)
Lexical Environment - Real-life optimizationsAs we’ve seen, in theory while a function is alive, all outer variables are also retained. But in practice, JavaScript engines try to optimize that. They analyze variable usage and if it’s easy to see that an outer variable is not used – it is removed
An important side effect in V8 (Chrome, Opera) is that such variable will become unavailable in debugging
As you could see (in Developer Tools in Chrome) – there is no such variable! In theory, it should be accessible, but the engine optimized it out.
Immediately-Invoked Function Expressions (IIFE)
Ways to create IIFE
The old "var"From the first sight, var behaves similar to let. That is, declares a variable, but:
1. “var” has no block scope○ If a code block in inside a
function, then var becomes a function-level Variable
2. “var” are processed at the function startalso called “hoisting” (raising)
The old "var" (2) - hoisting
So in the example above, if (false) branch never executes, but that doesn’t matter. The var inside it is processed in the beginning of the function, so at the moment of (*) the variable exists.
The old "var" (3)3. Declarations are hoisted,
but assignments are not
Global objectWhen JavaScript was created, there was an idea of a “global object” that provides all global variables and functions
Since then, JavaScript greatly evolved, and that idea of linking code through global variables became much less appealing. In modern JavaScript, the concept of modules took its place
But the global object still remains in the specification
In a browser it is named “window”, for Node.JS it is “global”, for other environments it may have another name
Global object (2)It does two things:
1. Provides access to built-in functions and values, defined by the specification and the environment
2. Provides access to global Function Declarations and var variables
But the global object does not have variables declared with let/const
Uses of “window”In server-side environments, the global object is used exceptionally rarely. Probably it would be fair to say “never”. In-browser window is sometimes used though1. To access exactly the global variable if the function has the local one with
the same name2. To check if a certain global variable or a builtin exists3. To take the variable from the right window. That’s probably the most valid
use casea. A browser may open multiple windows and tabs. A window may also embed another one
in <iframe>. Every browser window has its own window object and global variables. JavaScript allows windows that come from the same site (same protocol, host, port) to access variables from each other
“this” and global objectSometimes, the value of this is exactly the global object. That’s rarely used, but some scripts rely on that
Function objectAs we already know, functions in JavaScript are values
Every value in JavaScript has the type
In JavaScript, a function is an object
A good way to imagine functions is as callable “action objects”. We can not only call them, but also treat them as objects: add/remove properties, pass by reference etc
● The “name” property: returns function name● The “length” property: returns the number of function parameters
In the specification, this feature is called a “contextual name”. If the function does not provide one, then in an assignment it is figured out from the context
Function object - Custom propertiesA property assigned to a function like sayHi.counter = 0 does not define a local variable counter inside it. In other words, a property counter and a variable (let counter) are two unrelated things
Named Function Expression
Adding such a name (func) also did not break anything
There are two special things about the name func:
1. It allows to reference the function from inside itself2. It is not visible outside of the function
Sometimes, when we need a reliable internal name, it’s the reason to rewrite a Function Declaration to Named Function Expression form
The "new Function" syntax
But new Function allows to turn any string into a function, for example we can receive a new function from the server and then execute it
The "new Function" syntax and the closureBut when a function is created using new Function, its environment references not the current Lexical Environment, but instead the global one
SchedulingWe may decide to execute a function not right now, but at a certain time later. That’s called “scheduling a call”. here are two methods for it:
● setTimeout allows to run a function once after the interval of time● setInterval allows to run a function regularly with the interval between
the runs
These methods are not a part of JavaScript specification. But most environments have the internal scheduler and provide these methods. In particular, they are supported in all browsers and Node.JS
setTimeout
func|codeFunction or a string of code to execute. Usually, that’s a function. For historical reasons, a string of code can be passed, but that’s not recommended.
delayThe delay before run, in milliseconds (1000 ms = 1 second).
arg1, arg2…Arguments for the function (not supported in IE9-)
That doesn’t work, because setTimeout expects a reference to function. And here sayHi() runs the function, and the result of its execution is passed to setTimeout. In our case the result of sayHi() is undefined (the function returns nothing), so nothing is scheduled
Canceling with clearTimeout
In a browser the timer identifier is a number. In other environments, that can be something else. For instance, Node.JS returns a timer object with additional methods
setInterval
To stop further calls, we should call clearInterval(timerId)
Modal windows freeze time in Chrome/Opera/SafariIn browsers IE and Firefox the internal timer continues “ticking” while showing alert/confirm/prompt, but in Chrome, Opera and Safari the internal timer becomes “frozen”
● So if you run the code above and don’t dismiss the alert window for some time, then in Firefox/IE next alert will be shown immediately as you do it (2 seconds passed from the previous invocation), and in Chrome/Opera/Safari – after 2 more seconds (timer did not tick during the alert)
Recursive setTimeoutThere are two ways of running something regularly:
● setInterval● recursive setTimeout
Recursive setTimeout(2)The setTimeout above schedules next call right at the end of the current one (*).
Recursive setTimeout is more flexible method than setInterval. This way the next call may be scheduled differently, depending on the results of the current one.
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds…
Recursive setTimeout(3)Recursive setTimeout guarantees a delay between the executions, setInterval – does not
● The real delay between func calls for setInterval is less than in the code!● Recursive setTimeout guarantees the fixed delay (here 100ms)
setTimeout(…,0)This schedules the execution of func as soon as possible. But scheduler will invoke it only after the current code is completeSo the function is scheduled to run “right after” the current code. In other words, asynchronously
For instance, this outputs “Hello”, then immediately “World”:
The first line “puts the call into calendar after 0ms”. But the scheduler will only “check the calendar” after the current code is complete, so "Hello" is first, and "World" – after it.
Splitting CPU-hungry tasksThere’s a trick to split CPU-hungry task using setTimeout
We can split the long text to pieces. First 100 lines, then plan another 100 lines using setTimeout(...,0), and so on
If you run it, the CPU will hang
1. First run: i=1...1000000.2. Second run: i=1000001..2000000.3. …and so on, the while checks if i is evenly
divided by 100000.
Pauses between count executions provide just enough “breath” for the JavaScript engine to do something else, to react on other user actions
Allowing the browser to render
If you run it, the changes to i will show up after the whole count finishes
Minimal delay of nested timers in-browserIn the browser, there’s a limitation of how often nested timers can run. The HTML5 standard says: “after five nested timers…, the interval is forced to be at least four milliseconds.”
...
First timers run immediately, and then the delay comes into play
DecoratorsLet’s say we have a function slow(x) which is CPU-heavy, but its results are stable. In other words, for the same x it always returns the same result.
If the function is called often, we may want to cache (remember) the results for different x to avoid spending extra-time on recalculations
Using “func.call” for the contextThe caching decorator mentioned above is not suited to work with object methods
The reason is that the wrapper calls the original function as func(x) in the line (**). And, when called like that, the function gets this = undefined. We would observe a similar symptom if we tried to run
There’s a special built-in function method func.call(context, …args) that allows to call a function explicitly setting this
Going multi-argument with “func.apply”Now let’s make cachingDecorator even more universal. Till now it was working only with single-argument functions. Here we can use another built-in method func.apply: It runs the func setting this=context and using an array-like object args as the list of arguments
In the line (*) it calls hash to create a single key from arguments. Here we use a simple “joining” function that turns arguments (3, 5) into the key "3,5". More complex cases may require other hashing functions
Borrowing a method
…Unfortunately, that won’t work. Because we are calling hash(arguments) and arguments object is both iterable and array-like, but not a real array
We take (borrow) a join method from a regular array [].join. And use [].join.call to run it in the context of arguments
Losing “this”We already know that in JavaScript it’s easy to lose this. Once a method is passed somewhere separately from the object – this is lost
Solution 1: a wrapper
Now it works, because it receives user from the outer lexical environment, and then calls the method normally
Solution 1: vulnerabilityWhat if before setTimeout triggers (there’s one second delay!) user changes value? Then, suddenly, it will call the wrong object!
The next solutionguarantees thatsuch thing won’thappen
Solution 2: bindFunctions provide a built-in method bind that allows to fix this
calling boundFunc is like func with fixed this
All arguments are passed to the original func “as is”
Partial function application We create a new function by fixing some parameters of the existing one
We can bind not only this, but also arguments. That’s rarely done, but sometimes can be handy
Please note that here we actually don’t use this here. But bind requires it, so we must put in something like null
CurryingCurrying is translating a function from callable as f(a, b, c) into callable as f(a)(b)(c)
More advanced implementations of currying like _.curry from lodash library do somethingmore sophisticated. They return a wrapper that allows a function to be called normally when all arguments are supplied or returns apartial otherwise
Arrow functions revisitedArrow functions are not just a “shorthand” for writing small stuff
JavaScript is full of situations where we need to write a small function, that’s executed somewhere else. For instance:
● arr.forEach(func) – func is executed by forEach for every array item.● setTimeout(func) – func is executed by the built-in scheduler.● …there are more
Arrow functions have no “this”Arrow functions do not have this. If this is accessed, it is taken from the outside
If we used a “regular” function, there would be an error.The error occurs because forEach runs functions with this=undefined by default, so the attempt to access undefined.title is made.
Arrows have no “arguments”Arrow functions also have no arguments variable. That’s great for decorators, when we need to forward a call with the current this and arguments
The same without an arrow function would look like
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Property flags and descriptorsObject properties, besides a value, have three special attributes (so-called “flags”):
● writable – if true, can be changed, otherwise it’s read-only● enumerable – if true, then listed in loops, otherwise not listed● configurable – if true, the property can be deleted and these attributes
can be modified, otherwise not
The method Object.getOwnPropertyDescriptor allows to query the full information about a property
To change the flags, we can use Object.defineProperty
Making a property non-configurable is a one-way road. We cannot change it back, because defineProperty doesn’t work on non-configurable properties
In the non-strict mode, no errors occur when writing to read-only properties and such. But the operation still won’t succeed
“flags-aware” way of cloning an objectTo get all property descriptors at once, we can use the method Object.getOwnPropertyDescriptors(obj)
Normally when we clone an object, we use an assignment to copy properties.…But that does not copy flags. So if we want a “better” clone then Object.defineProperties is preferred
Normally, a built-in toString for objects is non-enumerable, it does not show up in for..in. But if we add toString of our own, then by default it shows up in for..in
Sealing an object globally● Object.preventExtensions(obj): Forbids to add properties to the object● Object.seal(obj):Forbids to add/remove properties, sets for all existing
properties configurable: false● Object.freeze(obj):Forbids to add/remove/change properties, sets for all
existing properties configurable: false, writable: false● Object.isExtensible(obj):Returns false if adding properties is forbidden,
otherwise true● Object.isSealed(obj):Returns true if adding/removing properties is
forbidden, and all existing properties have configurable: false.● Object.isFrozen(obj): Returns true if adding/removing/changing properties
is forbidden, and all current properties are configurable: false, writable: false
Getters and settersAccessor properties are represented by “getter” and “setter” methods
we have a “virtual” property. It is readable and writable, but in fact does not exist
Accessor descriptor may have:
● get – a function without arguments, that works when a property is read,
● set – a function with one argument, that is called when the property is set,
● enumerable – same as for data properties,
● configurable – same as for data properties.
Smarter getters/settersTechnically, the external code may still access the name directly by using user._name. But there is a widely known agreement that properties starting with an underscore "_" are internal and should not be touched from outside the object
Prototypal inheritanceIn JavaScript, objects have a special hidden property [[Prototype]] (as named in the specification), that is either null or references another object. That object is called “a prototype”
That [[Prototype]] has a “magical” meaning. When we want toread a property from object, and it’s missing, JavaScript automatically takes it from the prototype. In programming, such thing is called “prototypal inheritance”
The property [[Prototype]] is internal and hidden, but there are many ways to set it. One of them is to use __proto__
There are actually only two limitations for __proto__:
● The references can’t go in circles. JavaScript will throw an error if we try to assign __proto__ in a circle
● The value of __proto__ can be either an object or null. All other values (like primitives) are ignored
The value of “this”The answer is simple: this is not affected by prototypes at all
No matter where the method is found: in an object or its prototype. In a method call, this is always the object before the dot
The “prototype” propertyIn modern JavaScript we can set a prototype using __proto__. But it wasn’t like that all the time
When a new object is created with new F(), the object’s [[Prototype]] is set to F.prototype
Default F.prototype, constructor propertyEvery function has the "prototype" property even if we don’t supply it
The default "prototype" is an object with the only property constructor that points back to the function itself
Native prototypes
In-browser tools like Chrome developer console also show inheritance (may need to use console.dir for built-in objects)
Changing native prototypesNative prototypes can be modified. For instance, if we add a method to String.prototype, it becomes available to all strings:
Borrowing from prototypes
That’s more efficient, because it avoids the creation of an extra array object []
Methods for prototypes● Object.create(proto[, descriptors]) – creates an empty object with given
proto as [[Prototype]] (can be null) and optional property descriptors
● Object.getPrototypeOf(obj) – returns the [[Prototype]] of obj (same as __proto__ getter).
● Object.setPrototypeOf(obj, proto) – sets the [[Prototype]] of obj to proto (same as __proto__ setter).
● Object.keys(obj) / Object.values(obj) / Object.entries(obj) – returns an array of enumerable own string property names/values/key-value pairs.
● Object.getOwnPropertySymbols(obj) – returns an array of all own symbolic property names.
Methods for prototypes (2)● Object.getOwnPropertyNames(obj) – returns an array of all own string
property names.
● Reflect.ownKeys(obj) – returns an array of all own property names.
● obj.hasOwnProperty(key): it returns true if obj has its own (not inherited) property named key.
// fully identical shallow clone of objlet clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Functional class patternThe constructor function below can be considered a “class” according to the definition
Factory class pattern
Prototype-based classesPrototype-based classes is the most important and generally the best. Functional and factory class patterns are rarely used in practice
Prototype-based classes (2)There is a widely known agreement that internal properties and methods are prepended with an underscore "_". Like _name or _calcAge(). Technically, that’s just an agreement, the outer code still can access them. But most developers recognize the meaning of "_" and try not to touch prefixed properties and methods in the external code
Prototype-based classes (3)Here are the advantages over the functional pattern:● In the functional pattern, each object has its own copy of every method.
We assign a separate copy of this.sayHi = function() {...} and other methods in the constructor.
● In the prototypal pattern, all methods are in User.prototype that is shared between all user objects. An object itself only stores the data.
So the prototypal pattern is more memory-efficient
Prototype-based inheritance for classes
The “class” syntax
The “class” syntax (2)The class User {...} here actually does two things:● Declares a variable User that references the function named "constructor"● Puts into User.prototype methods listed in the definition. Here it includes
sayHi and the constructor
The “class” syntax (3)Constructors require new: Unlike a regular function, a class constructor can’t be called without new
Different string output: If we output it like alert(User), some engines show "class User...", while others show "function User..."
Class methods are non-enumerable: That’s good, because if we for..in over an object, we usually don’t want its class methods
The “class” syntax (4)Classes have a default constructor() {} : If there’s no constructor in the class construct, then an empty function is generated, same as if we had written constructor() {}
Classes always use strict: All code inside the class construct is automatically in strict mode
Getters/setters
Only methodsUnlike object literals, no property:value assignments are allowed inside class. There may be only methods and getters/setters. There is some work going on in the specification to lift that limitation, but it’s not yet there
If we really need to put a non-function value into the prototype, then we can alter prototype manually, but such properties will be shared among all objects of the class
Class Expression and Named Class Expression
Static methodsThe value of this inside User.staticMethod() is the class constructor User itself (the “object before dot” rule)
That actually does the same as assigning it as a function property
Static methods
Class inheritance
Any expression is allowed after extendsThat may be useful for advanced programming patterns when we use functions to generate classes depending on many conditions and can inherit from them
Here class User inherits from the result of f("Hello")
Overriding a method
Arrow functions have no super
If we specified a “regular” function here, there would be an error
Overriding constructorWith constructors, things are is a little bit tricky
According to the specification, if a class extends another class and has no constructor, then the following constructor is generated
● When a normal constructor runs, it creates an empty object as this and continues with it.● But when a derived constructor runs, it doesn’t do it. It expects the parent constructor to do this
job
Super: internals
in both lines (*) and (**) the value of this.__proto__ is exactly the same: rabbit. They both call rabbit.eat without going up the chain in the endless loop
[[HomeObject]]When a function is specified as a class or object method, its [[HomeObject]] property becomes that object
It is used only for calling parent methods in super, to resolve the prototype
[[HomeObject]][[HomeObject]] is defined for methods defined both in classes and in plain objects. But for objects, methods must be specified exactly the given way: as method(), not as "method: function()".
Static methods and inheritance
No static inheritance in built-insPlease note that built-in classes don’t have such static [[Prototype]] reference. For instance, Object has Object.defineProperty, Object.keys and so on, but Array, Date etc do not inherit them
Natives are extendablePlease note one very interesting thing. Built-in methods like filter, map and others – return new objects of exactly the inherited type. They rely on the constructor property to do so
The static getter Symbol.species, if exists, returns the constructor to use in such cases
Natives are extendable (2)
The instanceof operator
The algorithm of obj instanceof Class works roughly as follows
1. If there’s a static method Symbol.hasInstance, then use it
2. Most classes do not have Symbol.hasInstance. In that case, check if Class.prototype equals to one of prototypes in the obj prototype chain
MixinsIn JavaScript we can only inherit from a single object
As defined in Wikipedia, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. In other words, a mixin provides methods that implement a certain behavior, but we do not use it alone, we use it to add the behavior to other classes
Mixin – is a generic object-oriented programming term: a class that contains methods for other classes.
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
The “try…catch” syntax
try..catch works synchronously: if an exception happens in a “scheduled” code, like in setTimeout, then try..catch won’t catch it
“Throw” operatorJavaScript has many built-in constructors for standard errors: Error, SyntaxError, ReferenceError, TypeError and others. We can use them to create error objects as well
try…catch…finally● If you answer “Yes” to
“Make an error?”, then try -> catch -> finally
● If you say “No”, thentry -> finally
Variables are local inside try..catch..finally
Global catchLet’s imagine we’ve got a fatal error outside of try..catch, and the script died. Like a programming error or something else terrible.
There is none in the specification, but environments usually provide it, because it’s really useful. For instance, Node.JS has process.on(‘uncaughtException’) for that. And in the browser we can assign a function to special window.onerror property. It will run in case of an uncaught error.
Custom errors, extending Error
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Browser environmentDocument Object Model (DOM)
● The document object gives access to the page content. We can change or create anything on the page using it.
Browser environment (2)Browser Object Model (BOM) are additional objects provided by the browser (host environment) to work with everything except the document, such as:● navigator object provides background information about the browser
and the operation system. There are many properties, but two most widely known are: navigator.userAgent – about the current browser, and navigator.platform – about the platform (can help to differ between Windows/Linux/Mac etc).
● location object allows to read the current URL and redirect the browser to a new one
Walking the DOM
As we can see, childNodes looks like an array. But actually it’s not an array, but rather a collection – a special array-like iterable object
Replace childNodes with children. Now it shows only elements
Searching: getElement* and querySelector*
The tag parameter can also be a star "*" for “any tags”
Method Searches by... Can call on an element? Live?getElementById id - -getElementsByName name - ✔
getElementsByTagName tag or '*' ✔ ✔
getElementsByClassName class ✔ ✔
querySelector CSS-selector ✔ -querySelectorAll CSS-selector ✔ -
Live collectionsA collection that always reflect the current state of the document and “auto-update” when it changes
matchesIt checks if elem matches the given CSS-selector
closestAll elements that are directly above the given one are called its ancestors
The method elem.closest(css) looks the nearest ancestor that matches the CSS-selector. The elem itself is also included in the search
DOM node classesThe tagName property exists only for Element nodes.
The nodeName is defined for any Node
innerHTML: the contents
We can append “more HTML” by using elem.innerHTML+="something"
outerHTML: full HTML of the elementThe outerHTML property contains the full HTML of the element. That’s like innerHTML plus the element itself
Beware: unlike innerHTML, writing to outerHTML does not change the element. Instead, it replaces it as a whole in the outer context
outerHTML: full HTML of the element (2)
In the line (*) we take the full HTML of <div>...</div> and replace it by <p>...</p>. In the outer document we can see the new content instead of the <div>. But the old div variable is still the same
Novice developers sometimes make an error here: they modify div.outerHTML and then continue to work with div as if it had the new content in it
nodeValue/data: text node content
For text nodes we can imagine a reason to read or modify them, but why comments? Usually, they are not interesting at all, but sometimes developers embed information into HTML in them
textContent: pure textThe textContent provides access to the text inside the element: only text, minus all <tags>
In practice, reading such text is rarely needed. Writing to textContent is much more useful, because it allows to write text the “safe way”
textContent: pure text (2)With innerHTML we’ll have it inserted “as HTML”, with all HTML tags.
With textContent we’ll have it inserted “as text”, all symbols are treated literally
The “hidden” propertyThe “hidden” attribute and the DOM property specifies whether the element is visible or not
More propertiesDOM elements also have additional properties, many of them provided by the class:● value – the value for <input>, <select> and <textarea>
(HTMLInputElement, HTMLSelectElement…).● href – the “href” for <a href="..."> (HTMLAnchorElement).● id – the value of “id” attribute, for all elements (HTMLElement).● …and much more…
DOM propertiesDOM nodes are regular JavaScript objects. We can alter the
HTML attributes
So, if an attribute is non-standard, there won’t be DOM-property for it
● elem.hasAttribute(name) – checks for existence.● elem.getAttribute(name) – gets the value.● elem.setAttribute(name, value) – sets the value.● elem.removeAttribute(name) – removes the attribute
In HTML language, tags may have attributes. When the browser reads HTML text and creates DOM objects for tags, it recognizes standard attributes and creates DOM properties from them
Property-attribute synchronizationWhen a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vise-versa
DOM properties are typedDOM properties are not always strings● Example: The style attribute is a string, but style property is an object
Non-standard attributes, datasetWhat if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something
● The HTML language is alive, it grows, more attributes appear to suit the needs of developers. There may be unexpected effects in such case
All attributes starting with “data-” are reserved for programmers’ use. They are available in dataset property● If an elem has an attribute named "data-about", it’s available as
elem.dataset.about● Multiword attributes like data-order-state become camel-cased:
dataset.orderState
Modifying the documentDOM modifications is the key to create “live” pages (to create new elements “on the fly” and modify the existing page content)
Methods to create new nodes:
● document.createElement(tag) – creates an element with the given tag,● document.createTextNode(value) – creates a text node (rarely used),● elem.cloneNode(deep) – clones the element, if deep==true then with all
descendants
Modifying the document (2)● parent.appendChild(node)● parent.insertBefore(node, nextSibling)● parent.removeChild(node)● parent.replaceChild(newElem, node)
All these methods return node
Modifying the document (3)● node.append(...nodes or strings) – insert into node, at the end● node.prepend(...nodes or strings) – insert into node, at the beginning● node.before(...nodes or strings) –- insert right before node● node.after(...nodes or strings) –- insert right after node● node.replaceWith(...nodes or strings) –- replace node● node.remove() –- remove the node
● "beforebegin" – insert html right before elem● "afterbegin" – insert html into elem, at the beginning● "beforeend" – insert html into elem, at the end● "afterend" – insert html right after elem
Styles and classesChanging a class is one of the most often actions in scripts. To manage classes, there are two DOM properties:
● className – the string value, good to manage the whole set of classes.● classList – the object with methods add/remove/toggle/contains, good for
individual classes.
The property elem.style is an object that corresponds to what’s written in the "style" attribute
-moz-border-radius, -webkit-border-radius
Full rewrite with style.cssTextNormally, we use style.* to assign individual style properties. We can’t set the full style like div.style="color: red; width: 100px", because div.style is an object, and it’s read-only
Computed styles: getComputedStyleTo read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated):
● The getComputedStyle(elem[, pseudo]) returns the style-like object with them. Read-only
○ getComputedStyle requires the full property name■ We should always ask for the exact property that we want, like paddingLeft or
marginTop or borderTopWidth. Otherwise the correct result is not guaranteed
getComputedStyle requires the full property nameThere are other inconsistencies. As an example, some browsers (Chrome) show 10px in the document below, and some of them (Firefox) – do not
Width/height of the window
ScrollingGet the current scroll
Change the current scroll:
● window.scrollTo(pageX,pageY) – absolute coordinates,● window.scrollBy(x,y) – scroll relative the current place,● elem.scrollIntoView(top) – scroll to make elem visible (align with the
top/bottom of the window)
Window coordinates: getBoundingClientRectThe method elem.getBoundingClientRect() returns window coordinates for elem as an object with properties:
● top – Y-coordinate for the top element edge,
● left – X-coordinate for the left element edge,
● right – X-coordinate for the right element edge,
● bottom – Y-coordinate for the bottom element edge
elementFromPoint(x, y)The call to document.elementFromPoint(x, y) returns the most nested element at window coordinates (x, y)
The method document.elementFromPoint(x,y) only works if (x,y) are inside the visible area. If any of the coordinates is negative or exceeds the window width/height, then it returns null
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
EventsMouse 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.
Event (2)Form element events:● submit – when the visitor submits a <form>.● focus – when the visitor focuses on an element, e.g. on an <input>.
Keyboard events: keydown and keyup – when the visitor presses and then releases the button.
Document events: DOMContentLoaded – when the HTML is loaded and processed, DOM is fully built.
CSS events: transitionend – when a CSS-animation finishes
Event handlers
Event handlers
It works for compatibility reasons, but strongly not recommended
The value of this inside a handler is the element. The one which has the handler on it
Assign a handler to elem.onclick, not elem.ONCLICK, because DOM properties are case-sensitive
addEventListener and removeEventListenerThe fundamental problem of the aforementioned ways to assign handlers – we can’t assign multiple handlers to one event
elem.addEventListener(event, handler[, phase]) to add, removeEventListener to remove (Removal requires the same function)
Event object
● event.type:Event type, here it’s "click".● event.currentTarget: Element that handled the event. That’s exactly the
same as this, unless you bind this to something else, and then event.currentTarget becomes useful.
● event.clientX / event.clientY: Window-relative coordinates of the cursor, for mouse events.
Object handlers: handleEventWe can assign an object as an event handler using addEventListener. When an event occurs, its handleEvent method is called with it
BubblingWhen an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors
event.targetevent.target – is the “target” element that initiated the event, it doesn’t change through the bubbling process.
this – is the “current” element, the one that has a currently running handler on it.
Stopping bubblingA bubbling event goes from the target element straight up. Normally it goes upwards till <html>, and then to document object, and some events even reach window, calling all handlers on the path
But any handler may decide that the event has been fully processed and stop the bubbling
The method for it is event.stopPropagation()
Don’t stop bubbling without a need!Bubbling is convenient. Don’t stop it without a real need: obvious and architecturally well-thought.
Sometimes event.stopPropagation() creates hidden pitfalls that later may become problems. For instance:● We create a nested menu. Each submenu handles clicks on its elements
and calls stopPropagation so that outer menu don’t trigger.● Later we decide to catch clicks on the whole window, to track users’
behavior (where people click). Some analytic systems do that. Usually the code uses document.addEventListener('click'…) to catch all clicks.
● Our analytic won’t work over the area where clicks are stopped by stopPropagation. We’ve got a “dead zone”.
Event delegationThe idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them – we put a single handler on their common ancestor
Please note that this.onClick is bound to this in (*). That’s important, because otherwise this inside it would reference the DOM element (elem), not the menu object, and this[action] would not be what we need
Delegation: actions in markup
The “behavior” patternWe can also use event delegation to add “behaviors” to elements declaratively, with special attributes and classes
TogglerOne more example. A click on an element with the attribute data-toggle-id will show/hide the element with the given id
Browser default actionsThere are many default browser actions:● mousedown – starts the selection (move the mouse to select).● click on <input type="checkbox"> – checks/unchecks the input.● submit – clicking an <input type="submit"> or hitting Enter inside a form
field causes this event to happen, and the browser submits the form after it.
● wheel – rolling a mouse wheel event has scrolling as the default action.● contextmenu – the event happens on a right-click, the action is to show
the browser context menu.● …there are more…
Browser default actions (2)There are two ways to tell the browser we don’t want it to act:
● The main way is to use the event object. There’s a method event.preventDefault().
● If the handler is assigned using on<event> (not by addEventListener), then we can just return false from it.
Dispatching custom events
event type – may be any string, like "click" or our own like "hey-ho!"
options – the object with two optional properties:
● bubbles: true/false – if true, then the event bubbles● cancelable: true/false – if true, then the “default action” may be
prevented.
By default both are false: {bubbles: false, cancelable: false}
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Mouse events basicsEvents order: mousedown → mouseup → click
if we track mousedown and mouseup, then we need it, because these events trigger on any button, so which allows to distinguish between “right-mousedown” and “left-mousedown”. There are the three possible values:
● event.which == 1 – the left button● event.which == 2 – the middle button● event.which == 3 – the right button
No selection on mousedown
Preventing copying
Mouseover/mouseout, relatedTargetThese events are special, because they have a relatedTarget.
For mouseover:
● event.target – is the element where the mouse came over.● event.relatedTarget – is the element from which the mouse came
For mouseout the reverse:
● event.target – is the element that mouse left.● event.relatedTarget – is the new under-the-pointer element (that mouse
left for)
Events frequencyThe mousemove event triggers when the mouse moves. But that doesn’t mean that every pixel leads to an event.
The browser checks the mouse position from time to time. And if it notices changes then triggers the events.
If the mouse moves very fast from #FROM to #TO elements as painted above, then intermediate <div> (or some of them) may be skipped. The mouseout event may trigger on #FROM and then immediately mouseover on #TO
“Extra” mouseout when leaving for a childImagine – a mouse pointer entered an element. The mouseover triggered. Then the cursor goes into a child element. The interesting fact is that mouseout triggers in that case. The cursor is still in the element, but we have a mouseout from it!
We can fix it by using mouseenter/mouseleave events instead
Events mouseenter/mouseleave are like mouseover/mouseout. They also trigger when the mouse pointer enters/leaves the element
Page lifecycleDOMContentLoaded – the browser fully loaded HTML, and the DOM tree is built, but external resources like pictures <img> and stylesheets may be not yet loaded. We can apply JavaScript to elements at this stage
● All scripts are executed except those that are external with async or defer
load – the browser loaded all resources (images, styles etc).
beforeunload/unload – when the user is leaving the page
document.readyStateWhat happens if we set the DOMContentLoaded handler after the document is loaded? Naturally, it never runs.
The document.readyState property gives us information about it. There are 3 possible values:
● "loading" – the document is loading.● "interactive" – the document was fully read.● "complete" – the document was fully read and all resources (like images)
are loaded too
Resource loading
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Navigation: form and elements
Instead of form.elements.login we can write form.login
Backreference: element.form
Form submissionThe submit event triggers when the form is submitted, it is usually used to validate the form before sending it to the server or to abort the submission and process it in JavaScript
here are two main ways to submit a form:
● The first – to click <input type="submit"> or <input type="image">.● The second – press Enter on an input field
Both actions lead to submit event on the form. The handler can check the data, and if there are errors, show them and call event.preventDefault(), then the form won’t be sent to the server
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
JavaScript animationsJavaScript animations can handle things that CSS can’t
From the HTML/CSS point of view, an animation is a gradual change of the style property. For instance, changing style.left from 0px to 100px moves the element
train.style.left
requestAnimationFrame
That schedules the callback function to run in the closest time when the browser wants to do animation. The callback gets one argument – the time passed from the beginning of the page load in microseconds. This time can also be obtained by calling performance.now()
lighter than
Structured animation
Timing functions
ch05-01.html
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
PopupsA popup window is one of the oldest methods to show additional document to user
Most browsers block popups if they are called outside of user-triggered event handlers like onclick
a timeout of 2000ms or less are acceptable, but after it – removes the “trust”
Please note that external document content is only accessible for windows from the same origin (the same protocol://domain:port).
For windows with URLs from another sites, we are able to change the location by assigning newWindow.location=..., but we can’t read the location or access the content. That’s for user safety, so that an evil page can’t open a popup with http://gmail.com and read the data
Cross-window communicationThe “Same Origin” (same site) policy limits access of windows and frame to each other● The idea is that if we have two windows open: one from john-smith.com,
and another one is gmail.com, then we wouldn’t want a script from john-smith.com to read our mail
Two URLs are said to have the “same origin” if they have the same protocol, domain and port
Subdomains may be same-originThere’s an important exclusion in the same-origin policy.
If windows share the same second-level domain, for instance john.site.com, peter.site.com and site.com, we can use JavaScript to assign to document.domain their common second-level domain site.com. Then these windows are treated as having the same origin.
The clickjacking attackThe idea (example):
1. A visitor is lured to the evil page. No matter how.2. The page has a harmlessly-looking link on it (like “get rich now” or “click
here, very funny” and so on).3. Over that link the evil page positions a transparent <iframe> with src from
facebook.com, in such a way that the “Like” button is right above that link. Usually that’s done with z-index.
4. Clicking on that link, the visitor in fact presses that button.
Here we have a half-transparent <iframe src="facebook.html">, and in the example we can see it hovering over the button. A click on the button actually clicks on the iframe, but that’s not visible to the user, because the iframe is transparent.
As a result if the visitor is authorized on facebook (“remember me” is usually turned on), then it adds a “Like”. On Twitter that would be a “Follow” button.
1. Old-school defences (weak)The oldest defence method is the piece of JavaScript that forbids to open the page in a frame (so-called “framebusting”)
That is: if the window finds out that it’s not on the top, then it automatically makes itself the top.
As of now, that’s not reliable, because there are many ways to hack around it.
2. Blocking top-navigationWe can block the transition caused by changing top.location in the beforeunload event.
The top page (that belongs to the hacker) sets a handler to it, and when the iframe tries to change top.location the visitor gets a message asking him whether he wants to leave
In most cases the visitor would answer negatively, because he doesn’t know about the iframe, all he can see is the top page, there’s no reason to leave. And so the top.location won’t change!
3. X-Frame-OptionsServer-side header X-Frame-Options can allow or forbid showing the page inside a frame.
● DENY: Never ever show the page inside an iframe.● SAMEORIGIN: Allow to show inside an iframe if the parent document
comes from the same origin.● ALLOW-FROM domain: Allows to show inside an iframe if the parent
document is from the given domain.
For instance, Twitter uses X-Frame-Options: SAMEORIGINDepending on the browser, the result either empty or it has a message telling that “the browser can’t show it”
4. Showing with disabled functionalityThe protecting X-Frame-Options header has a side-effect. Other sites can’t show our page in an iframe, even if they have “legal” reasons to do so
So there are other solutions. For instance, we can “cover” the page with a <div> with height:100%;width:100%, so that it handles all clicks. That <div> should disappear if window == top or we figure out that we don’t need protection.
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
“regexp”, or just “reg”The long syntax:
…And the short one, using slashes "/":
The str.search method looks for the pattern /love/ and returns the position inside the string
Flagsi: With this flag the search is case-insensitive: no difference between A and a
g: With this flag the search looks for all matches, without it – only the first one
,...
If a part of the pattern is delimited by parentheses (...), then it becomes a separate element of the array.
When there’s a "g" flag, then str.match returns an array of all matches. There are no additional properties in that array, and parentheses do not create any elements.
To find numbers from 3 to 5 digits
The JavaScript language
Materials
● An introduction● JavaScript Fundamentals● Code quality● Objects: the basics● Data types● Advanced working with functions● Objects, classes, inheritance● Error handling● Document● Introduction into Events● Events in details● Forms, controls● Animation● Frames and windows● Regular expressions● Promises, async/await
Introduction: callbacks
Callback in callback
Pyramid of doom
To evade such pyramidsOne of the best ways is to use “promises”
PromisePromise object has internal properties:
● state – initially is “pending”, then changes to “fulfilled” or “rejected”,● result – an arbitrary value, initially undefined.
There can be only one result or an error
Consumers: “.then” and “.catch”
Returning promisesNormally, a value returned by a .then handler is immediately passed to the next handler. But there’s an exception.
If the returned value is a promise, then the further execution is suspended until it settles. After that, the result of that promise is given to the next .then handler.
Promise APIPromise.resolve(value) – makes a resolved promise with the given value,
Promise.reject(error) – makes a rejected promise with the given error,
Promise.all(promises) – waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of Promise.all, and all other results are ignored.
Promise.race(promises) – waits for the first promise to settle, and its result/error becomes the outcome.
Async functionsThe word “async” before a function means one simple thing: a function always returns a promise. If the code has return <non-promise> in it, then JavaScript automatically wraps it into a resolved promise with that value.● We could explicitly return a promise, that would be the same
AwaitThe keyword await makes JavaScript wait until that promise settles and returns its result
Can’t use await in regular functions
await accepts thenables
Async methods
async/await works well with Promise.all
thanks for your attention any questions?