';
+};
+
+const downloadCollage = () => {
+ const date = new Date();
+ const fileName = `Collage-${date.getDay()}-${date.getMonth()}-${date.getFullYear()}.png`;
+ const img = canvasEl.toDataURL("image/png");
+ const link = document.createElement("a");
+ link.download = fileName;
+ link.href = img;
+ link.click();
+ link.remove();
+};
+
+const changeLayout = ({ target }) => {
+ state.currentLayout = JSON.parse(target.value);
+};
+
+// Listeners.
+selectEl.addEventListener("change", changeLayout);
+createCollageBtn.addEventListener("click", createCollage);
+startOverBtn.addEventListener("click", startOver);
+downloadBtn.addEventListener("click", downloadCollage);
diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js
new file mode 100644
index 000000000..f0140c116
--- /dev/null
+++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js
@@ -0,0 +1,321 @@
+const loggerContainerEl = document.querySelector(".loggerContainer");
+
+export const images = [
+ {
+ img: "https://images.unsplash.com/photo-1471357674240-e1a485acb3e1",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1589118949245-7d38baf380d6",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1527631746610-bca00a040d60",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1500835556837-99ac94a94552",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1503220317375-aaad61436b1b",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1501785888041-af3ef285b470",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1528543606781-2f6e6857f318",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1539635278303-d4002c07eae3",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1533105079780-92b9be482077",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1516483638261-f4dbaf036963",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1502791451862-7bd8c1df43a7",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1663047367140-91adf819d007",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1506197603052-3cc9c3a201bd",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1517760444937-f6397edcbbcd",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1518684079-3c830dcef090",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1505832018823-50331d70d237",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1524850011238-e3d235c7d4c9",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1661277758451-b5053309eea1",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1541410965313-d53b3c16ef17",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1528702748617-c64d49f918af",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1502003148287-a82ef80a6abc",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1661281272544-5204ea3a481a",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1503457574462-bd27054394c1",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1499363536502-87642509e31b",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1551918120-9739cb430c6d",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1661382219642-43e54f7e81d7",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1497262693247-aa258f96c4f5",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1525254134158-4fd5fdd45793",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1661274025419-4c54107d5c48",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1553697388-94e804e2f0f6",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1574260031597-bcd9eb192b4f",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1536323760109-ca8c07450053",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1527824404775-dce343118ebc",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1612278675615-7b093b07772d",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1522010675502-c7b3888985f6",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1501555088652-021faa106b9b",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1669223469435-27e091439169",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1506012787146-f92b2d7d6d96",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1511739001486-6bfe10ce785f",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1553342385-111fd6bc6ab3",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1516546453174-5e1098a4b4af",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1527142879-95b61a0b8226",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1520466809213-7b9a56adcd45",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1516939884455-1445c8652f83",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1545389336-cf090694435e",
+ },
+ {
+ img: "https://plus.unsplash.com/premium_photo-1669223469455-b7b734c838f4",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1454391304352-2bf4678b1a7a",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1433838552652-f9a46b332c40",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1506125840744-167167210587",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1522199873717-bc67b1a5e32b",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1495904786722-d2b5a19a8535",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1614094082869-cd4e4b2905c7",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1474755032398-4b0ed3b2ae5c",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1501554728187-ce583db33af7",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1515859005217-8a1f08870f59",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1531141445733-14c2eb7d4c1f",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1500259783852-0ca9ce8a64dc",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1510662145379-13537db782dc",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1573790387438-4da905039392",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1512757776214-26d36777b513",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1518855706573-84de4022b69b",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1500049242364-5f500807cdd7",
+ },
+ {
+ img: "https://images.unsplash.com/photo-1528759335187-3b683174c86a",
+ },
+];
+export const THUMBNAIL_PARAMS = "w=240&h=240&fit=crop&auto=format";
+
+// Console styles.
+export const CONSOLE_BASE_STYLES = [
+ "font-size: 12px",
+ "padding: 4px",
+ "border: 2px solid #5a5a5a",
+ "color: white",
+].join(";");
+export const CONSOLE_PRIMARY = [
+ CONSOLE_BASE_STYLES,
+ "background-color: #13315a",
+].join(";");
+export const CONSOLE_SUCCESS = [
+ CONSOLE_BASE_STYLES,
+ "background-color: #385a4e",
+].join(";");
+export const CONSOLE_ERROR = [
+ CONSOLE_BASE_STYLES,
+ "background-color: #5a1a24",
+].join(";");
+
+// Layouts.
+export const LAYOUT_4_COLUMNS = {
+ name: "Layout 4 columns",
+ columns: 4,
+ itemWidth: 240,
+ itemHeight: 240,
+};
+export const LAYOUT_8_COLUMNS = {
+ name: "Layout 8 columns",
+ columns: 8,
+ itemWidth: 240,
+ itemHeight: 240,
+};
+export const LAYOUTS = [LAYOUT_4_COLUMNS, LAYOUT_8_COLUMNS];
+
+export const createImageFile = async (src) =>
+ new Promise((resolve, reject) => {
+ const img = new Image();
+ img.src = src;
+ img.onload = () => resolve(img);
+ img.onerror = () => reject(new Error("Failed to construct image."));
+ });
+
+export const loadImage = async (url) => {
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(String(response.status));
+ }
+
+ return await response.blob();
+ } catch (e) {
+ console.log(`%cFETCHED_FAILED: ${e}`, CONSOLE_ERROR);
+ }
+};
+
+export const weakRefCache = (fetchImg) => {
+ const imgCache = new Map();
+ const registry = new FinalizationRegistry(({ imgName, size, type }) => {
+ const cachedImg = imgCache.get(imgName);
+ if (cachedImg && !cachedImg.deref()) {
+ imgCache.delete(imgName);
+ console.log(
+ `%cCLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`,
+ CONSOLE_ERROR,
+ );
+
+ const logEl = document.createElement("div");
+ logEl.classList.add("logger-item", "logger--error");
+ logEl.innerHTML = `CLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`;
+ loggerContainerEl.appendChild(logEl);
+ loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;
+ }
+ });
+
+ return async (imgName) => {
+ const cachedImg = imgCache.get(imgName);
+
+ if (cachedImg?.deref() !== undefined) {
+ console.log(
+ `%cCACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`,
+ CONSOLE_SUCCESS,
+ );
+
+ const logEl = document.createElement("div");
+ logEl.classList.add("logger-item", "logger--success");
+ logEl.innerHTML = `CACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`;
+ loggerContainerEl.appendChild(logEl);
+ loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;
+
+ return cachedImg?.deref();
+ }
+
+ const newImg = await fetchImg(imgName);
+ console.log(
+ `%cFETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`,
+ CONSOLE_PRIMARY,
+ );
+
+ const logEl = document.createElement("div");
+ logEl.classList.add("logger-item", "logger--primary");
+ logEl.innerHTML = `FETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`;
+ loggerContainerEl.appendChild(logEl);
+ loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;
+
+ imgCache.set(imgName, new WeakRef(newImg));
+ registry.register(newImg, {
+ imgName,
+ size: newImg.size,
+ type: newImg.type,
+ });
+
+ return newImg;
+ };
+};
+
+export const stateObj = {
+ loading: false,
+ drawing: true,
+ collageRendered: false,
+ currentLayout: LAYOUTS[0],
+ selectedImages: new Set(),
+};
diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md
index f680554dd..eedc28fb3 100644
--- a/2-ui/1-document/01-browser-environment/article.md
+++ b/2-ui/1-document/01-browser-environment/article.md
@@ -1,12 +1,12 @@
# Browser environment, specs
-The JavaScript language was initially created for web browsers. Since then it has evolved and become a language with many uses and platforms.
+The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms.
-A platform may be a browser, or a web-server or another *host*, even a coffee machine. Each of them provides platform-specific functionality. The JavaScript specification calls that a *host environment*.
+A platform may be a browser, or a web-server or another *host*, or even a "smart" coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a *host environment*.
-A host environment provides own objects and functions additional to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on.
+A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on.
-Here's a bird's-eye view of what we have when JavaScript runs in a web-browser:
+Here's a bird's-eye view of what we have when JavaScript runs in a web browser:

