diff --git a/articles/snippet-template.md b/articles/snippet-template.md new file mode 100644 index 000000000..dbfaa896e --- /dev/null +++ b/articles/snippet-template.md @@ -0,0 +1,12 @@ +--- +title: My amazing story +shortTitle: Amazing story +type: story +tags: [javascript,webdev] +author: chalarangelo +cover: image +excerpt: A short summary of your story up to 180 characters long. +dateModified: 2021-06-13T05:00:00-04:00 +--- + +Write your blog post here. diff --git a/articles/snippets/10-vs-code-extensions-for-js-developers.md b/articles/snippets/10-vs-code-extensions-for-js-developers.md new file mode 100644 index 000000000..515021dea --- /dev/null +++ b/articles/snippets/10-vs-code-extensions-for-js-developers.md @@ -0,0 +1,53 @@ +--- +title: 10 must-have VS Code extensions for JavaScript developers +shortTitle: Essential VS Code extensions +type: story +tags: [devtools,vscode] +author: chalarangelo +cover: computer-screens +excerpt: VS Code is steadily gaining popularity among developers. Here are 10 essential extensions for JavaScript developers that aim to increase your productivity. +unlisted: true +dateModified: 2021-06-12T19:30:41+03:00 +--- + +Developers will most likely argue for the rest of eternity about the most productive code editor and the best extensions. Here are my personal extension preferences for VS Code as a JavaScript developer: + +### ESLint + +[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) turns the popular JavaScript linter into an extension of VS Code. It automatically reads your linting configuration, identifies problems and even fixes them for you, if you want. + +### GitLens + +[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) is a very powerful collaboration tool for VS Code. It provides many useful tools for git such as blame, code authorship, activity heatmaps, recent changes, file history and even commit search. + +### Debugger for Chrome + +[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) allows you to debug your JavaScript code in Chrome or Chromium. Breakpoints, call stack inspection and stepping inside a function are only some of its features. + +### Bracket Pair Colorizer 2 + +[Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2) makes reading code faster as it makes matching brackets the same color. This extension for VS Code improves upon its predecessor by providing improved performance. + +### Bookmarks + +[Bookmarks](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks) is one of those extensions that will significantly reduce your time jumping between different files, as it allows you to save important positions and navigate back to them easily and quickly. + +### TODO Highlight + +[TODO Highlight](https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight) simplifies tracking leftover tasks by allowing you to list all of your TODO annotations, as well as adding a handy background highlight to them to make them pop out immediately. + +### Live Server + +[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) gives you an easy way to serve web pages from VS Code, making previewing and debugging a lot easier. One of the core features is the live reload support that many developers are used to. + +### REST Client + +[REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) allows you to send HTTP requests and view the responses directly in VS Code. This extension supports a wide range of formats and authorization and should work with most setups. + +### One Dark Pro + +[One Dark Pro](https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme) is one of the most popular VS Code themes and with very good reason. It provides a clean theme with a nice palette that has great contrast and is very comfortable to use on a daily basis. + +### Fira Code + +[Fira Code](https://github.com/tonsky/FiraCode) is not a traditional VS Code extension and might take a couple more steps to set up, but it's a superb programming font with ligatures that will help you scan code faster once you get used to it. diff --git a/articles/snippets/25-css-gradients.md b/articles/snippets/25-css-gradients.md new file mode 100644 index 000000000..0b82eaaf5 --- /dev/null +++ b/articles/snippets/25-css-gradients.md @@ -0,0 +1,302 @@ +--- +title: 25 CSS gradients for your next project +shortTitle: CSS gradients +type: cheatsheet +tags: [css,visual] +author: chalarangelo +cover: colors-mural +excerpt: We hand picked 25 of our favorite CSS gradients from uiGradients for your next design. Get them now! +dateModified: 2021-06-12T19:30:41+03:00 +--- + +[uiGradients](https://uigradients.com/) has an amazing collection of ready-to-use CSS gradients for pretty much anything. I highly recommend checking out the full collection. Meantime, here are our top picks in case you're looking for some color: + + + +
+ +```css +.stripe { + background: linear-gradient(to right, #1fa2ff, #12d8fa, #a6ffcb); +} +``` + +
+ +```css +.flare { + background: linear-gradient(to right, #f12711, #f5af19); +} +``` + +
+ +```css +.vanusa { + background: linear-gradient(to right, #da4453, #89216b); +} +``` + +
+ +```css +.sublime-light { + background: linear-gradient(to right, #fc5c7d, #6a82fb); +} +``` + +
+ +```css +.bighead { + background: linear-gradient(to right, #c94b4b, #4b134f); +} +``` + +
+ +```css +.velvet-sun { + background: linear-gradient(to right, #e1eec3, #f05053); +} +``` + +
+ +```css +.argon { + background: linear-gradient(to right, #03001e, #7303c0, #ec38bc, #fdeff9); +} +``` + +
+ +```css +.celestial { + background: linear-gradient(to right, #c33764, #1d2671); +} +``` + +
+ +```css +.relay { + background: linear-gradient(to right, #3a1c71, #d76d77, #ffaf7b); +} +``` + +
+ +```css +.crystal-clear { + background: linear-gradient(to right, #159957, #155799); +} +``` + +
+ +```css +.ibiza-sunset { + background: linear-gradient(to right, #ee0979, #ff6a00); +} +``` + +
+ +```css +.fresh-turboscent { + background: linear-gradient(to right, #f1f2b5, #135058); +} +``` + +
+ +```css +.cheer-up-emo-kid { + background: linear-gradient(to right, #556270, #ff6b6b); +} +``` + +
+ +```css +.starfall { + background: linear-gradient(to right, #f0c27b, #4b1248); +} +``` + +
+ +```css +.nelson { + background: linear-gradient(to right, #f2709c, #ff9472); +} +``` + +
+ +```css +.forever-lost { + background: linear-gradient(to right, #5d4157, #a8caba); +} +``` + +
+ +```css +.blurry-beach { + background: linear-gradient(to right, #d53369, #cbad6d); +} +``` + +
+ +```css +.influenza { + background: linear-gradient(to right, #c04848, #480048); +} +``` + +
+ +```css +.jshine { + background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59); +} +``` + +
+ +```css +.calm-darya { + background: linear-gradient(to right, #5f2c82, #49a09d); +} +``` + +
+ +```css +.titanium { + background: linear-gradient(to right, #283048, #859398); +} +``` + +
+ +```css +.pinky { + background: linear-gradient(to right, #dd5e89, #f7bb97); +} +``` + +
+ +```css +.purple-paradise { + background: linear-gradient(to right, #1d2b64, #f8cdda); +} +``` + +
+ +```css +.horizon { + background: linear-gradient(to right, #003973, #e5e5be); +} +``` + +
+ +```css +.noon-to-dusk { + background: linear-gradient(to right, #ff6e7f, #bfe9ff); +} +``` diff --git a/articles/snippets/4-javascript-array-methods.md b/articles/snippets/4-javascript-array-methods.md new file mode 100644 index 000000000..e9c86e281 --- /dev/null +++ b/articles/snippets/4-javascript-array-methods.md @@ -0,0 +1,58 @@ +--- +title: 4 JavaScript Array methods you must know +shortTitle: Useful array methods +type: story +tags: [javascript,array,cheatsheet] +author: chalarangelo +cover: arrays +excerpt: JavaScript arrays have a very robust API offering some amazing tools. Learn the 4 must-know JavaScript array methods in this quick guide. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +JavaScript arrays have a very robust API offering a plethora of amazing tools. Here are our top 4 JavaScript array methods every developer should know: + +### Array.prototype.map() + +[`Array.prototype.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) creates a new array by applying the provided transformation to each element of the original array. The result is an array with the same length as the original array and elements transformed based on the provided function. + +```js +const arr = [1, 2, 3]; +const double = x => x * 2; +arr.map(double); // [2, 4, 6] +``` + +### Array.prototype.filter() + +[`Array.prototype.filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) creates a new array by using a filtering function to keep only elements that return `true` based on that function. The result is an array with equal or less than the original array's length, containing a subset of the same elements as the original array. + +```js +const arr = [1, 2, 3]; +const isOdd = x => x % 2 === 1; +arr.filter(isOdd); // [1, 3] +``` + +![JavaScript Array Methods](./illustrations/js-array-methods.png) + +### Array.prototype.reduce() + +[`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) creates an output value of any type depending on a reducer function and an initial value. The result can be of any type such as an integer, an object or an array, based on the reducer function provided. + +```js +const arr = [1, 2, 3]; + +const sum = (x, y) => x + y; +arr.reduce(sum, 0); // 6 + +const increment = (x, y) => [...x, x[x.length - 1] + y]; +arr.reduce(increment, [0]); // [0, 1, 3, 6] +``` + +### Array.prototype.find() + +[`Array.prototype.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) returns the first element for which a matcher function returns `true`. The result is a single element from the original array. + +```js +const arr = [1, 2, 3]; +const isOdd = x => x % 2 === 1; +arr.find(isOdd); // 1 +``` diff --git a/articles/snippets/4-seo-tips-for-developers.md b/articles/snippets/4-seo-tips-for-developers.md new file mode 100644 index 000000000..d2b1a84e4 --- /dev/null +++ b/articles/snippets/4-seo-tips-for-developers.md @@ -0,0 +1,27 @@ +--- +title: 4 SEO tips for developers +shortTitle: SEO tips +type: story +tags: [seo,webdev] +author: chalarangelo +cover: sunrise-over-city +excerpt: SEO is a very relevant topics that most developers are inexperienced in. Here are 4 actionable SEO tips you can implement today. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +SEO is becoming increasingly relevant as the internet keeps growing. While most web developers usually have little experience with SEO, it is not particularly difficult to handle some of the basics. Here are my top SEO tips for web developers: + +### Craft user-friendly URLs and map the appropriately + +First and foremost, start at the very beginning, which is your **URL slugs**. For URL slugs the rule is that you want them to be human-readable, with words separated by dashes and with no random letters or digits added. Secondly, it's a good idea to make sure that parts of your URLs match to a **logical structure** in your website (e.g. `/blog/posts/awesome-list-of-seo-tips`). Finally, take the time to build a sitemap and redirect any broken or old URLs to new ones. This will help reduce 404 pages. + +### Use structured data to help Google understand your pages + +**Structured data** is what Google uses to power its **featured snippets**, those little cards that appear at the top of certain searches. It's a good idea to set up structured data for your pages, which will help you rank higher in search results and possibly even land a featured snippet every once in a while. Just make sure to find the appropriate structured data type and include it in your page and you should be good to go. +### Set up Google Analytics and Google Search Console + +This is hardly a new tip, but I think it deserves a spot on the list, as both of these tools are extremely important. **Google Analytics** allows you to track user behavior and gather data that can help you identify problems and opportunities, while **Google Search Console** is helpful for figuring out what users are searching for before landing on your website. + +### Keep an eye on your markup, performance and accessibility + +Last but not least, something that is probably already on your list, is **optimizing your code**. This includes everything from writing semantic markup and keeping requests to a minimum to optimizing for all device types, making your website accessible and ensuring fast load times. Keep in mind that websites have many moving parts that evolve and change over time, so it's a good idea to audit your website often with a tool like Lighthouse. diff --git a/articles/snippets/5-tips-for-better-pull-requests.md b/articles/snippets/5-tips-for-better-pull-requests.md new file mode 100644 index 000000000..ba8268ad0 --- /dev/null +++ b/articles/snippets/5-tips-for-better-pull-requests.md @@ -0,0 +1,32 @@ +--- +title: 5 tips for better Pull Requests +type: story +tags: [git,github,programming,webdev] +author: chalarangelo +cover: keyboard-tea +excerpt: Writing good code is only part of the job. Here are 5 tips to improve your pull requests and help people review them. +unlisted: true +dateModified: 2021-06-12T19:30:41+03:00 +--- + +Writing good code is only part of the job. Here are 5 tips to improve your pull requests and help people review them: + +### Small pull requests + +The pull requests that get reviewed more thoroughly and confidently and are most often prioritized by developers with limited time are the smallest ones. Make sure you separate concerns into different pull requests (e.g. refactoring and feature implementation), while also keeping commits atomic and well-documented to make the changes easier to understand and review. + +### Good descriptions + +Always take the time to describe your code and any related tasks in your pull request. Explain the feature you are implementing or the bug you are fixing and provide images and steps to reproduce, if applicable. Note decisions made during implementation, your approach, as well as any limitations, findings and points of interest that might help others better understand your code. + +### Rebase onto master + +Always rebase your pull requests onto the `master` branch of the repository. This way you can always test your code against the latest changes and resolve merge conflicts, minimizing issues that might arise later on. Apart from that, reviewers will not have to deal with missing features or bug fixes that might have been deployed already, which can considerably speed up review times. + +### Review it yourself + +Before submitting your pull request for review, always take the time to review it yourself. That way you can handle some low-hanging fruits (typos, easy optimizations, leftover code etc.) and check things you would in other people's pull requests. Self-reviewing has the added benefit of allowing you to reason about decisions and realize which ones might need clarification. + +### Respond to reviews + +Set some time aside to respond to reviews after submitting your pull request. Handle anything you can as soon as possible and start discussion whenever necessary to arrive to a solution. Use `--fixup` for changes suggested in review comments or add new commits to help reviewers parse new changes more easily. Finally, assume good intentions, be polite and thank your peers. diff --git a/articles/snippets/6-javascript-regexp-tricks.md b/articles/snippets/6-javascript-regexp-tricks.md new file mode 100644 index 000000000..c408fcaa0 --- /dev/null +++ b/articles/snippets/6-javascript-regexp-tricks.md @@ -0,0 +1,120 @@ +--- +title: 6 JavaScript Regular Expression features you can use today +shortTitle: JavaScript Regular Expression tips +type: story +tags: [javascript,string,regexp] +author: chalarangelo +cover: taking-photos +excerpt: Regular expressions are very powerful, but hard to master. Understand these features and start using them in your JavaScript code. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +Regular expressions, while very powerful, are notoriously hard to master. Here are 6 useful features that can help you start using them in your JavaScript projects: + +### Capturing groups + +Capturing groups allow you to get specific parts of the matched string, simply by wrapping part of the regular expression in parentheses `(...)`: + +```js +const str = 'JavaScript is a programming language'; +/(JavaScript) is a (.*)/.exec(str); +/* + [ + 0: 'JavaScript is a programming language', + 1: 'JavaScript', + 2: 'programming language' + ] +*/ +``` + +### Non-capturing groups + +Non-capturing groups are used for matching something without capturing it, like an either/or matching group that you do not really need. They are defined similarly to capturing groups, but prefixed with `?:`: + +```js +const str = 'JavaScript is a programming language'; +/(?:JavaScript|Python) is a (.+)/.exec(str); +/* + [ + 0: 'JavaScript is a programming language', + 1: 'programming language' + ] +*/ +``` + +### Named capturing groups + +Named capturing groups allow you to name a capturing group, by prefixing it with ``: + +```js +const str = 'JavaScript is a programming language'; +/(?.+) is a (?.+)/.exec(str); +/* + [ + 0: 'JavaScript is a programming language', + 1: 'JavaScript', + 2: 'programming language', + groups: { + subject: 'JavaScript, + description: 'programming language' + } + ] +*/ +``` + +### Capturing group backreferences + +Backreferences help you write shorter regular expressions, by repeating an existing capturing group, using `\1`, `\2` etc. Similarly, you can also repeat named capturing groups using `\k`: + +```js +const str = 'JavaScript is a programming language - an awesome programming language JavaScript is'; +/(.+) is a (?.+) - an awesome \k \1 is/.exec(str); +/* + [ + 0: 'JavaScript is a programming language - an awesome programming language JavaScript is', + 1: 'JavaScript', + 2: 'programming language', + groups: { + subject: 'JavaScript, + description: 'programming language' + } + ] +*/ +``` + +### Lookaheads + +Lookaheads allow you to check if something is followed by a certain pattern, without actually matching it. You can create positive lookaheads using `?=` and negative lookaheads using `?!`: + +```js +const str = 'JavaScript is not the same as Java and you should remember that'; +/Java(?=Script)(.*)/.exec(str); +/* + [ + 0: 'JavaScript is not the same as Java and you should remember that', + 1: 'Script is not the same as Java and you should remember that' + ] +*/ + +/Java(?!Script)(.*)/.exec(str); +/* + [ + 0: 'Java and you should remember that', + 1: ' and you should remember that' + ] +*/ +``` + +### Unicode characters + +Finally, you can match unicode characters, using `/p{...}` and the `/u` flag. Examples include, but are not limited to `{Emoji}`, `{Math_Symbols}` and `{Script=Greek}`: + +```js +const str = 'Greek looks like this: γεια'; +/\p{Script=Greek}+/u.exec(str); +/* + [ + 0: 'γεια' + ] +*/ +``` diff --git a/articles/snippets/6-python-f-strings-tips.md b/articles/snippets/6-python-f-strings-tips.md new file mode 100644 index 000000000..29880fec0 --- /dev/null +++ b/articles/snippets/6-python-f-strings-tips.md @@ -0,0 +1,79 @@ +--- +title: 6 Python f-strings tips and tricks +type: story +tags: [python,string] +author: chalarangelo +cover: sea-view +excerpt: Python's f-strings can do a lot more than you might expect. Learn a few useful tips and tricks in this quick guide. +dateModified: 2021-07-20T05:00:00-04:00 +--- + +Python's f-strings provide a more readable, concise and less error-prone way to format strings than traditional string formatting. They are packed with useful features that are sure to come in handy in day-to-day use. Let's take a look at some of them. + +### String Interpolation + +The most used f-string feature by far is string interpolation. All you need to do is wrap the value or variable in curly braces (`{}`) and you're good to go. + +```py +str_val = 'apples' +num_val = 42 + +print(f'{num_val} {str_val}') # 42 apples +``` + +### Variable names + +Apart from getting a variable's value, you can also get its name alongside the value. This can be especially useful when debugging and can be easily accomplished by adding an equals sign (`=`) after the variable name inside the curly braces. + +Bear in mind that whitespace inside the curly braces is taken into account, so adding spaces around the equals sign can make for a more readable result. + +```py +str_val = 'apples' +num_val = 42 + +print(f'{str_val=}, {num_val = }') # str_val='apples', num_val = 42 +``` + +### Mathematical operations + +Not syntactically unlike variable names, you can also perform mathematical operations in f-strings. You can place the mathematical expression inside the curly braces and, if you add an equal sign, you'll get the expression and its result. + +```py +num_val = 42 + +print(f'{num_val % 2 = }') # num_val % 2 = 0 +``` + +### Printable representation + +Apart from plain string interpolation, you might want to get the printable representation of a value. This is already easy to accomplish using the `repr()` function. f-strings provide a much shorter syntax by appending a `!r` inside the curly braces. + +```py +str_val = 'apples' + +print(f'{str_val!r}') # 'apples' +``` + +### Number formatting + +Additionally, f-strings can also be used for formatting - hence the **f** in the name. To add formatting to a value you can add a colon (`:`) followed by a format specifier. This can also be combined with the equals sing from before, shall you want to print the name of the variable as well. + +Numbers are a great candidate for this. If, for example, you want to trim a numeric value to two digits after the decimal, you can use the `.2f` format specifier. + +```py +price_val = 6.12658 + +print(f'{price_val:.2f}') # 6.13 +``` + +### Date formatting + +Finally, dates can also be formatted the same way as numbers, using format specifiers. As usual, `%Y` denotes the full year, `%m` is the month and `%d` is the day of the month. + +```py +from datetime import datetime; + +date_val = datetime.utcnow() + +print(f'{date_val=:%Y-%m-%d}') # date_val=2021-07-09 +``` diff --git a/articles/snippets/7-chrome-extensions-for-web-developers.md b/articles/snippets/7-chrome-extensions-for-web-developers.md new file mode 100644 index 000000000..7d82b5df9 --- /dev/null +++ b/articles/snippets/7-chrome-extensions-for-web-developers.md @@ -0,0 +1,39 @@ +--- +title: 7 essential Chrome extensions for web developers +shortTitle: Essential Chrome extensions +type: story +tags: [devtools,webdev] +author: chalarangelo +cover: computer-screens +excerpt: Google Chrome's developer tools are nothing short of amazing. Here are 7 must-have extensions you can add to increase your productivity even further. +unlisted: true +dateModified: 2021-06-12T19:30:41+03:00 +--- + +Google Chrome's developer tools are nothing short of amazing, but there are a few missing tools that can increase your productivity even further. Here are my personal favorite extensions that I use everyday: + +### CSS Peeper +[CSS Peeper](https://chrome.google.com/webstore/detail/css-peeper/mbnbehikldjhnfehhnaidhjhoofhpehk?hl=en) is an all-in-one tool for CSS inspection that allows you to quickly peek at the styles, fonts, color palettes and assets of any website you want. + +### React Developer Tools +[React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) is the de facto extension when working with React, providing all the necessary inspection tools for your React applications. + +### LastPass + +[LastPass](https://chrome.google.com/webstore/detail/lastpass-free-password-ma/hdokiejnpimakedhajhdlcegeplioahd?hl=en) keeps your accounts and API keys secure, by providing a free password manager complete with password generation and password-protected notes. + +### uBlock Origin + +[uBlock Origin](https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en) is a simple yet effective ad blocker extension that you can just install and forget about, as it keeps your browsing experience ad-free. + +### VisBug + +[VisBug](https://chrome.google.com/webstore/detail/visbug/cdockenadnadldjbbgcallicgledbeoc?hl=en) allows you to tweak website designs on the fly, using very intuitive controls and tools, without having to delve into any code at all. + +### JSON Viewer + +[JSON Viewer](https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh?hl=en) provides some much needed syntax highlighting and style customization to JSON responses, which are nowadays very commonplace in most web APIs. + +### EditThisCookie + +[EditThisCookie](https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg?hl=en) is a powerful cookie manager extension, that allows you to add, delete, edit and even export and import cookies quickly and efficiently. diff --git a/articles/snippets/8-tips-for-accessible-websites.md b/articles/snippets/8-tips-for-accessible-websites.md new file mode 100644 index 000000000..821ab0389 --- /dev/null +++ b/articles/snippets/8-tips-for-accessible-websites.md @@ -0,0 +1,42 @@ +--- +title: 8 tips for accessible websites +shortTitle: Accessibility tips +type: story +tags: [javascript,accessibility,webdev] +author: chalarangelo +cover: accessibility +excerpt: Accessibility (a11y) can improve your website and attract new users. Learn how to get started with these 8 quick tips. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +### Use semantic HTML + +HTML5 introduced a variety of new semantic HTML elements to help replace the much dreaded `
`, such as `
`, `
`, `
`, `
+ ); +}; + +ReactDOM.createRoot(document.getElementById('root')).render( + +); +``` + +This is a pretty simple React application, with a container, two buttons and a state variable. The problem is it will crash if you click the button that calls `destroyElement()` and then click the other one. _Why?_ you might ask. The issue here might not be immediately obvious, but if you look at your browser console you will notice the following exception: + +``` +Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. +``` + +This might still be cryptic, so let me explain what's going on. React uses its own representation of the DOM, called a **virtual DOM**, in order to figure out what to render. Usually, the virtual DOM will match the current DOM structure and React will process changes in props and state. It will then update the virtual DOM and then batch and send the necessary changes to the real DOM. + +However, in this case React's virtual DOM and the real DOM are different, because of `destroyElement()` removing the `#my-div` element. As a result, when React tries to update the real DOM with the changes from the virtual DOM, the element cannot be removed as it doesn't exist anymore. This results in the above exception being thrown and your application breaking. + +You can refactor `destroyElement()` to be part of the `App` component and interact with its state to fix the issue in this example. Regardless of the simplicity of the problem or the fix, it showcases how fragile React can be under circumstances. This is only compounded in a large codebase where many developers contribute code daily in different areas. In such a setting, issues like this can be easily introduced and tracking them down can be rather tricky. This is why I would advice you to be very careful when directly manipulating the DOM in combination with React. diff --git a/articles/snippets/code-anatomy-chaining-reduce-for-loop.md b/articles/snippets/code-anatomy-chaining-reduce-for-loop.md new file mode 100644 index 000000000..0487df521 --- /dev/null +++ b/articles/snippets/code-anatomy-chaining-reduce-for-loop.md @@ -0,0 +1,76 @@ +--- +title: Code Anatomy - For loops, array reduce and method chaining +shortTitle: For loops, array reduce and method chaining +type: story +tags: [javascript,array,iterator] +author: chalarangelo +cover: case-study +excerpt: There are many ways to iterate and transform array data in JavaScript. Learn how each one works and where you should use them. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +### For loops + +```js +const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ]; +let filePaths = []; + +for (let file of files) { + const fileName = file.trim(); + if(fileName) { + const filePath = `~/cool_app/${fileName}`; + filePaths.push(filePath); + } +} + +// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo'] +``` + +- Any `for` loop can be used - [read more about the different JavaScript loops](/blog/s/javascript-for-in-for-of-foreach/). +- Less common nowadays, due to functional programming being more popular. +- Control over the iteration, such as skipping over elements or early `return`s. +- Resulting array needs to be declared beforehand, outside the loop. +- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements. +- `O(N)` complexity, each element will be iterated over only once. + +### Array reduce + +```js +const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ]; +const filePaths = files.reduce((acc, file) => { + const fileName = file.trim(); + if(fileName) { + const filePath = `~/cool_app/${fileName}`; + acc.push(filePath); + } + return acc; +}, []); + +// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo'] +``` + +- Uses `Array.prototype.reduce()` with an empty array as the initial value. +- More common nowadays, due to functional programming being more popular. +- Less control over the iteration, cannot skip elements or `return` early. +- Can be chained with other methods, if necessary. +- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements. +- `O(N)` complexity, each element will be iterated over only once. + +### Method chaining + +```js +const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ]; +const filePaths = files + .map(file => file.trim()) + .filter(Boolean) + .map(fileName => `~/cool_app/${fileName}`); + +// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo'] +``` + +- Uses `Array.prototype.map()` and `Array.prototype.filter()`. +- More common nowadays, due to functional programming being more popular. +- Less control over the iteration, cannot skip elements or `return` early. +- Declarative, easier to read and refactor, chain can grow as necessary. +- Does not use `Array.prototype.push()` or the spread (`...`) operator. +- `O(cN)` complexity, `c` iterations per element, (`c`: length of the chain). diff --git a/articles/snippets/code-anatomy-optimizing-recursion.md b/articles/snippets/code-anatomy-optimizing-recursion.md new file mode 100644 index 000000000..93e2a07f2 --- /dev/null +++ b/articles/snippets/code-anatomy-optimizing-recursion.md @@ -0,0 +1,121 @@ +--- +title: Code Anatomy - Optimizing recursive functions +shortTitle: Optimizing recursive functions +type: story +tags: [javascript,recursion,performance] +author: chalarangelo +cover: case-study +excerpt: Recursive code tends to be inefficient or in need of optimization. Learn a couple of tricks we use to speed up our recursive functions. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +### Recursive functions + +Recursion is a programming technique where the final solution is computed by breaking down the problem into smaller instances of the same problem and computing the solution for each one. The most common implementation is a function that calls itself, reducing the problem every time until it reaches an instance of the problem whose solution is either trivial to compute or already known. Let's look at a very well-known example, calculating the `n`th term of the [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number), implemented using recursion in JavaScript: + +```js +const fibonacciNumber = n => + n < 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n; +``` + +To understand recursion better, let's add a `console.log()` call before each `return` and figure out what exactly is happening: + +```js +const fibonacciNumber = n => { + console.log(`[CALLED] fibonacciNumber(${n})`); + const r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n; + console.log(`[RETURN] ${r} for n=${n}`); + return r; +} + +fibonacciNumber(4); +// [CALLED] fibonacciNumber(4) +// [CALLED] fibonacciNumber(3) +// [CALLED] fibonacciNumber(2) +// [CALLED] fibonacciNumber(1) +// [RETURN] 1 for n=1 +// [CALLED] fibonacciNumber(0) +// [RETURN] 0 for n=0 +// [RETURN] 1 for n=2 +// [CALLED] fibonacciNumber(1) +// [RETURN] 1 for n=1 +// [RETURN] 2 for n=3 +// [CALLED] fibonacciNumber(2) +// [CALLED] fibonacciNumber(1) +// [RETURN] 1 for n=1 +// [CALLED] fibonacciNumber(0) +// [RETURN] 0 for n=0 +// [RETURN] 1 for n=2 +// [RETURN] 3 for n=4 +``` + +As you can see, for each value of `n`, `fibonacciNumber` will be called twice, once with `n - 1` and once with `n - 2` and this will continue until it's called with either `1` or `0`. While this is straightforward to write and understand, it is inefficient as it will have to calculate the same value more than once. + +### Calculation memoization + +The solution to this problem, and the first trick that you can use to speed up recursive functions, is to use memoization. We already published [a great blog post on memoization](/articles/s/javascript-memoization/) a little while back, so be sure to check it out to learn more about the subject. Here's our `fibonacciNumber` function, using memoization: + +```js +const fibonacciCache = new Map(); + +const fibonacciNumber = n => { + console.log(`[CALL] fibonacciNumber(${n})`); + const cacheKey = `${n}`; + let r; + if(fibonacciCache.has(cacheKey)) { + r = fibonacciCache.get(cacheKey); + console.log(`[MEMO] Cache hit for ${n}: ${r}`); + } + else { + r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n; + fibonacciCache.set(cacheKey, r); + console.log(`[CALC] Computed and stored value for ${n}: ${r}`); + } + return r; +} + +fibonacciNumber(4); +// [CALL] fibonacciNumber(4) +// [CALL] fibonacciNumber(3) +// [CALL] fibonacciNumber(2) +// [CALL] fibonacciNumber(1) +// [CALC] Computed and stored value for 1: 1 +// [CALL] fibonacciNumber(0) +// [CALC] Computed and stored value for 0: 0 +// [CALC] Computed and stored value for 2: 1 +// [CALL] fibonacciNumber(1) +// [MEMO] Cache hit for 1: 1 +// [CALC] Computed and stored value for 3: 2 +// [CALL] fibonacciNumber(2) +// [MEMO] Cache hit for 2: 1 +// [CALC] Computed and stored value for 4: 3 +``` + +As you can see in the example above, the value for each `n` is only computed once. While the Fibonacci sequence doesn't require any costly calculations, this could make a huge difference for a more computationally expensive problem. It will also be a lot more noticeable for higher values of `n` where the number of calculations will increase significantly. + +### Using iteration + +The second and final trick stems from the very definition of recursive programming turned on its head. If we can solve a smaller instance of the problem and use it for the solution of a larger instance of the problem, it should be possible to work iteratively from the smaller problem to the larger one, instead of recursively. Here's this idea in practice for our `fibonacciNumber` function: + +```js +const fibonacciNumber = n => { + let r = 0, l = 1, s = 0; + for(let i = 0; i < n; i++) { + r = l; + l = s; + s = r + l; + console.log(`[CALC] i = ${i}: r = ${r}, l = ${l}, s = ${s}`); + } + return s; +} + +fibonacciNumber(4); +// [CALC] i = 0: r = 1, l = 0, s = 1 +// [CALC] i = 1: r = 0, l = 1, s = 1 +// [CALC] i = 2: r = 1, l = 1, s = 2 +// [CALC] i = 3: r = 1, l = 2, s = 3 +``` + +The iterative solution above makes the same calculations as the memoized one, however it performs better due to two key reasons. First of all, there is no cache, which would take up space in memory, making the latter implementation require fewer resources. Similarly, as there are no recursive calls or checks for cache hits, the code performs better and requires fewer resources to execute. + +However, you have to bear in mind what the actual use cases of your recursive code are and be very careful how you optimize them. Memoization can be a more powerful tool if a recursive function is called multiple times with different arguments, as its cache persists between calls, while iteration can be faster for recursive computations that are used less frequently. Always pay attention to your code and optimize for the cases you know or anticipate to be more common. diff --git a/articles/snippets/code-anatomy-performant-python.md b/articles/snippets/code-anatomy-performant-python.md new file mode 100644 index 000000000..84bbce4f9 --- /dev/null +++ b/articles/snippets/code-anatomy-performant-python.md @@ -0,0 +1,62 @@ +--- +title: Code Anatomy - Writing high performance Python code +shortTitle: Performant Python code +type: story +tags: [python,list,performance] +cover: walking-on-top +excerpt: Writing efficient Python code can be tricky. Read how we optimize our list snippets to increase performance using a couple of simple tricks. +dateModified: 2021-11-07T16:34:37+03:00 +--- + +Writing short and efficient Python code is not always easy or straightforward. However, it's often that we see a piece of code and we don't realize the thought process behind the way it was written. We will be taking a look at the [difference](/python/s/difference) snippet, which returns the difference between two iterables, in order to understand its structure. + +Based on the description of the snippet's functionality, we can naively write it like this: + +```py +def difference(a, b): + return [item for item in a if item not in b] +``` + +This implementation may work well enough, but doesn't account for duplicates in `b`. This makes the code take more time than necessary in cases with many duplicates in the second list. To solve this issue, we can make use of the `set()` method, which will only keep the unique values in the list: + +```py +def difference(a, b): + return [item for item in a if item not in set(b)] +``` + +This version, while it seems like an improvement, may actually be slower than the previous one. If you look closely, you will see that `set()` is called for every `item` in `a` causing the result of `set(b)` to be evaluated every time. Here's an example where we wrap `set()` with another method to better showcase the problem: + +```py +def difference(a, b): + return [item for item in a if item not in make_set(b)] + +def make_set(itr): + print('Making set...') + return set(itr) + +print(difference([1, 2, 3], [1, 2, 4])) +# Making set... +# Making set... +# Making set... +# [3] +``` + +The solution to this issue is to call `set()` once before the list comprehension and store the result to speed up the process: + +```py +def difference(a, b): + _b = set(b) + return [item for item in a if item not in _b] +``` + +Another option worth mentioning in terms of performance is the use of a list comprehension versus `filter()` and `list()`. Implementing the same code using the latter option would result in something like this: + +```py +def difference(a, b): + _b = set(b) + return list(filter(lambda item: item not in _b, a)) +``` + +Using `timeit` to analyze the performance of the last two code examples, it's pretty clear that using list comprehension can be up to ten times faster than the alternative. This is due to it being a native language feature that works very similar to a simple `for` loop without the overhead of the extra function calls. This explains why we prefer it, apart from readability. + +This pretty much applies to most mathematical list operation snippets, such as [difference](/python/s/difference), [symmetric_difference](/python/s/symmetric-difference) and [intersection](/python/s/intersection). diff --git a/articles/snippets/common-regexp-cheatsheet.md b/articles/snippets/common-regexp-cheatsheet.md new file mode 100644 index 000000000..35ec342fd --- /dev/null +++ b/articles/snippets/common-regexp-cheatsheet.md @@ -0,0 +1,132 @@ +--- +title: Common regular expressions +type: cheatsheet +tags: [javascript,string,regexp] +author: chalarangelo +cover: rocky-beach +excerpt: A collection of regular expressions that can be used to solve common problems. +dateModified: 2022-11-09T05:00:00-04:00 +--- + +### Exact string match + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Add the string you want to match in-between the two anchors. + +```js +const regexp = /^abc$/; +// Where 'abc' is the exact string you want to match +``` + +### Match empty string + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Do not add any characters in-between to match an empty string. + +```js +const regexp = /^$/; +``` + +### Match whitespace sequences + +- Use the `\s` meta-sequence to match any whitespace character, including spaces, tabs, newlines, etc. +- Use the `+` quantifier to match one or more occurrences of the previous character. +- Add the global flag (`g`) to match all occurrences of the pattern in the string. + +```js +const regexp = /\s+/g; +``` + +### Match line breaks + +- Depending on the environment, line breaks can be represented in different ways. +- Use the `\r` character to match carriage returns, the `\n` character to match newlines, and the `\r\n` sequence to match carriage returns followed by newlines. +- Add the global (`g`) and multiline (`m`) flags to match all occurrences of the pattern in the string. + +```js +const regexp = /\r|\n|\r\n/gm; +``` + +### Match non-word characters + +- Use negation (`^`) to match any character that is not a word character (`\w`) or a whitespace character (`\s`). +- Add the global flag (`g`) to match all occurrences of the pattern in the string. +- Add the ignore case flag (`i`) to match both uppercase and lowercase characters. + +```js +const regexp = /[^\w\s]/gi; +``` + +### Match alphanumeric, dashes and hyphens + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Use the `a-zA-Z0-9-` pattern to match any alphanumeric character, dashes and hyphens. +- Use the `+` quantifier to match one or more occurrences of the previous character. +- Particularly useful when matching URL slugs. + +```js +const regexp = /^[a-zA-Z0-9-_]+$/; +``` + +### Match letters and whitespaces + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Use the `a-zA-Z\s` pattern to match any letter and whitespace character. +- Use the `+` quantifier to match one or more occurrences of the previous pattern. + +```js +const regexp = /^[A-Za-z\s]+$/; +``` + +### Pattern not included + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Use a negative lookahead (`?!`) to match any character that is not followed by the pattern you want to exclude. +- Add the global flag (`g`) to match all occurrences of the pattern in the string. +- To ensure more than one pattern is not included, use the `|` character to separate them. + +```js +const regexp = /^((?!(abc|bcd)).)*$/; +// Where 'abc' and 'bcd' are pattern you want to exclude +``` + +### Text inside brackets + +- Use the `\(` and `\)` characters to match the opening and closing brackets, respectively. +- Use a capturing group between the two and exclude the closing parenthesis character. +- Use the `+` quantifier to match one or more characters, as needed. +- Add the global flag (`g`) to match all occurrences of the pattern in the string. +- Replace `\(` and `\)` with `\[` and `\]` to match square brackets and with `\{` and `\}` to match curly brackets. + +```js +const regexp = /\(([^)]+)\)/g; +``` + +### Validate GUID/UUID + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Validate each segment of the GUID/UUID separately using numeric character ranges and quantifiers. + +```js +const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/; +``` + +### Validate date format (DD/MM/YYYY) + +- Use the `^` and `$` anchors to match the start and end of the string, respectively. +- Validate each segment of the date separately using numeric character ranges and quantifiers. +- Alter the order of the segments and separators to match different formats. + +```js +const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/; +``` + +### Chunk string into n-size chunks + +- Use the `.{1,n}` quantifier to match any character between `1` and `n` times. +- Add the global flag (`g`) to match all occurrences of the pattern in the string. + +```js +const regexp = /.{1,2}/g; +// Where '2' is the number of characters per chunk +``` diff --git a/articles/snippets/console-log-cheatsheet.md b/articles/snippets/console-log-cheatsheet.md new file mode 100644 index 000000000..01569c19b --- /dev/null +++ b/articles/snippets/console-log-cheatsheet.md @@ -0,0 +1,118 @@ +--- +title: JavaScript console.log() tips & tricks +type: story +tags: [javascript,browser,cheatsheet] +author: chalarangelo +cover: terminal +excerpt: Level up your JavaScript logging with these `console.log()` tips and tricks. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +Everyone uses the JavaScript console for logging or debugging every once in a while. But there is a lot more to the [console](https://developer.mozilla.org/en-US/docs/Web/API/Console) object than `console.log()`. + +### Computed property names + +ES6 computed property names are particularly useful, as they can help you identify logged variables by adding a pair of curly braces around them. + +```js +const x = 1, y = 2, z = 3; + +console.log({x, y, z}); // {x: 1, y: 2, z: 3} +``` + +### console.trace() + +[`console.trace()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace) works the exact same as `console.log()`, but it also outputs the entire stack trace so you know exactly what's going on. + +```js +const outer = () => { + const inner = () => console.trace('Hello'); + inner(); +}; + +outer(); +/* + Hello + inner @ VM207:3 + outer @ VM207:5 + (anonymous) @ VM228:1 +*/ +``` + +### console.group() + +[`console.group()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/group) allows you to group logs into collapsable structures and is particularly useful when you have multiple logs. + +```js +console.group('Outer'); // Create a group labelled 'Outer' +console.log('Hello'); // Log inside 'Outer' +console.groupCollapsed('Inner'); // Create a group labelled 'Inner', collapsed +console.log('Hellooooo'); // Log inside 'Inner' +console.groupEnd(); // End of current group, 'Inner' +console.groupEnd(); // End of current group, 'Outer' +console.log('Hi'); // Log outside of any groups +``` + +### Logging levels + +There are a few more logging levels apart from `console.log()`, such as [`console.debug()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug), [`console.info()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/info), [`console.warn()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn) and [`console.error()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/error). + +```js +console.debug('Debug message'); +console.info('Useful information'); +console.warn('This is a warning'); +console.error('Something went wrong!'); +``` + +### console.assert() + +[`console.assert()`](https://developer.mozilla.org/en-US/docs/Web/API/console/assert) provides a handy way to only log something as an error when an assertion fails (i.e. when the first argument is `false`), otherwise skip the log entirely. + +```js +const value = 10; + +console.assert(value === 10, 'Value is not 10!'); // Nothing is logged +console.assert(value === 20, 'Value is not 20!'); // Logs "Value is not 20!" +``` + +### console.count() + +You can use [`console.count()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/count) to count how many times a piece of code has executed. + +```js +Array.from({ length: 4 }).forEach( + () => console.count('items') // Call the counter labelled 'items' +); +/* + items: 1 + items: 2 + items: 3 + items: 4 +*/ +console.countReset('items'); // Reset the counter labelled 'items' +``` + +### console.time() + +[`console.time()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) gives you a quick way to check the performance of your code, but should not be used for real benchmarking due to its low accuracy. + +```js +console.time('slow comp'); // Start the 'slow comp' timer +console.timeLog('slow comp'); // Log the value of the 'slow comp' timer +console.timeEnd('slow comp'); // Stop and log the 'slow comp' timer +``` + +### CSS + +Last but not least, you can use the `%c` string substitution expression in `console.log()` to apply CSS to parts of a log. + +```js +console.log( + 'CSS can make %cyour console logs%c %cawesome%c!', // String to format + // Each string is the CSS to apply for each consecutive %c + 'color: #fff; background: #1e90ff; padding: 4px', // Apply styles + '', // Clear any styles + 'color: #f00; font-weight: bold', // Apply styles + '' // Clear any styles +); +``` diff --git a/articles/snippets/cookies-local-storage-session.md b/articles/snippets/cookies-local-storage-session.md new file mode 100644 index 000000000..3b1746966 --- /dev/null +++ b/articles/snippets/cookies-local-storage-session.md @@ -0,0 +1,56 @@ +--- +title: What is the difference between cookies, local storage, and session storage? +shortTitle: Cookies, local storage, and session storage +type: question +tags: [javascript,browser,webdev] +author: chalarangelo +cover: three-vases +excerpt: Learn the difference between cookies, local storage and session storage and start using the correct option for your needs. +dateModified: 2021-06-12T19:30:41+03:00 +--- + +### Cookies + +Cookies store small amounts of data that has to be sent back to the server with subsequent requests and their expiration can be set from either server or client. They are primarily used for server-side reading. + +- Capacity: 4KB +- Accessible from: Any window +- Expiration: Manually set +- Storage location: Browser and server +- Sent with requests: Yes +- Blockable by users: Yes +- Editable by users: Yes + +### Local storage + +Local storage stores a larger amount of data on the client's computer in a key-value pair format and has no expiration date. Data is never transferred to the server and is accessible via JavaScript and HTML5. + +- Capacity: 10MB +- Accessible from: Any window +- Expiration: Never +- Storage location: Browser only +- Sent with requests: No +- Blockable by users: Yes +- Editable by users: Yes + +### Session storage + +Session storage stores a larger amount of data on the client's computer only for the current session, expiring the data on tab close. Data is never transferred to the server and is accessible client-side from the same tab. + +- Capacity: 5MB +- Accessible from: Same tab +- Expiration: On tab close +- Storage location: Browser only +- Sent with requests: No +- Blockable by users: Yes +- Editable by users: Yes + +| | Cookies | Local storage | Session storage | +| -- | -- | -- | -- | +| Capacity | 4KB | 10MB | 5MB | +| Accessible from | Any window | Any window | Same tab | +| Expiration | Manually set | Never | On tab close | +| Storage location | Browser and server | Browser only | Browser only | +| Sent with requests | Yes | No | No | +| Blockable by users | Yes | Yes | Yes | +| Editable by users | Yes | Yes | Yes | diff --git a/articles/snippets/copy-text-to-clipboard-with-javascript.md b/articles/snippets/copy-text-to-clipboard-with-javascript.md new file mode 100644 index 000000000..93c26aa98 --- /dev/null +++ b/articles/snippets/copy-text-to-clipboard-with-javascript.md @@ -0,0 +1,54 @@ +--- +title: How can I copy text to clipboard with JavaScript? +shortTitle: Copy text to clipboard +type: question +tags: [javascript,browser] +author: chalarangelo +cover: typing +excerpt: Learn how to programmatically copy text to clipboard with a few lines of JavaScript and level up your web development skills. +dateModified: 2022-01-11T09:47:54+03:00 +--- + +### Asynchronous Clipboard API + +A very common need when building websites is the ability to copy text to clipboard with a single button click. If you only need to support modern browsers, it's highly recommended to use the asynchronous [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). It's supported in all modern browsers and provides an easy and secure way to update the clipboard's contents. + +All you have to do is ensure `Navigator`, `Navigator.clipboard` and `Navigator.clipboard.writeText` are truthy and then call `Clipboard.writeText()` to copy the value to clipboard. In case anything goes wrong, you can use `Promise.reject()` to return a promise that rejects immediately and keep the return type consistent. + +```js +const copyToClipboard = str => { + if (navigator && navigator.clipboard && navigator.clipboard.writeText) + return navigator.clipboard.writeText(str); + return Promise.reject('The Clipboard API is not available.'); +}; +``` + +This is pretty much how the [copyToClipboardAsync snippet](/js/s/copy-to-clipboard-async) is implemented and should work across all modern browsers. + +### Document.execCommand('copy') + +While support for the Clipboard API is pretty high across the board, you might need a fallback if you have to support older browsers. If that's the case, you can use `Document.execCommand('copy')` to do so. Here's a quick step-by-step guide: + +1. Create a`