diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 26fcf0834..b8e6006a6 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,7 +1,11 @@ # ജാവാസ്ക്രിപ്റ്റ് ഒരു ഇൻട്രൊഡകഷൻ, നമുക്ക് നോക്കാം ജാവാസ്ക്രിപ്റ്റ് എന്താണെന്നും അതിന്റെ പ്രെത്യേകതയെന്നുo,നമുക്ക് അതുകൊണ്ടു എന്തൊക്കെ നേടാം, പിന്നെ ഏതൊക്കെ മറ്റു ടെക്നോളജിസ് അതിന്റെ കൂടെ വർക് ചെയ്യുമെന്ന്. +<<<<<<< HEAD ## എന്താണ് ജാവാസ്ക്രിപ്റ്റ് +======= +Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a *ജാവാസ്ക്രിപ്റ്റ്* ആദ്യമായി ഉപയോഗിച്ചു തുടങ്ങിയത് വെബ്‌പേജുകൾക്ക് ജീവൻ കൊടുക്കാനാണ്. @@ -19,7 +23,13 @@ Javascript ആദ്യമായി പുറത്തിറക്കിയപ ഇന്ന്, javascript ബ്രൗസർകളിൽ മാത്രമല്ല സെർവർകളിൽ വരെ ഉപയോഗിക്കാൻ പറ്റും, ചുരുക്കിപ്പറഞ്ഞാൽ[the JavaScript engine](https://en.wikipedia.org/wiki/JavaScript_engine) ഉള്ള ഏതൊരു ഡിവൈസിലും അതു ഉപയോഗിക്കാൻ പറ്റും. +<<<<<<< HEAD ബ്രൗസറുകൾക് സ്വന്തമായി ഒരു ജാവസ്ക്രിപ്റ് എൻജിൻ ഉണ്ടായിരിക്കും "JavaScript virtual machine". +======= +- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. +- ...There are other codenames like "Chakra" for IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari, etc. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a പല എഞ്ചിനുകൾക്കും പല "കോഡുനെയിം" ആയിരിക്കും. ഉദാഹരണത്തിന്: @@ -107,7 +117,16 @@ Engines നല്ല ബുദ്ധിമുട്ടുള്ള ഒരു വ - [ഫ്ലോ] (http://flow.org/) ഡാറ്റ ടൈപ്പിംഗും ചേർക്കുന്നു, പക്ഷേ മറ്റൊരു രീതിയിൽ. ഫേസ്ബുക്ക് വികസിപ്പിച്ചെടുത്തത്. - [ഡാർട്ട്] (https://www.dartlang.org/) ബ്രൗസർ ഇതര പരിതസ്ഥിതികളിൽ (മൊബൈൽ അപ്ലിക്കേഷനുകൾ പോലെ) പ്രവർത്തിക്കുന്ന സ്വന്തം എഞ്ചിൻ ഉള്ള ഒരു ഒറ്റപ്പെട്ട ഭാഷയാണ്, മാത്രമല്ല ഇത് ജാവാസ്ക്രിപ്റ്റിലേക്ക് കൈമാറാനും കഴിയും. Google വികസിപ്പിച്ചെടുത്തത്. +<<<<<<< HEAD കൂടുതൽ ഉണ്ട്. തീർച്ചയായും, ഞങ്ങൾ‌ ട്രാൻ‌സ്‌പൈൽ‌ ചെയ്‌ത ഭാഷകളിലൊന്ന്‌ ഉപയോഗിച്ചാലും, ഞങ്ങൾ‌ ചെയ്യുന്നതെന്താണെന്ന് ശരിക്കും മനസിലാക്കാൻ‌ ഞങ്ങൾ‌ JavaScript നെ അറിഞ്ഞിരിക്കണം. +======= +- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it. +- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft. +- [Flow](http://flow.org/) also adds data typing, but in a different way. Developed by Facebook. +- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google. +- [Brython](https://brython.info/) is a Python transpiler to JavaScript that enables the writing of applications in pure Python without JavaScript. +- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) is a modern, concise and safe programming language that can target the browser or Node. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a ## സംഗ്രഹം @@ -115,3 +134,9 @@ Engines നല്ല ബുദ്ധിമുട്ടുള്ള ഒരു വ - ഇന്ന്, HTML / CSS മായി പൂർണ്ണമായി സംയോജിപ്പിച്ച് ഏറ്റവും വ്യാപകമായി സ്വീകരിച്ച ബ്രൌസർ ഭാഷയായി ജാവാസ്ക്രിപ്റ്റിന് സവിശേഷമായ ഒരു സ്ഥാനമുണ്ട്. - ജാവാസ്ക്രിപ്റ്റിലേക്ക് "കൈമാറ്റം ചെയ്യപ്പെടുകയും" ചില സവിശേഷതകൾ നൽകുകയും ചെയ്യുന്ന നിരവധി ഭാഷകളുണ്ട്. ജാവാസ്ക്രിപ്റ്റ് മാസ്റ്റേഴ്സ് ചെയ്തതിനുശേഷം, ചുരുക്കത്തിൽ, അവ പരിശോധിക്കുന്നത് നല്ലതായിരിക്കും. +<<<<<<< HEAD +======= +- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well. +- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration in HTML/CSS. +- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md index e2aec0392..166a17872 100644 --- a/1-js/01-getting-started/2-manuals-specifications/article.md +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -11,19 +11,31 @@ പക്ഷെ വളരെ ഫോർമലായത് കൊണ്ടു, തുടക്കമിത് മനസ്സിലാക്കാൻ നല്ല ബുദ്ധിമുട്ടായിരിക്കും. അതിനാൽ, വിശദാംശങ്ങളെക്കുറിച്ചുള്ള ഏറ്റവും വിശ്വാസ്യതയുള്ള ഡാറ്റ സ്രോതസ്സ് നിങ്ങൾക്ക് ആവശ്യമുണ്ടെങ്കിൽ, ഇതൊരു ശരിയായ സ്ഥലമാണ്. എന്നാൽ ഇത് സാധാരണ ഉപയോഗത്തിന് വേണ്ടിയല്ല. +<<<<<<< HEAD ഓരോ വർഷവും പുതിയൊരു വേർഷൻ അവർ പുറത്തിറക്കും. ഏറ്റവും പുതിയ വേർഷൻ റിലീസിന്റെ കൂടെ ൽ ലഭിക്കുന്നതാണ്. +======= +Also, if you're in developing for the browser, then there are other specifications covered in the [second part](info:browser-environment) of the tutorial. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a പുതിയ സ്റ്റാൻഡേർഡ് ആയിട്ടുള്ള ഫീച്ചേഴ്‌സ് വായിക്കാൻ("stage 3"), ഇവിടെ ഞെക്കിയാൽ മതിയാകും . +<<<<<<< HEAD കൂടാതെ,നിങ്ങളൊരു ബ്രൌസർ ഡെവലപ്‌ ചെയ്യുകയാണെങ്കിൽ, കുറച്ചു കൂടി ഡീറ്റൈലായിട്ടൊരു [രണ്ടാം ഭാഗം](info:browser-environment) കിട്ടുന്നതാണ്. +======= +- **MDN (Mozilla) JavaScript Reference** is the main manual with examples and other information. It's great to get in-depth information about individual language functions, methods etc. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a +<<<<<<< HEAD - **MDN (Mozilla) JavaScript Reference** ഇതു ഒരുപാട് ഉദാഹരണങ്ങളും മറ്റു കാര്യങ്ങളും അടങ്ങിയ ഒരു മാനുവലാണ് . ഇത് language functions,methods ഉം അതുപോലെ മറ്റു കാര്യങ്ങളെ കുറിച്ചുo ആഴത്തിൽ മനസ്സിലാക്കാൻ നമ്മളെ സഹായിക്കും. ഒന്നു നോക്കുക . അല്ലങ്കിൽ ഇന്റർനെറ്റിൽ സെർച്ച് ചെയ്താലും മതി. സെർച്ച് ചെയ്യുമ്പോൾ "MDN [term]" അടിക്കാൻ മറക്കരുത്, e.g.`parseInt` സെർച്ച് ചെയ്യുന്നത് . +======= +Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. to search for `parseInt` function. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a - **MSDN** – Microsoft ന്റെ മാന്വലിൽ ജാവസ്ക്രിപ്റ്(j script) ഉൾപ്പടെ ഒരുപാട് ഇൻഫോര്മാഷൻസ് ഉണ്ട്. ഒരാൾക്ക് Internet Explorer സ്പെസിഫിക് ആയിട്ടുള്ള എന്തെങ്കിലും വേണമെങ്കിൽ, അവിടെ പോകുന്നതായിരിക്കും നല്ലത്: . diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md index 6d2d54d0e..dc27c31e7 100644 --- a/1-js/01-getting-started/4-devtools/article.md +++ b/1-js/01-getting-started/4-devtools/article.md @@ -8,7 +8,11 @@ മിക്ക ഡവലപ്പർമാരും ഡെവലപ്‌മെന്റിനായി Chrome അല്ലെങ്കിൽ Firefox ആയിരിക്കുo ആശ്രയിക്കുന്നത്, കാരണം ആ ബ്രൗസറുകളിൽ മികച്ച ഡവലപ്പർ ടൂൾസ് ഉൾപ്പെടുത്തിയിട്ടുണ്ട്. മറ്റ് ബ്രവ്സറുകളും ഡെവലപ്പർ ടൂൾസ് നൽകുന്നു, ചിലപ്പോൾ ചില പ്രത്യേകതകളും അവ നൽകും, പക്ഷേ സാധാരണയായി Chrome അല്ലെങ്കിൽ Firefox ലോട്ടു അനുകരിക്കുകയാണ് അവ ചെയ്യുന്നത്. അതിനാൽ മിക്ക ഡവലപ്പർമാർക്കും അവർക്ക് "ഇഷ്ടപ്പെട്ട" ഒരു ബ്രൗസർ കാണും കൂടാതെ ഒരു ബ്രൗസറിൽ എന്തെങ്കിലും പ്രശ്നമുള്ളതായി തോന്നുകയാണെങ്കിൽ മറ്റുള്ളവയിലേക്കു മാറുകയോ ചെയ്യും. +<<<<<<< HEAD ഡവലപ്പർ ടൂൾസ് വളരെ ശക്തമാണ്; അവയ്‌ക്ക് നിരവധി സവിശേഷതകളുണ്ട്. നമുക്കിപ്പോൾ, അവ എങ്ങനെ ഓപ്പൺ ചെയ്യാമെന്നും തെറ്റുകൾ എങ്ങനെ നോക്കാമെന്നും javascript command കൾ എങ്ങനെ ടെസ്റ്റ് ചെയ്തു നോക്കാമെന്നും പഠിക്കാം. +======= +Developer tools are potent, they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands. +>>>>>>> 97ef86242f9f236b13152e1baf52a55c4db8728a ## Google Chrome diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index b3149f112..fa935f341 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -9,7 +9,7 @@ So first, let's see how we attach a script to a webpage. For server-side environ ## The "script" tag -JavaScript programs can be inserted into any part of an HTML document with the help of the ` diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md index 4bd41a168..497205f42 100644 --- a/1-js/13-modules/02-import-export/article.md +++ b/1-js/13-modules/02-import-export/article.md @@ -321,7 +321,7 @@ export {default as User} from './user.js'; // re-export default Why would that be needed? Let's see a practical use case. -Imagine, we're writing a "package": a folder with a lot of modules, with some of the functionality exported outside (tools like NPM allow us to publish and distribute such packages), and many modules are just "helpers", for internal use in other package modules. +Imagine, we're writing a "package": a folder with a lot of modules, with some of the functionality exported outside (tools like NPM allow us to publish and distribute such packages, but we don't have to use them), and many modules are just "helpers", for internal use in other package modules. The file structure could be like this: ``` @@ -378,7 +378,7 @@ export {default as User} from './user.js'; The default export needs separate handling when re-exporting. -Let's say we have `user.js`, and we'd like to re-export class `User` from it: +Let's say we have `user.js` with the `export default class User` and would like to re-export it: ```js // 📁 user.js @@ -387,7 +387,9 @@ export default class User { } ``` -1. `export User from './user.js'` won't work. What can go wrong?... But that's a syntax error! +We can come across two problems with it: + +1. `export User from './user.js'` won't work. That would lead to a syntax error. To re-export the default export, we have to write `export {default as User}`, as in the example above. @@ -399,7 +401,7 @@ export default class User { export {default} from './user.js'; // to re-export the default export ``` -Such oddities of re-exporting the default export are one of the reasons why some developers don't like them. +Such oddities of re-exporting a default export are one of the reasons why some developers don't like default exports and prefer named ones. ## Summary diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md index 357a57313..9db69cb2f 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md @@ -19,5 +19,5 @@ function wrap(target) { user = wrap(user); alert(user.name); // John -alert(user.age); // ReferenceError: Property doesn't exist "age" +alert(user.age); // ReferenceError: Property doesn't exist: "age" ``` diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md index d7093c0c3..47985e1a7 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md @@ -27,6 +27,6 @@ user = wrap(user); alert(user.name); // John *!* -alert(user.age); // ReferenceError: Property doesn't exist "age" +alert(user.age); // ReferenceError: Property doesn't exist: "age" */!* ``` diff --git a/1-js/99-js-misc/01-proxy/article.md b/1-js/99-js-misc/01-proxy/article.md index 3f7ef63b8..1f84912e5 100644 --- a/1-js/99-js-misc/01-proxy/article.md +++ b/1-js/99-js-misc/01-proxy/article.md @@ -39,7 +39,7 @@ As there are no traps, all operations on `proxy` are forwarded to `target`. As we can see, without any traps, `proxy` is a transparent wrapper around `target`. -![](proxy.svg) +![](proxy.svg) `Proxy` is a special "exotic object". It doesn't have own properties. With an empty `handler` it transparently forwards operations to `target`. @@ -61,13 +61,13 @@ For every internal method, there's a trap in this table: the name of the method | `[[Delete]]` | `deleteProperty` | `delete` operator | | `[[Call]]` | `apply` | function call | | `[[Construct]]` | `construct` | `new` operator | -| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) | -| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) | -| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) | -| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) | -| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) | -| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | -| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object/keys/values/entries` | +| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) | +| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) | +| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) | +| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) | +| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) | +| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | +| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` | ```warn header="Invariants" JavaScript enforces some invariants -- conditions that must be fulfilled by internal methods and traps. @@ -335,7 +335,7 @@ let user = { _password: "secret" }; -alert(user._password); // secret +alert(user._password); // secret ``` Let's use proxies to prevent any access to properties starting with `_`. @@ -376,7 +376,7 @@ user = new Proxy(user, { }, *!* deleteProperty(target, prop) { // to intercept property deletion -*/!* +*/!* if (prop.startsWith('_')) { throw new Error("Access denied"); } else { @@ -437,7 +437,7 @@ user = { ``` -A call to `user.checkPassword()` call gets proxied `user` as `this` (the object before dot becomes `this`), so when it tries to access `this._password`, the `get` trap activates (it triggers on any property read) and throws an error. +A call to `user.checkPassword()` gets proxied `user` as `this` (the object before dot becomes `this`), so when it tries to access `this._password`, the `get` trap activates (it triggers on any property read) and throws an error. So we bind the context of object methods to the original object, `target`, in the line `(*)`. Then their future calls will use `target` as `this`, without any traps. @@ -963,9 +963,13 @@ revoke(); alert(proxy.data); // Error ``` -A call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. The target object can be garbage-collected after that. +A call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. + +Initially, `revoke` is separate from `proxy`, so that we can pass `proxy` around while leaving `revoke` in the current scope. -We can also store `revoke` in a `WeakMap`, to be able to easily find it by a proxy object: +We can also bind `revoke` method to proxy by setting `proxy.revoke = revoke`. + +Another option is to create a `WeakMap` that has `proxy` as the key and the corresponding `revoke` as the value, that allows to easily find `revoke` for a proxy: ```js run *!* @@ -980,21 +984,19 @@ let {proxy, revoke} = Proxy.revocable(object, {}); revokes.set(proxy, revoke); -// ..later in our code.. +// ..somewhere else in our code.. revoke = revokes.get(proxy); revoke(); alert(proxy.data); // Error (revoked) ``` -The benefit of such an approach is that we don't have to carry `revoke` around. We can get it from the map by `proxy` when needed. - We use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes "unreachable" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more. ## References - Specification: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). -- MDN: [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). +- MDN: [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). ## Summary @@ -1016,13 +1018,13 @@ We can trap: - Reading (`get`), writing (`set`), deleting (`deleteProperty`) a property (even a non-existing one). - Calling a function (`apply` trap). - The `new` operator (`construct` trap). -- Many other operations (the full list is at the beginning of the article and in the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)). +- Many other operations (the full list is at the beginning of the article and in the [docs](mdn:/JavaScript/Reference/Global_Objects/Proxy)). That allows us to create "virtual" properties and methods, implement default values, observable objects, function decorators and so much more. We can also wrap an object multiple times in different proxies, decorating it with various aspects of functionality. -The [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects. +The [Reflect](mdn:/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects. Proxies have some limitations: diff --git a/1-js/99-js-misc/03-currying-partials/article.md b/1-js/99-js-misc/03-currying-partials/article.md index bb308847c..d71ac23f8 100644 --- a/1-js/99-js-misc/03-currying-partials/article.md +++ b/1-js/99-js-misc/03-currying-partials/article.md @@ -155,7 +155,7 @@ function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); } else { - return function pass(...args2) { // (2) + return function(...args2) { // (2) return curried.apply(this, args.concat(args2)); } } @@ -164,18 +164,10 @@ function curried(...args) { When we run it, there are two `if` execution branches: -1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it. -2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result. +1. If passed `args` count is the same or more than the original function has in its definition (`func.length`) , then just pass the call to it using `func.apply`. +2. Otherwise, get a partial: we don't call `func` just yet. Instead, another wrapper is returned, that will re-apply `curried` providing previous arguments together with the new ones. -For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`. - -For the call `curried(1)(2)(3)`: - -1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`. -2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together. As the argument count is still less than 3, `curry` returns `pass`. -3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function. - -If that's still not obvious, just trace the calls sequence in your mind or on paper. +Then, if we call it, again, we'll get either a new partial (if not enough arguments) or, finally, the result. ```smart header="Fixed-length functions only" The currying requires the function to have a fixed number of arguments. diff --git a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md index 31ea4ff88..e4ee78748 100644 --- a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md +++ b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md @@ -5,7 +5,7 @@ Here's the explanations. 2. The same, parentheses do not change the order of operations here, the dot is first anyway. -3. Here we have a more complex call `(expression).method()`. The call works as if it were split into two lines: +3. Here we have a more complex call `(expression)()`. The call works as if it were split into two lines: ```js no-beautify f = obj.go; // calculate the expression @@ -14,7 +14,7 @@ Here's the explanations. Here `f()` is executed as a function, without `this`. -4. The similar thing as `(3)`, to the left of the dot `.` we have an expression. +4. The similar thing as `(3)`, to the left of the parentheses `()` we have an expression. To explain the behavior of `(3)` and `(4)` we need to recall that property accessors (dot or square brackets) return a value of the Reference Type. diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md index c680c17f9..227253436 100644 --- a/1-js/99-js-misc/04-reference-type/article.md +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -93,7 +93,7 @@ Reference type is a special "intermediary" internal type, with the purpose to pa Any other operation like assignment `hi = user.hi` discards the reference type as a whole, takes the value of `user.hi` (a function) and passes it on. So any further operation "loses" `this`. -So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). Later in this tutorial, we will learn various ways to solve this problem such as [func.bind()](/bind#solution-2-bind). +So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). There are various ways to solve this problem such as [func.bind()](/bind#solution-2-bind). ## Summary diff --git a/1-js/99-js-misc/05-bigint/article.md b/1-js/99-js-misc/05-bigint/article.md index 062dd6017..2a1cfc843 100644 --- a/1-js/99-js-misc/05-bigint/article.md +++ b/1-js/99-js-misc/05-bigint/article.md @@ -126,5 +126,5 @@ We can use such JSBI code "as is" for engines that don't support bigints and for ## References -- [MDN docs on BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). +- [MDN docs on BigInt](mdn:/JavaScript/Reference/Global_Objects/BigInt). - [Specification](https://tc39.es/ecma262/#sec-bigint-objects). diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 019398be9..f5afca5e5 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. 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..5af6435ce 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -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] @@ -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. @@ -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..fc3bf6525 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 @@ -198,7 +198,7 @@ In XML mode the case is kept "as is". Nowadays XML mode is rarely used. ## innerHTML: the contents -The [innerHTML](https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML) property allows to get the HTML inside the element as a string. +The [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string. We can also modify it. So it's one of the most powerful ways to change the page. @@ -397,7 +397,7 @@ Compare the two:
``` -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. @@ -283,9 +282,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 b5535bea5..4f3be1933 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 @@ -52,21 +52,21 @@ Click-related events always have the `button` property, which allows to get the 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. -From the other hand, `mousedown` and `mouseup` handlers we may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". +From 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". The possible values of `event.button` are: | Button state | `event.button` | |--------------|----------------| | Left button (primary) | 0 | -| Middle button (auxillary) | 1 | +| Middle button (auxiliary) | 1 | | Right button (secondary) | 2 | | X1 button (back) | 3 | | X2 button (forward) | 4 | 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. -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](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons) if you ever need it. +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: @@ -156,7 +156,7 @@ Move the mouse over the input field to see `clientX/clientY` (the example is in 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 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`! ![](mouseover-to-child.svg) 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 a47bfade6..6cb1152c1 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 @@ -276,7 +276,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 +300,4 @@ 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. -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. diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md index 9d92144bc..3e751a4af 100644 --- a/2-ui/3-event-details/6-pointer-events/article.md +++ b/2-ui/3-event-details/6-pointer-events/article.md @@ -8,7 +8,9 @@ Let's make a small overview, so that you understand the general picture and the - Long ago, in the past, there were only mouse events. - Then touch devices appeared. For the old code to work, they also generate mouse events. For instance, tapping generates `mousedown`. But mouse events were not good enough, as touch devices are more powerful in many aspects. For example, it's possible to touch multiple points at once, and mouse events don't have any properties for that. + Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages. + + But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once ("multi-touch"). Although, mouse events don't have necessary properties to handle such multi-touches. - So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better). @@ -16,15 +18,19 @@ Let's make a small overview, so that you understand the general picture and the - To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices. -As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works. Unless you code for Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. +As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compartible with Pointer Events level 2. + +Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. -That being said, they have some important peculiarities that one should know in order to use them correctly and avoid surprises. We'll make note of them in this article. +Then your code will work well with both touch and mouse devices. + +That said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. We'll make note of them in this article. ## Pointer event types Pointer events are named similarly to mouse events: -| Pointer Event | Mouse event | +| Pointer event | Similar mouse event | |---------------|-------------| | `pointerdown` | `mousedown` | | `pointerup` | `mouseup` | @@ -42,7 +48,7 @@ As we can see, for every `mouse`, there's a `pointer` that plays a ```smart header="Replacing `mouse` with `pointer` in our code" We can replace `mouse` events with `pointer` in our code and expect things to continue working fine with mouse. -The support for touch devices will also "magically" improve, but we'll probably need to add `touch-action: none` in CSS. See the details below in the section about `pointercancel`. +The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. ``` ## Pointer event properties @@ -51,21 +57,21 @@ Pointer events have the same properties as mouse events, such as `clientX/Y`, `t - `pointerId` - the unique identifier of the pointer causing the event. - Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (explained below). + Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow). - `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". We can use this property to react differently on various pointer types. -- `isPrimary` - `true` for the primary pointer (the first finger in multi-touch). +- `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch). -For pointers that measure contact area and pressure, e.g. a finger on the touchscreen, the additional properties can be useful: +Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that: -- `width` - the width of the area where the pointer touches the device. Where unsupported, e.g. for a mouse, it's always `1`. +- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. - `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`. - `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`. - `tangentialPressure` - the normalized tangential pressure. - `tiltX`, `tiltY`, `twist` - pen-specific properties that describe how the pen is positioned relative the surface. -These properties aren't very well supported across devices, so they are rarely used. You can find the details in the [specification](https://w3c.github.io/pointerevents/#pointerevent-interface) if needed. +These properties aren't supported by most devices, so they are rarely used. You can find the details about them in the [specification](https://w3c.github.io/pointerevents/#pointerevent-interface) if needed. ## Multi-touch @@ -73,11 +79,11 @@ One of the things that mouse events totally don't support is multi-touch: a user Pointer Events allow handling multi-touch with the help of the `pointerId` and `isPrimary` properties. -Here's what happens when a user touches a screen in one place, then puts another finger somewhere else on it: +Here's what happens when a user touches a touchscreen in one place, then puts another finger somewhere else on it: -1. At the first touch: +1. At the first finger touch: - `pointerdown` with `isPrimary=true` and some `pointerId`. -2. For the second finger and further touches: +2. For the second finger and more fingers (assuming the first one is still touching): - `pointerdown` with `isPrimary=false` and a different `pointerId` for every finger. Please note: the `pointerId` is assigned not to the whole device, but for each touching finger. If we use 5 fingers to simultaneously touch the screen, we have 5 `pointerdown` events, each with their respective coordinates and a different `pointerId`. @@ -91,17 +97,15 @@ Here's the demo that logs `pointerdown` and `pointerup` events: [iframe src="multitouch" edit height=200] -Please note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference. For single-touch devices, such as a mouse, there'll be always same `pointerId` with `isPrimary=true`, for all pointer events. +Please note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference in `pointerId/isPrimary`. For single-touch devices, such as a mouse, there'll be always same `pointerId` with `isPrimary=true`, for all pointer events. ``` ## Event: pointercancel -We've mentioned the importance of `touch-action: none` before. Now let's explain why, as skipping this may cause our interfaces to malfunction. - The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. Such causes are: -- The pointer device hardware was disabled. +- The pointer device hardware was physically disabled. - The device orientation changed (tablet rotated). - The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else. @@ -111,33 +115,33 @@ Let's say we're impelementing drag'n'drop for a ball, just as in the beginning o Here is the flow of user actions and the corresponding events: -1) The user presses the mouse button on an image, to start dragging +1) The user presses on an image, to start dragging - `pointerdown` event fires -2) Then they start dragging the image +2) Then they start moving the pointer (thus dragging the image) - `pointermove` fires, maybe several times -3) Surprise! The browser has native drag'n'drop support for images, that kicks in and takes over the drag'n'drop process, thus generating `pointercancel` event. +3) And then the surprise happens! The browser has native drag'n'drop support for images, that kicks in and takes over the drag'n'drop process, thus generating `pointercancel` event. - The browser now handles drag'n'drop of the image on its own. The user may even drag the ball image out of the browser, into their Mail program or a File Manager. - No more `pointermove` events for us. -So the issue is that the browser "hijacks" the interaction: `pointercancel` fires and no more `pointermove` events are generated. +So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated. ```online -Here's the demo with pointer events (only `up/down`, `move` and `cancel`) logged in the textarea: +Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: [iframe src="ball" height=240 edit] ``` -We'd like to implement our own drag'n'drop, so let's tell the browser not to take it over. +We'd like to implement the drag'n'drop on our own, so let's tell the browser not to take it over. -**Prevent default browser actions to avoid `pointercancel`.** +**Prevent the default browser action to avoid `pointercancel`.** We need to do two things: 1. Prevent native drag'n'drop from happening: - We can do this by setting `ball.ondragstart = () => false`, just as described in the article . - That works well for mouse events. -2. For touch devices, there are also touch-related browser actions. We'll have problems with them too. - - We can prevent them by setting `#ball { touch-action: none }` in CSS. +2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too: + - Prevent them by setting `#ball { touch-action: none }` in CSS. - Then our code will start working on touch devices. After we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`. @@ -156,41 +160,52 @@ Now we can add the code to actually move the ball, and our drag'n'drop will work Pointer capturing is a special feature of pointer events. -The idea is that we can "bind" all events with a particular `pointerId` to a given element. Then all subsequent events with the same `pointerId` will be retargeted to the same element. That is: the browser sets that element as the target and trigger associated handlers, no matter where it actually happened. +The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type. -The related methods are: -- `elem.setPointerCapture(pointerId)` - binds the given `pointerId` to `elem`. -- `elem.releasePointerCapture(pointerId)` - unbinds the given `pointerId` from `elem`. +The main method is: +- `elem.setPointerCapture(pointerId)` - binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. -Such binding doesn't hold long. It's automatically removed after `pointerup` or `pointercancel` events, or when the target `elem` is removed from the document. +In other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`. -Now when do we need this? +The binding is removed: +- automatically when `pointerup` or `pointercancel` events occur, +- automatically when `elem` is removed from the document, +- when `elem.releasePointerCapture(pointerId)` is called. -**Pointer capturing is used to simplify drag'n'drop kind of interactions.** +**Pointer capturing can be used to simplify drag'n'drop kind of interactions.** -Let's recall the problem we met while making a custom slider in the article . +As an example, let's recall how one can implement a custom slider, described in the . -1) First, the user presses `pointerdown` on the slider thumb to start dragging it. -2) ...But then, as they move the pointer, it may leave the slider: go below or over it. +We make a slider element with the strip and the "runner" (`thumb`) inside it. -But we continue tracking track `pointermove` events and move the thumb until `pointerup`, even though the pointer is not on the slider any more. +Then it works like this: -[Previously](info:mouse-drag-and-drop), to handle `pointermove` events that happen outside of the slider, we listened for `pointermove` events on the whole `document`. +1. The user presses on the slider `thumb` - `pointerdown` triggers. +2. Then they move the pointer - `pointermove` triggers, and we move the `thumb` along. + - ...As the pointer moves, it may leave the slider `thumb`: go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer. -Pointer capturing provides an alternative solution: we can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler, and then all future pointer events until `pointerup` will be retargeted to `thumb`. +So, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `pointermove` event handler on the whole `document`. -That is: events handlers on `thumb` will be called, and `event.target` will always be `thumb`, even if the user moves their pointer around the whole document. So we can listen at `thumb` for `pointermove`, no matter where it happens. +That solution looks a bit "dirty". One of the problems is that pointer movements around the document may cause side effects, trigger other event handlers, totally not related to the slider. + +Pointer capturing provides a means to bind `pointermove` to `thumb` and avoid any such problems: + +- We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler, +- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. +- When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it. + +So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Besides, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. Here's the essential code: ```js thumb.onpointerdown = function(event) { - // retarget all pointer events (until pointerup) to me + // retarget all pointer events (until pointerup) to thumb thumb.setPointerCapture(event.pointerId); }; thumb.onpointermove = function(event) { - // move the slider: listen at thumb, as all events are retargeted to it + // moving the slider: listen on the thumb, as all pointer events are retargeted to it let newLeft = event.clientX - slider.getBoundingClientRect().left; thumb.style.left = newLeft + 'px'; }; @@ -205,7 +220,11 @@ The full demo: [iframe src="slider" height=100 edit] ``` -**As a summary: the code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. That's what pointer capturing does.** +At the end, pointer capturing gives us two benefits: +1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically. +2. If there are any `pointermove` handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. + +### Pointer capturing events There are two associated pointer events: @@ -214,16 +233,16 @@ There are two associated pointer events: ## Summary -Pointer events allow handling mouse, touch and pen events simultaneously. +Pointer events allow handling mouse, touch and pen events simultaneously, with a single piece of code. Pointer events extend mouse events. We can replace `mouse` with `pointer` in event names and expect our code to continue working for mouse, with better support for other device types. -Remember to set `touch-events: none` in CSS for elements that we engage, otherwise the browser will hijack many types of touch interactions, and pointer events won't be generated. +For drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-events: none` in CSS for elements that we engage. -Additional abilities of Pointer events are: +Additional abilities of pointer events are: - Multi-touch support using `pointerId` and `isPrimary`. - Device-specific properties, such as `pressure`, `width/height`, and others. - Pointer capturing: we can retarget all pointer events to a specific element until `pointerup`/`pointercancel`. -As of now, pointer events are supported in all major browsers, so we can safely switch to them, as long as IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events. +As of now, pointer events are supported in all major browsers, so we can safely switch to them, especially if IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events. diff --git a/2-ui/3-event-details/7-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md index 617852ccf..54bde42b4 100644 --- a/2-ui/3-event-details/7-keyboard-events/article.md +++ b/2-ui/3-event-details/7-keyboard-events/article.md @@ -170,6 +170,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/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..6c7e18aca 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -177,18 +177,16 @@ It stores only the HTML that was initially on the page, not the current value. A ``: -1. Find the corresponding `