@@ -15,9 +15,9 @@ There's a "root" object called `window`. It has two roles:
1. First, it is a global object for JavaScript code, as described in the chapter .
2. Second, it represents the "browser window" and provides methods to control it.
-For instance, here we use it as a global object:
+For instance, we can use it as a global object:
-```js run
+```js run global
function sayHi() {
alert("Hello");
}
@@ -26,17 +26,17 @@ function sayHi() {
window.sayHi();
```
-And here we use it as a browser window, to see the window height:
+And we can use it as a browser window, to show the window height:
```js run
alert(window.innerHeight); // inner window height
```
-There are more window-specific methods and properties, we'll cover them later.
+There are more window-specific methods and properties, which we'll cover later.
## DOM (Document Object Model)
-Document Object Model, or DOM for short, represents all page content as objects that can be modified.
+The Document Object Model, or DOM for short, represents all page content as objects that can be modified.
The `document` object is the main "entry point" to the page. We can change or create anything on the page using it.
@@ -49,20 +49,18 @@ document.body.style.background = "red";
setTimeout(() => document.body.style.background = "", 1000);
```
-Here we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification:
-
-- **DOM Living Standard** at
+Here, we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification: [DOM Living Standard](https://dom.spec.whatwg.org).
```smart header="DOM is not only for browsers"
The DOM specification explains the structure of a document and provides objects to manipulate it. There are non-browser instruments that use DOM too.
-For instance, server-side scripts that download HTML pages and process them can also use DOM. They may support only a part of the specification though.
+For instance, server-side scripts that download HTML pages and process them can also use the DOM. They may support only a part of the specification though.
```
```smart header="CSSOM for styling"
-CSS rules and stylesheets are structured in a different way than HTML. There's a separate specification, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/), that explains how they are represented as objects, and how to read and write them.
+There's also a separate specification, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) for CSS rules and stylesheets, that explains how they are represented as objects, and how to read and write them.
-CSSOM is used together with DOM when we modify style rules for the document. In practice though, CSSOM is rarely required, because usually CSS rules are static. We rarely need to add/remove CSS rules from JavaScript, but that's also possible.
+The CSSOM is used together with the DOM when we modify style rules for the document. In practice though, the CSSOM is rarely required, because we rarely need to modify CSS rules from JavaScript (usually we just add/remove CSS classes, not modify their CSS rules), but that's also possible.
```
## BOM (Browser Object Model)
@@ -71,7 +69,7 @@ The Browser Object Model (BOM) represents additional objects provided by the bro
For instance:
-- The [navigator](mdn:api/Window/navigator) object provides background information about the browser and the operating system. There are many properties, but the 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).
+- The [navigator](mdn:api/Window/navigator) object provides background information about the browser and the operating system. There are many properties, but the two most widely known are: `navigator.userAgent` -- about the current browser, and `navigator.platform` -- about the platform (can help to differentiate between Windows/Linux/Mac etc).
- The [location](mdn:api/Window/location) object allows us to read the current URL and can redirect the browser to a new one.
Here's how we can use the `location` object:
@@ -83,12 +81,12 @@ if (confirm("Go to Wikipedia?")) {
}
```
-Functions `alert/confirm/prompt` are also a part of BOM: they are directly not related to the document, but represent pure browser methods of communicating with the user.
+The functions `alert/confirm/prompt` are also a part of the BOM: they are not directly related to the document, but represent pure browser methods for communicating with the user.
```smart header="Specifications"
-BOM is the part of the general [HTML specification](https://html.spec.whatwg.org).
+The BOM is a part of the general [HTML specification](https://html.spec.whatwg.org).
-Yes, you heard that right. The HTML spec at is not only about the "HTML language" (tags, attributes), but also covers a bunch of objects, methods and browser-specific DOM extensions. That's "HTML in broad terms". Also, some parts have additional specs listed at .
+Yes, you heard that right. The HTML spec at is not only about the "HTML language" (tags, attributes), but also covers a bunch of objects, methods, and browser-specific DOM extensions. That's "HTML in broad terms". Also, some parts have additional specs listed at .
```
## Summary
@@ -96,20 +94,20 @@ Yes, you heard that right. The HTML spec at is no
Talking about standards, we have:
DOM specification
-: Describes the document structure, manipulations and events, see .
+: Describes the document structure, manipulations, and events, see .
CSSOM specification
-: Describes stylesheets and style rules, manipulations with them and their binding to documents, see .
+: Describes stylesheets and style rules, manipulations with them, and their binding to documents, see .
HTML specification
: Describes the HTML language (e.g. tags) and also the BOM (browser object model) -- various browser functions: `setTimeout`, `alert`, `location` and so on, see . It takes the DOM specification and extends it with many additional properties and methods.
Additionally, some classes are described separately at .
-Please note these links, as there's so much stuff to learn it's impossible to cover and remember everything.
+Please note these links, as there's so much to learn that it's impossible to cover everything and remember it all.
-When you'd like to read about a property or a method, the Mozilla manual at is also a nice resource, but the corresponding spec may be better: it's more complex and longer to read, but will make your fundamental knowledge sound and complete.
+When you'd like to read about a property or a method, the Mozilla manual at is also a nice resource, but the corresponding spec may be better: it's more complex and longer to read, but will make your fundamental knowledge sound and complete.
To find something, it's often convenient to use an internet search "WHATWG [term]" or "MDN [term]", e.g , .
-Now we'll get down to learning DOM, because the document plays the central role in the UI.
+Now, we'll get down to learning the DOM, because the document plays the central role in the UI.
diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md
index 019398be9..f7f2be91d 100644
--- a/2-ui/1-document/02-dom-nodes/article.md
+++ b/2-ui/1-document/02-dom-nodes/article.md
@@ -51,7 +51,7 @@ The DOM represents HTML as a tree structure of tags. Here's how it looks:
@@ -143,7 +143,7 @@ drawHtmlTree(node4, 'div.domtree', 690, 360);
````warn header="Tables always have ``"
-An interesting "special case" is tables. By the DOM specification they must have ``, but HTML text may (officially) omit it. Then the browser creates `` in the DOM automatically.
+An interesting "special case" is tables. By DOM specification they must have `` tag, but HTML text may omit it. Then the browser creates `` in the DOM automatically.
For the HTML:
@@ -160,7 +160,7 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType":
drawHtmlTree(node5, 'div.domtree', 600, 200);
-You see? The `` appeared out of nowhere. You should keep this in mind while working with tables to avoid surprises.
+You see? The `` appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises.
````
## Other node types
@@ -188,7 +188,7 @@ For example, comments:
@@ -199,7 +199,7 @@ We may think -- why is a comment added to the DOM? It doesn't affect the visual
**Everything in HTML, even comments, becomes a part of the DOM.**
-Even the `` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before ``. We are not going to touch that node, we even don't draw it on diagrams for that reason, but it's there.
+Even the `` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before ``. Few people know about that. We are not going to touch that node, we even don't draw it on diagrams, but it's there.
The `document` object that represents the whole document is, formally, a DOM node as well.
@@ -212,7 +212,7 @@ There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usu
## See it for yourself
-To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up as a DOM at an instant.
+To see the DOM structure in real-time, try [Live DOM Viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up as a DOM at an instant.
Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing.
diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md
index f7123d70d..b5f03098c 100644
--- a/2-ui/1-document/03-dom-navigation/article.md
+++ b/2-ui/1-document/03-dom-navigation/article.md
@@ -214,7 +214,7 @@ alert( document.body.previousSibling ); // HTMLHeadElement
## Element-only navigation
-Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if there exist.
+Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist.
But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page.
diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md
index f5ab0b785..405129694 100644
--- a/2-ui/1-document/04-searching-elements-dom/article.md
+++ b/2-ui/1-document/04-searching-elements-dom/article.md
@@ -55,7 +55,7 @@ Also, there's a global variable named by `id` that references the element:
```
```warn header="Please don't use id-named global variables to access elements"
-This behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), so it's kind of standard. But it is supported mainly for compatibility.
+This behavior is described [in the specification](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), but it is supported mainly for compatibility.
The browser tries to help us by mixing namespaces of JS and DOM. That's fine for simple scripts, inlined into HTML, but generally isn't a good thing. There may be naming conflicts. Also, when one reads JS code and doesn't have HTML in view, it's not obvious where the variable comes from.
@@ -71,7 +71,7 @@ If there are multiple elements with the same `id`, then the behavior of methods
```
```warn header="Only `document.getElementById`, not `anyElem.getElementById`"
-The method `getElementById` that can be called only on `document` object. It looks for the given `id` in the whole document.
+The method `getElementById` can be called only on `document` object. It looks for the given `id` in the whole document.
```
## querySelectorAll [#querySelectorAll]
@@ -116,7 +116,7 @@ In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but t
Previous methods were searching the DOM.
-The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
+The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us.
@@ -142,7 +142,7 @@ For instance:
*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
-The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
+The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned.
@@ -154,7 +154,7 @@ For instance:
Chapter 1
-
Chapter 1
+
Chapter 2
@@ -363,7 +363,7 @@ There are 6 main methods to search for nodes in DOM:
-By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts.
+By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts.
Besides that:
diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md
index 76469c187..7a31f62d4 100644
--- a/2-ui/1-document/05-basic-dom-node-properties/article.md
+++ b/2-ui/1-document/05-basic-dom-node-properties/article.md
@@ -10,7 +10,7 @@ Different DOM nodes may have different properties. For instance, an element node
Each DOM node belongs to the corresponding built-in class.
-The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
+The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
Here's the picture, explanations to follow:
@@ -18,16 +18,46 @@ Here's the picture, explanations to follow:
The classes are:
+<<<<<<< HEAD
- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class. Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
- [Node](http://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are concrete node classes that inherit from it, namely: `Text` for text nodes, `Element` for element nodes and more exotic ones like `Comment` for comment nodes.
- [Element](http://dom.spec.whatwg.org/#interface-element) -- is a base class for DOM elements. It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. A browser supports not only HTML, but also XML and SVG. The `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` and `HTMLElement`.
- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- is finally the basic class for all HTML elements. It is inherited by concrete HTML elements:
+=======
+- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything.
+
+ Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
+
+- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes.
+
+ It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality).
+
+- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole.
+
+ The `document` global object belongs exactly to this class. It serves as an entry point to the DOM.
+
+- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by:
+ - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `
` in the table.
diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
index 4522006ae..8d6472ee6 100644
--- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
+++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css
@@ -4,16 +4,6 @@ body {
font: 75%/120% sans-serif;
}
-h2 {
- font: bold 190%/100% sans-serif;
- margin: 0 0 .2em;
-}
-
-h2 em {
- font: normal 80%/100% sans-serif;
- color: #999999;
-}
-
#largeImg {
border: solid 1px #ccc;
width: 550px;
diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
index b6e523014..8d6472ee6 100644
--- a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
+++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css
@@ -4,16 +4,6 @@ body {
font: 75%/120% sans-serif;
}
-h2 {
- font: bold 190%/100% sans-serif;
- margin: 0 0 .2em;
-}
-
-h2 em {
- font: normal 80%/100% sans-serif;
- color: #999999;
-}
-
#largeImg {
border: solid 1px #ccc;
width: 550px;
@@ -32,4 +22,13 @@ h2 em {
#thumbs a:hover {
border-color: #FF9900;
+}
+
+#thumbs li {
+ list-style: none;
+}
+
+#thumbs {
+ margin: 0;
+ padding: 0;
}
\ No newline at end of file
diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md
index ceac160c1..cd815654f 100644
--- a/2-ui/2-events/04-default-browser-action/article.md
+++ b/2-ui/2-events/04-default-browser-action/article.md
@@ -17,7 +17,7 @@ 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` (not by `addEventListener`), then returning `false` also works the same.
-In this HTML a click on a link doesn't lead to navigation, browser doesn't do anything:
+In this HTML, a click on a link doesn't lead to navigation; the browser doesn't do anything:
```html autorun height=60 no-beautify
Click here
@@ -96,7 +96,7 @@ That's because the browser action is canceled on `mousedown`. The focusing is st
The optional `passive: true` option of `addEventListener` signals the browser that the handler is not going to call `preventDefault()`.
-Why that may be needed?
+Why might that be needed?
There are some events like `touchmove` on mobile devices (when the user moves their finger across the screen), that cause scrolling by default, but that scrolling can be prevented using `preventDefault()` in the handler.
diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md
index ee3e74875..b93acb3f1 100644
--- a/2-ui/2-events/05-dispatch-events/article.md
+++ b/2-ui/2-events/05-dispatch-events/article.md
@@ -8,7 +8,7 @@ We can generate not only completely new events, that we invent for our own purpo
## Event constructor
-Build-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class.
+Built-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](https://dom.spec.whatwg.org/#events) class.
We can create `Event` objects like this:
@@ -162,7 +162,7 @@ Besides, the event class describes "what kind of event" it is, and if the event
## event.preventDefault()
-Many browser events have a "default action", such as nagivating to a link, starting a selection, and so on.
+Many browser events have a "default action", such as navigating to a link, starting a selection, and so on.
For new, custom events, there are definitely no default browser actions, but a code that dispatches such event may have its own plans what to do after triggering the event.
@@ -187,7 +187,6 @@ Any handler can listen for that event with `rabbit.addEventListener('hide',...)`
```
The output order is: 1 -> nested -> 2.
-Please note that the nested event `menu-open` fully bubbles up and is handled on the `document`. The propagation and handling of the nested event must be fully finished before the processing gets back to the outer code (`onclick`).
+Please note that the nested event `menu-open` is caught on the `document`. The propagation and handling of the nested event is finished before the processing gets back to the outer code (`onclick`).
+
+That's not only about `dispatchEvent`, there are other cases. If an event handler calls methods that trigger other events -- they are processed synchronously too, in a nested fashion.
-That's not only about `dispatchEvent`, there are other cases. JavaScript in an event handler can call methods that lead to other events -- they are too processed synchronously.
+Let's say we don't like it. We'd want `onclick` to be fully processed first, independently from `menu-open` or any other nested events.
-If we don't like it, we can either put the `dispatchEvent` (or other event-triggering call) at the end of `onclick` or, maybe better, wrap it in zero-delay `setTimeout`:
+Then we can either put the `dispatchEvent` (or another event-triggering call) at the end of `onclick` or, maybe better, wrap it in the zero-delay `setTimeout`:
```html run
@@ -253,7 +259,6 @@ If we don't like it, we can either put the `dispatchEvent` (or other event-trigg
menu.onclick = function() {
alert(1);
- // alert(2)
setTimeout(() => menu.dispatchEvent(new CustomEvent("menu-open", {
bubbles: true
})));
@@ -265,7 +270,7 @@ If we don't like it, we can either put the `dispatchEvent` (or other event-trigg
```
-Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `mouse.onclick`, so event handlers are totally separate.
+Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `menu.onclick`, so event handlers are totally separate.
The output order becomes: 1 -> 2 -> nested.
@@ -281,9 +286,9 @@ Other constructors of native events like `MouseEvent`, `KeyboardEvent` and so on
For custom events we should use `CustomEvent` constructor. It has an additional option named `detail`, we should assign the event-specific data to it. Then all handlers can access it as `event.detail`.
-Despite the technical possibility to generate browser events like `click` or `keydown`, we should use with the great care.
+Despite the technical possibility of generating browser events like `click` or `keydown`, we should use them with great care.
-We shouldn't generate browser events as it's a hacky way to run handlers. That's a bad architecture most of the time.
+We shouldn't generate browser events as it's a hacky way to run handlers. That's bad architecture most of the time.
Native events might be generated:
diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md
index 4a5ea6416..5fdb9869a 100644
--- a/2-ui/3-event-details/1-mouse-events-basics/article.md
+++ b/2-ui/3-event-details/1-mouse-events-basics/article.md
@@ -1,4 +1,5 @@
-# Mouse events basics
+
+# Mouse events
In this chapter we'll get into more details about mouse events and their properties.
@@ -6,11 +7,7 @@ Please note: such events may come not only from "mouse devices", but are also fr
## Mouse event types
-We can split mouse events into two categories: "simple" and "complex"
-
-### Simple events
-
-The most used simple events are:
+We've already seen some of these events:
`mousedown/mouseup`
: Mouse button is clicked/released over an element.
@@ -21,54 +18,76 @@ The most used simple events are:
`mousemove`
: Every mouse move over an element triggers that event.
-`contextmenu`
-: Triggers when opening a context menu is attempted. In the most common case, that happens when the right mouse button is pressed. Although, there are other ways to open a context menu, e.g. using a special keyboard key, so it's not exactly the mouse event.
-
-...There are several other event types too, we'll cover them later.
-
-### Complex events
-
`click`
: Triggers after `mousedown` and then `mouseup` over the same element if the left mouse button was used.
`dblclick`
-: Triggers after a double click over an element.
+: Triggers after two clicks on the same element within a short timeframe. Rarely used nowadays.
-Complex events are made of simple ones, so in theory we could live without them. But they exist, and that's good, because they are convenient.
+`contextmenu`
+: Triggers when the right mouse button is pressed. There are other ways to open a context menu, e.g. using a special keyboard key, it triggers in that case also, so it's not exactly the mouse event.
-### Events order
+...There are several other events too, we'll cover them later.
-An action may trigger multiple events.
+## Events order
-For instance, a click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released.
+As you can see from the list above, a user action may trigger multiple events.
+
+For instance, a left-button click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released.
In cases when a single action initiates multiple events, their order is fixed. That is, the handlers are called in the order `mousedown` -> `mouseup` -> `click`.
```online
Click the button below and you'll see the events. Try double-click too.
+<<<<<<< HEAD
On the teststand below all mouse events are logged, and if there is more than a 1 second delay between them they are separated by a horizontal ruler.
+=======
+On the teststand below, all mouse events are logged, and if there is more than a 1 second delay between them, they are separated by a horizontal rule.
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
-Also we can see the `which` property that allows to detect the mouse button.
+Also, we can see the `button` property that allows us to detect the mouse button; it's explained below.
```
-## Getting the button: which
+## Mouse button
-Click-related events always have the `which` property, which allows to get the exact mouse button.
+Click-related events always have the `button` property, which allows to get the exact mouse button.
-It is not used for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click.
+We usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click.
-But 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".
+On the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown".
-There are the three possible values:
+The possible values of `event.button` are:
+<<<<<<< HEAD
- `event.which == 1` -- the left button
- `event.which == 2` -- the middle button
- `event.which == 3` -- the right button
+=======
+| Button state | `event.button` |
+|--------------|----------------|
+| Left button (primary) | 0 |
+| Middle button (auxiliary) | 1 |
+| Right button (secondary) | 2 |
+| X1 button (back) | 3 |
+| X2 button (forward) | 4 |
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
+
+Most mouse devices only have the left and right buttons, so possible values are `0` or `2`. Touch devices also generate similar events when one taps on them.
-The middle button is somewhat exotic right now and is very rarely used.
+Also there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](mdn:/api/MouseEvent/buttons) if you ever need it.
+
+```warn header="The outdated `event.which`"
+Old code may use `event.which` property that's an old non-standard way of getting a button, with possible values:
+
+- `event.which == 1` – left button,
+- `event.which == 2` – middle button,
+- `event.which == 3` – right button.
+
+As of now, `event.which` is deprecated, we shouldn't use it.
+```
## Modifiers: shift, alt, ctrl and meta
@@ -116,17 +135,29 @@ For JS-code it means that we should check `if (event.ctrlKey || event.metaKey)`.
```
```warn header="There are also mobile devices"
+<<<<<<< HEAD
Keyboard combinations are good as an addition to the workflow. So that if the visitor has a keyboard -- it works. And if their device doesn't have it -- then there should be another way to do the same.
+=======
+Keyboard combinations are good as an addition to the workflow. So that if the visitor uses a keyboard -- they work.
+
+But if their device doesn't have it -- then there should be a way to live without modifier keys.
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
```
## Coordinates: clientX/Y, pageX/Y
-All mouse events have coordinates in two flavours:
+All mouse events provide coordinates in two flavours:
1. Window-relative: `clientX` and `clientY`.
2. Document-relative: `pageX` and `pageY`.
-For instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`. And if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is, how far the document was scrolled. They are similar to `position:fixed`.
+We already covered the difference between them in the chapter .
+
+In short, document-relative coordinates `pageX/Y` are counted from the left-upper corner of the document, and do not change when the page is scrolled, while `clientX/Y` are counted from the current window left-upper corner. When the page is scrolled, they change.
+
+For instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`, no matter how the page is scrolled.
+
+And if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is. They are similar to `position:fixed` in that aspect.
````online
Move the mouse over the input field to see `clientX/clientY` (the example is in the `iframe`, so coordinates are relative to that `iframe`):
@@ -136,13 +167,11 @@ Move the mouse over the input field to see `clientX/clientY` (the example is in
```
````
-Document-relative coordinates `pageX`, `pageY` are counted from the left-upper corner of the document, not the window. You can read more about coordinates in the chapter .
-
-## Disabling selection
+## Preventing selection on mousedown
-Double mouse click has a side-effect that may be disturbing in some interfaces: it selects the text.
+Double mouse click has a side effect that may be disturbing in some interfaces: it selects text.
-For instance, a double-click on the text below selects it in addition to our handler:
+For instance, double-clicking on the text below selects it in addition to our handler:
```html autorun height=50
Double-click me
@@ -185,7 +214,7 @@ Surely the user has access to HTML-source of the page, and can take the content
Mouse events have the following properties:
-- Button: `which`.
+- Button: `button`.
- Modifier keys (`true` if pressed): `altKey`, `ctrlKey`, `shiftKey` and `metaKey` (Mac).
- If you want to handle `key:Ctrl`, then don't forget Mac users, they usually use `key:Cmd`, so it's better to check `if (e.metaKey || e.ctrlKey)`.
diff --git a/2-ui/3-event-details/1-mouse-events-basics/head.html b/2-ui/3-event-details/1-mouse-events-basics/head.html
index f578fb7db..1b9a73fca 100644
--- a/2-ui/3-event-details/1-mouse-events-basics/head.html
+++ b/2-ui/3-event-details/1-mouse-events-basics/head.html
@@ -25,7 +25,7 @@
function logMouse(e) {
let evt = e.type;
while (evt.length < 11) evt += ' ';
- showmesg(evt + " which=" + e.which, 'test')
+ showmesg(evt + " button=" + e.button, 'test')
return false;
}
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
index e998165fd..84d52b18c 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html
@@ -54,7 +54,7 @@
Once upon a time there was a mother pig who had three little pigs.
-
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
+
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
The three little pigs set off. "We will take care that the wolf does not catch us," they said.
Once upon a time there was a mother pig who had three little pigs.
-
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
+
The three little pigs grew so big that their mother said to them, "You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you."
The three little pigs set off. "We will take care that the wolf does not catch us," they said.
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
index 4e6e2a3e9..7503ca9c2 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js
@@ -88,7 +88,7 @@ class HoverIntent {
if (speed < this.sensitivity) {
clearInterval(this.checkSpeedInterval);
this.isHover = true;
- this.over.call(this.elem, event);
+ this.over.call(this.elem);
} else {
// speed fast, remember new coordinates as the previous ones
this.prevX = this.lastX;
diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
index c7ac0d4db..d409c3f12 100644
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md
@@ -80,7 +80,7 @@ An important feature of `mouseout` -- it triggers, when the pointer moves from a
```
-If we're on `#parent` and then move the pointer deeper into `#child`, but we get `mouseout` on `#parent`!
+If we're on `#parent` and then move the pointer deeper into `#child`, we get `mouseout` on `#parent`!

diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
index 6d87199c2..5752e83ae 100755
--- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
+++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js
@@ -3,7 +3,7 @@ parent.onmouseover = parent.onmouseout = parent.onmousemove = handler;
function handler(event) {
let type = event.type;
- while (type < 11) type += ' ';
+ while (type.length < 11) type += ' ';
log(type + " target=" + event.target.id)
return false;
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md
index 0dc0d05c9..6caddd7d9 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md
@@ -4,9 +4,15 @@ Drag'n'Drop is a great interface solution. Taking something and dragging and dro
In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend`, and so on.
+<<<<<<< HEAD
These events are useful in that they allow us to solve simple tasks easily. For instance, they allow us to handle the drag'n'drop of "external" files into the browser, so we can take a file in the OS file-manager and drop it into the browser window, thereby giving JavaScript access to its contents.
But native Drag Events also have limitations. For instance, we can't limit dragging by a certain area. Also we can't make it "horizontal" or "vertical" only. And there are other drag'n'drop tasks that can't be done using that API. Also, mobile device support for such events is almost non-existant.
+=======
+These events allow us to support special kinds of drag'n'drop, such as handling dragging a file from OS file-manager and dropping it into the browser window. Then JavaScript can access the contents of such files.
+
+But native Drag Events also have limitations. For instance, we can't prevent dragging from a certain area. Also we can't make the dragging "horizontal" or "vertical" only. And there are many other drag'n'drop tasks that can't be done using them. Also, mobile device support for such events is very weak.
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
So here we'll see how to implement Drag'n'Drop using mouse events.
@@ -14,26 +20,23 @@ So here we'll see how to implement Drag'n'Drop using mouse events.
The basic Drag'n'Drop algorithm looks like this:
-1. On `mousedown` - prepare the element for moving, if needed (maybe create a copy of it).
-2. Then on `mousemove` move it by changing `left/top` and `position:absolute`.
-3. On `mouseup` - perform all actions related to a finished Drag'n'Drop.
+1. On `mousedown` - prepare the element for moving, if needed (maybe create a clone of it, add a class to it or whatever).
+2. Then on `mousemove` move it by changing `left/top` with `position:absolute`.
+3. On `mouseup` - perform all actions related to finishing the drag'n'drop.
-These are the basics. Later we can extend it, for instance, by highlighting droppable (available for the drop) elements when hovering over them.
+These are the basics. Later we'll see how to add other features, such as highlighting current underlying elements while we drag over them.
-Here's the algorithm for drag'n'drop of a ball:
+Here's the implementation of dragging a ball:
```js
-ball.onmousedown = function(event) { // (1) start the process
-
- // (2) prepare to moving: make absolute and on top by z-index
+ball.onmousedown = function(event) {
+ // (1) prepare to moving: make absolute and on top by z-index
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
+
// move it out of any current parents directly into body
// to make it positioned relative to the body
- document.body.append(ball);
- // ...and put that absolutely positioned ball under the pointer
-
- moveAt(event.pageX, event.pageY);
+ document.body.append(ball);
// centers the ball at (pageX, pageY) coordinates
function moveAt(pageX, pageY) {
@@ -41,14 +44,17 @@ ball.onmousedown = function(event) { // (1) start the process
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
}
+ // move our absolutely positioned ball under the pointer
+ moveAt(event.pageX, event.pageY);
+
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
- // (3) move the ball on mousemove
+ // (2) move the ball on mousemove
document.addEventListener('mousemove', onMouseMove);
- // (4) drop the ball, remove unneeded handlers
+ // (3) drop the ball, remove unneeded handlers
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
@@ -64,10 +70,10 @@ Here's an example in action:
[iframe src="ball" height=230]
-Try to drag'n'drop the mouse and you'll see such behavior.
+Try to drag'n'drop with the mouse and you'll see such behavior.
```
-That's because the browser has its own Drag'n'Drop for images and some other elements that runs automatically and conflicts with ours.
+That's because the browser has its own drag'n'drop support for images and some other elements. It runs automatically and conflicts with ours.
To disable it:
@@ -93,14 +99,14 @@ So we should listen on `document` to catch it.
## Correct positioning
-In the examples above the ball is always moved so, that it's center is under the pointer:
+In the examples above the ball is always moved so that its center is under the pointer:
```js
ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
```
-Not bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer.
+Not bad, but there's a side effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer.
It would be better if we keep the initial shift of the element relative to the pointer.
@@ -124,7 +130,11 @@ Let's update our algorithm:
```js
// onmousemove
+<<<<<<< HEAD
// ball has position:absoute
+=======
+ // ball has position:absolute
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
ball.style.left = event.pageX - *!*shiftX*/!* + 'px';
ball.style.top = event.pageY - *!*shiftY*/!* + 'px';
```
@@ -219,7 +229,7 @@ That's why the initial idea to put handlers on potential droppables doesn't work
So, what to do?
-There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window).
+There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window). If there are multiple overlapping elements on the same coordinates, then the topmost one is returned.
We can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this:
@@ -276,7 +286,7 @@ function onMouseMove(event) {
}
```
-In the example below when the ball is dragged over the soccer gate, the gate is highlighted.
+In the example below when the ball is dragged over the soccer goal, the goal is highlighted.
[codetabs height=250 src="ball4"]
@@ -300,4 +310,8 @@ We can lay a lot on this foundation.
- We can use event delegation for `mousedown/up`. A large-area event handler that checks `event.target` can manage Drag'n'Drop for hundreds of elements.
- And so on.
+<<<<<<< HEAD
There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-part solution.
+=======
+There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-party solution.
+>>>>>>> 035c5267ba80fa7b55878f7213cbde449b4092d9
diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
index 3fdd7fe76..8751c70ad 100644
--- a/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
+++ b/2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html
@@ -13,16 +13,13 @@
+
+
+
diff --git a/2-ui/3-event-details/6-pointer-events/ball.view/index.html b/2-ui/3-event-details/6-pointer-events/ball.view/index.html
new file mode 100644
index 000000000..8bbef8f63
--- /dev/null
+++ b/2-ui/3-event-details/6-pointer-events/ball.view/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/style.css b/2-ui/3-event-details/6-pointer-events/slider.view/style.css
new file mode 100644
index 000000000..a84cd5e7e
--- /dev/null
+++ b/2-ui/3-event-details/6-pointer-events/slider.view/style.css
@@ -0,0 +1,20 @@
+.slider {
+ border-radius: 5px;
+ background: #E0E0E0;
+ background: linear-gradient(left top, #E0E0E0, #EEEEEE);
+ width: 310px;
+ height: 15px;
+ margin: 5px;
+}
+
+.thumb {
+ touch-action: none;
+ width: 10px;
+ height: 25px;
+ border-radius: 3px;
+ position: relative;
+ left: 10px;
+ top: -5px;
+ background: blue;
+ cursor: pointer;
+}
diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md
similarity index 100%
rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md
rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md
diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html
similarity index 100%
rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html
rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html
diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md
similarity index 100%
rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md
rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md
diff --git a/2-ui/3-event-details/5-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md
similarity index 81%
rename from 2-ui/3-event-details/5-keyboard-events/article.md
rename to 2-ui/3-event-details/7-keyboard-events/article.md
index 617852ccf..12fe63201 100644
--- a/2-ui/3-event-details/5-keyboard-events/article.md
+++ b/2-ui/3-event-details/7-keyboard-events/article.md
@@ -107,7 +107,7 @@ So, `event.code` may match a wrong character for unexpected layout. Same letters
To reliably track layout-dependent characters, `event.key` may be a better way.
-On the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location, even if the visitor changes languages. So hotkeys that rely on it work well even in case of a language switch.
+On the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location. So hotkeys that rely on it work well even in case of a language switch.
Do we want to handle layout-dependant keys? Then `event.key` is the way to go.
@@ -139,22 +139,25 @@ For instance, the `` below expects a phone number, so it does not accept
```html autorun height=60 run
```
-Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`.
+The `onkeydown` handler here uses `checkPhoneKey` to check for the key pressed. If it's valid (from `0..9` or one of `+-()`), then it returns `true`, otherwise `false`.
-Let's relax it a little bit:
+As we know, the `false` value returned from the event handler, assigned using a DOM property or an attribute, such as above, prevents the default action, so nothing appears in the `` for keys that don't pass the test. (The `true` value returned doesn't affect anything, only returning `false` matters)
+Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, do not work in the input. That's a side effect of the strict filter `checkPhoneKey`. These keys make it return `false`.
+
+Let's relax the filter a little bit by allowing arrow keys `key:Left`, `key:Right` and `key:Delete`, `key:Backspace`:
```html autorun height=60 run
@@ -162,7 +165,9 @@ function checkPhoneKey(key) {
Now arrows and deletion works well.
-...But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the `input` event -- it triggers after any modification. There we can check the new value and highlight/modify it when it's invalid.
+Even though we have the key filter, one still can enter anything using a mouse and right-click + Paste. Mobile devices provide other means to enter values. So the filter is not 100% reliable.
+
+The alternative approach would be to track the `oninput` event -- it triggers *after* any modification. There we can check the new `input.value` and modify it/highlight the `` when it's invalid. Or we can use both event handlers together.
## Legacy
@@ -170,6 +175,12 @@ In the past, there was a `keypress` event, and also `keyCode`, `charCode`, `whic
There were so many browser incompatibilities while working with them, that developers of the specification had no way, other than deprecating all of them and creating new, modern events (described above in this chapter). The old code still works, as browsers keep supporting them, but there's totally no need to use those any more.
+## Mobile Keyboards
+
+When using virtual/mobile keyboards, formally known as IME (Input-Method Editor), the W3C standard states that a KeyboardEvent's [`e.keyCode` should be `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) and [`e.key` should be `"Unidentified"`](https://www.w3.org/TR/uievents-key/#key-attr-values).
+
+While some of these keyboards might still use the right values for `e.key`, `e.code`, `e.keyCode`... when pressing certain keys such as arrows or backspace, there's no guarantee, so your keyboard logic might not always work on mobile devices.
+
## Summary
Pressing a key always generates a keyboard event, be it symbol keys or special keys like `key:Shift` or `key:Ctrl` and so on. The only exception is `key:Fn` key that sometimes presents on a laptop keyboard. There's no keyboard event for it, because it's often implemented on lower level than OS.
diff --git a/2-ui/3-event-details/7-keyboard-events/german-layout.svg b/2-ui/3-event-details/7-keyboard-events/german-layout.svg
new file mode 100644
index 000000000..7ac9a4008
--- /dev/null
+++ b/2-ui/3-event-details/7-keyboard-events/german-layout.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html
similarity index 95%
rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html
rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html
index 401062830..a0d5a4f40 100644
--- a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html
+++ b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html
@@ -28,7 +28,7 @@
-
+
diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js
similarity index 96%
rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js
rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js
index 5eba24c7a..d97f7a7b5 100644
--- a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js
+++ b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js
@@ -5,6 +5,8 @@ let lastTime = Date.now();
function handle(e) {
if (form.elements[e.type + 'Ignore'].checked) return;
+ area.scrollTop = 1e6;
+
let text = e.type +
' key=' + e.key +
' code=' + e.code +
diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css
similarity index 100%
rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css
rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css
diff --git a/2-ui/3-event-details/7-keyboard-events/us-layout.svg b/2-ui/3-event-details/7-keyboard-events/us-layout.svg
new file mode 100644
index 000000000..353f225f1
--- /dev/null
+++ b/2-ui/3-event-details/7-keyboard-events/us-layout.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md
index 10945ccd7..54c101193 100644
--- a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md
+++ b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md
@@ -55,11 +55,11 @@ function populate() {
// document bottom
let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;
- // if the user scrolled far enough (<100px to the end)
- if (windowRelativeBottom < document.documentElement.clientHeight + 100) {
- // let's add more data
- document.body.insertAdjacentHTML("beforeend", `
Date: ${new Date()}
`);
- }
+ // if the user hasn't scrolled far enough (>100px to the end)
+ if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;
+
+ // let's add more data
+ document.body.insertAdjacentHTML("beforeend", `
Date: ${new Date()}
`);
}
}
```
diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md
index 7b5cf4848..734bd84c6 100644
--- a/2-ui/3-event-details/8-onscroll/article.md
+++ b/2-ui/3-event-details/8-onscroll/article.md
@@ -1,6 +1,6 @@
# Scrolling
-The `scroll` event allows to react on a page or element scrolling. There are quite a few good things we can do here.
+The `scroll` event allows reacting to a page or element scrolling. There are quite a few good things we can do here.
For instance:
- Show/hide additional controls or information depending on where in the document the user is.
@@ -34,4 +34,4 @@ If we add an event handler to these events and `event.preventDefault()` in it, t
There are many ways to initiate a scroll, so it's more reliable to use CSS, `overflow` property.
-Here are few tasks that you can solve or look through to see the applications on `onscroll`.
+Here are few tasks that you can solve or look through to see applications of `onscroll`.
diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md
index 01af1f400..7bc87a0f0 100644
--- a/2-ui/4-forms-controls/1-form-elements/article.md
+++ b/2-ui/4-forms-controls/1-form-elements/article.md
@@ -8,11 +8,11 @@ Working with forms will be much more convenient when we learn them.
Document forms are members of the special collection `document.forms`.
-That's a so-called "named collection": it's both named and ordered. We can use both the name or the number in the document to get the form.
+That's a so-called *"named collection"*: it's both named and ordered. We can use both the name or the number in the document to get the form.
```js no-beautify
-document.forms.my - the form with name="my"
-document.forms[0] - the first form in the document
+document.forms.my; // the form with name="my"
+document.forms[0]; // the first form in the document
```
When we have a form, then any element is available in the named collection `form.elements`.
@@ -36,9 +36,9 @@ For instance:
```
-There may be multiple elements with the same name, that's often the case with radio buttons.
+There may be multiple elements with the same name. This is typical with radio buttons and checkboxes.
-In that case `form.elements[name]` is a collection, for instance:
+In that case, `form.elements[name]` is a *collection*. For instance:
```html run height=40