├── .gitignore
├── LICENSE
├── README.md
└── contents
├── accept-any-numbers-of-parameters.mdx
├── access-local-webpack-dev-servers-from-external-devices.mdx
├── access-the-selected-node-in-the-console.mdx
├── add-a-line-break-between-inline-elements.mdx
├── add-a-subject-to-a-mailto-link.mdx
├── add-an-icon-to-external-links.mdx
├── add-keyboard-shortcuts.mdx
├── always-pass-the-radix-to-parseint.mdx
├── always-put-the-footer-at-the-bottom.mdx
├── always-use-noopener-or-noreferrer-for-links-opened-in-new-tabs.mdx
├── append-leading-zeros-to-ordered-list-items.mdx
├── avoid-boolean-parameters.mdx
├── avoid-invisible-text-when-loading-a-font.mdx
├── avoid-skipping-heading-levels.mdx
├── avoid-to-use-colons-and-periods-in-the-id-attribute.mdx
├── avoid-using-css-import.mdx
├── avoid-using-default-exports.mdx
├── avoid-using-multiple-h1-tags-per-page.mdx
├── avoid-using-the-b-i-s-and-u-tags.mdx
├── capture-a-screenshot-without-shadow-on-macos.mdx
├── center-an-element-vertically-and-horizontally.mdx
├── check-if-the-browser-supports-for-an-element-attribute.mdx
├── checkout-the-previous-branch.mdx
├── combine-google-font-requests.mdx
├── combine-styles-with-the-is-pseudo-class-selector.mdx
├── compose-multiple-react-providers.mdx
├── conditional-logging-in-the-console.mdx
├── convert-string-to-number.mdx
├── copy-a-long-variable-from-the-console.mdx
├── copy-screenshots-to-the-clipboard-on-macos.mdx
├── copy-the-base64-data-of-an-image.mdx
├── copy-the-full-path-of-a-file-on-macos.mdx
├── copy-variables-from-the-browser-console-to-the-clipboard.mdx
├── count-how-many-times-a-function-has-been-called.mdx
├── create-a-big-file-on-linux.mdx
├── create-a-custom-emoji-cursor.mdx
├── create-a-descending-list-of-numbered-items.mdx
├── create-a-download-link.mdx
├── create-a-file-of-any-size.mdx
├── create-a-function-that-accepts-a-single-parameter.mdx
├── create-a-line-on-sides-heading.mdx
├── create-a-multiline-strings.mdx
├── create-a-polyfill.mdx
├── create-an-array-with-conditional-elements.mdx
├── create-an-autocomplete-list-with-the-datalist-element.mdx
├── create-an-object-with-dynamic-keys.mdx
├── create-an-one-time-event-handler.mdx
├── create-shapes-with-the-clip-path-property.mdx
├── disable-all-fields-of-a-form.mdx
├── display-links-in-the-print-mode.mdx
├── do-not-add-custom-methods-to-primitive-objects.mdx
├── do-not-mix-styles-of-an-element-with-its-container.mdx
├── do-not-use-magic-numbers-when-manipulating-strings.mdx
├── do-not-use-submit-to-name-a-submit-button.mdx
├── early-return.mdx
├── enforce-required-parameters.mdx
├── escape-css-class-names.mdx
├── filter-file-types-of-a-file-input.mdx
├── find-scrollable-elements.mdx
├── find-the-root-npm-package-to-update.mdx
├── fold-css-declarations-with-region-markers.mdx
├── force-the-browsers-to-download-new-favicon.mdx
├── format-a-list.mdx
├── format-a-number-as-a-currency-string.mdx
├── get-characters-of-a-string.mdx
├── get-rid-of-escaping-quotes-with-template-literal.mdx
├── get-the-current-timestamp.mdx
├── go-to-the-previous-directory.mdx
├── hide-an-element-with-chrome-devtools.mdx
├── highlight-text-with-the-mark-element.mdx
├── ignore-case-sensitivity-in-a-css-attribute-selector.mdx
├── ignore-items-from-array-destructuring.mdx
├── include-properties-conditionally.mdx
├── increase-or-decrease-css-values-with-chrome-devtools.mdx
├── indicate-img-elements-that-miss-alt-attribute.mdx
├── insert-a-link-into-a-markdown-editor-quickly.mdx
├── inspect-an-element-shown-on-hover.mdx
├── keep-the-calculation-of-a-magic-number.mdx
├── lazy-loading-images-with-the-loading-attribute.mdx
├── list-branches-sorted-by-most-recent-commit-date.mdx
├── load-given-characters-in-a-google-font-request.mdx
├── locate-an-element-with-a-given-selector.mdx
├── log-a-value-to-the-console.mdx
├── log-a-variable-in-an-arrow-function.mdx
├── log-a-variable-to-the-console-using-conditional-breakpoints.mdx
├── log-an-array-to-the-console.mdx
├── log-the-full-object-in-nodejs.mdx
├── make-a-table-with-equal-column-widths.mdx
├── manage-multiple-boolean-flags.mdx
├── merge-different-arrays.mdx
├── move-the-cursor-to-any-position-in-a-macos-command.mdx
├── move-the-screenshot-area-on-macos.mdx
├── number-headings-and-subheadings-automatically.mdx
├── omit-properties-of-a-svelte-component.mdx
├── omit-values-of-html-boolean-attributes.mdx
├── open-a-package-s-homepage-or-repo.mdx
├── open-all-links-in-new-tabs.mdx
├── override-the-behavior-of-instanceof.mdx
├── pass-an-array-as-function-arguments.mdx
├── pass-specified-environments-to-webpack.mdx
├── persist-console-logs-between-page-refreshes.mdx
├── pick-given-properties-from-a-json-representation.mdx
├── pick-the-first-and-last-items-of-an-array.mdx
├── prefix-class-name-with-js-to-manipulate-with-javascript.mdx
├── pretty-format-json.mdx
├── prevent-a-string-from-being-escaped.mdx
├── prevent-anchor-links-from-disappearing-behind-a-sticky-header.mdx
├── prevent-browsers-from-asking-to-translate.mdx
├── prevent-macos-from-going-to-sleep.mdx
├── prevent-scrolling-behind-a-fixed-header.mdx
├── prevent-the-default-behavior-with-jquery-event-handler.mdx
├── pure-collapsible-element.mdx
├── put-special-operators-at-the-beginning-of-a-function.mdx
├── quick-query-elements-in-the-console.mdx
├── quickly-insert-alternate-characters-while-typing.mdx
├── quickly-type-color-variables.mdx
├── remove-a-property-from-an-object.mdx
├── remove-the-border-from-the-last-navigation-item.mdx
├── replace-multiple-if-statements-with-a-lookup-table.mdx
├── replace-multiple-if-statements-with-a-single-switch-statement.mdx
├── return-an-object-in-an-arrow-function-quickly.mdx
├── reuse-the-current-color.mdx
├── run-the-last-command-as-the-root-user.mdx
├── save-a-few-bytes-when-checking-the-existence-of-module.mdx
├── select-a-folder-to-upload.mdx
├── select-the-direct-children-of-an-element.mdx
├── separate-list-items.mdx
├── set-a-numbering-type-for-a-list-element.mdx
├── set-content-for-an-empty-link.mdx
├── set-git-configuration-conditionally.mdx
├── share-recommendation-visual-studio-code-extensions.mdx
├── shorten-codes-with-the-comma-operator.mdx
├── shorten-import-paths-in-typescript.mdx
├── shorten-import-paths-in-webpack.mdx
├── show-a-placeholder-for-an-empty-list.mdx
├── show-the-first-letter-only.mdx
├── skip-questions-when-creating-a-package-json-file.mdx
├── smooth-scrolling-with-pure-css.mdx
├── specify-the-doctype.mdx
├── start-a-simple-web-server-on-macos.mdx
├── style-broken-images.mdx
├── style-index-numbers-of-list-items.mdx
├── style-list-items-with-special-characters.mdx
├── swap-two-variables.mdx
├── toggle-hidden-files-on-macos.mdx
├── track-the-focused-element-with-chrome-devtools.mdx
├── transform-values-from-a-json-representation.mdx
├── trim-the-spaces-before-parsing-a-number.mdx
├── truncate-long-text.mdx
├── unpack-a-property-of-an-object-with-different-name.mdx
├── use-an-underscore-to-name-unused-argument.mdx
├── use-array-includes-for-multiple-conditionals.mdx
├── use-css-fallback-properties.mdx
├── use-documentfragments-when-adding-a-big-list-of-elements.mdx
├── use-multiple-ssh-keys-for-different-github-accounts.mdx
├── use-negative-nth-child-and-nth-last-child.mdx
├── use-short-circuits-conditionals.mdx
├── use-string-literals-for-the-typescript-enum-values.mdx
├── use-template-literal-to-concatenate-strings.mdx
├── use-the-datetime-attribute-when-displaying-dates-times.mdx
├── use-the-strict-equality-operator-when-comparing-variables.mdx
├── use-the-wbr-tags-to-represent-path.mdx
├── use-underscores-to-represent-a-number.mdx
├── validate-an-input-value-with-the-pattern-attribute.mdx
├── view-a-file-in-a-different-branch-without-switching-the-branch.mdx
├── view-print-stylesheets-with-chrome-devtools.mdx
├── visualize-elements-on-page-with-the-outline-style.mdx
├── watch-a-variable-value-with-live-expressions.mdx
├── wrap-arrow-function-body-in-parentheses.mdx
└── write-css-rules-for-firefox-only.mdx
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 phuocng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Front-end Tips
2 |
3 | A series of super tiny, quick tips, tricks and best practices of front-end development.
4 |
5 | The series cover different topics:
6 |
7 | - CSS
8 | - HTML
9 | - JavaScript
10 | - TypeScript
11 | - Browser developer tools
12 |
13 | ## Contributing
14 |
15 | Pull requests are welcomed. To submit your favorite tip, please create a markdown file, and put it in the [contents](contents) folder.
16 | The content of markdown file has to look like
17 |
18 | ```md
19 | ---
20 | category: ___
21 | created: '___'
22 | tags: ___
23 | title: ___
24 | ---
25 |
26 | The content of post
27 | ```
28 |
29 | - `category`: Can be one of `Tip`, `Trick` or `Practice`
30 | - `created`: The date that post is created
31 | - `tags`: The list of topic(s), separated by a comma
32 | - `title`: Must match with the file name
33 |
34 | [Here](contents/convert-string-to-number.mdx) is an example.
35 |
36 | ## About
37 |
38 | This project is developed by _Nguyen Huu Phuoc_. I love building products and sharing knowledge.
39 |
40 | Be my friend on
41 |
42 | - [Twitter](https://twitter.com/nghuuphuoc)
43 | - [Github](https://github.com/phuocng)
44 |
--------------------------------------------------------------------------------
/contents/accept-any-numbers-of-parameters.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-01'
4 | tags: JavaScript
5 | title: Accept any numbers of parameters
6 | ---
7 |
8 | In the old days, we can use the `arguments` variable to get the dynamic parameters that are passed to a function.
9 |
10 | ```js
11 | const sum = function () {
12 | return Array.from(arguments).reduce((a, b) => a + b, 0);
13 | };
14 |
15 | sum(1); // 1
16 | sum(1, 2); // 3
17 | sum(1, 2, 3); // 6
18 | sum(1, 2, 3, 4); // 10
19 | ```
20 |
21 | However, most of us aren't aware about the existence of `arguments`. It has some drawbacks such as
22 |
23 | - It's not an array, so we usually have to convert it to array first in order to use `Array` methods
24 | - More importantly, it isn't available in arrow functions
25 |
26 | The ES6 rest parameter (`...`) provides an easier way to work with an unknown numbers of parameters. The `sum` function above can be [written](https://phuoc.ng/collection/1-loc/calculate-the-sum-of-arguments/) as following:
27 |
28 | ```js
29 | const sum = (...params) => params.reduce((a, b) => a + b, 0);
30 |
31 | sum(1, 2, 3, 4, 5); // 15
32 | ```
33 |
34 | ## See also
35 |
36 | - [Create a function that accepts a single parameter](https://phuoc.ng/collection/tips/create-a-function-that-accepts-a-single-parameter/)
37 | - [Pass an array as function arguments](https://phuoc.ng/collection/tips/pass-an-array-as-function-arguments/)
38 |
--------------------------------------------------------------------------------
/contents/access-local-webpack-dev-servers-from-external-devices.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/webpack-dev-server-host-option.png
4 | created: '2021-04-26'
5 | tags: Webpack
6 | title: Access local webpack dev servers from external devices
7 | ---
8 |
9 | Sometimes we would like to access a local development server externally. It happens when we want to see how our web application works on mobile phones. Or co-workers want to see how it looks on their browsers.
10 |
11 | The local server is available at `http://localhost:PORT` where `PORT` represents the port number that the server listens on. In order to make it accessible in the same network, we have to replace `localhost` with the IP address.
12 |
13 | Webpack dev server allows the server to be available externally via the `host` option:
14 |
15 | ```js
16 | // webpack.config.js
17 | module.exports = {
18 | ...
19 | devServer: {
20 | host: '0.0.0.0',
21 | port: 8001,
22 | ...
23 | },
24 | };
25 | ```
26 |
27 | The `host` option can be passed to the command line interface as well:
28 |
29 | ```shell
30 | webpack serve --host 0.0.0.0
31 | ```
32 |
33 | With the configurations above, we can access the server internally (`http://localhost:8001`) and externally (`http://THE-IP-ADDRESS:8001`).
34 |
--------------------------------------------------------------------------------
/contents/access-the-selected-node-in-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-04'
4 | tags: DevTools
5 | title: Access the selected node in the Console
6 | ---
7 |
8 | When you select a node under the _Elements_ tab, Chrome DevTools adds `== $0` right after the node.
9 |
10 | In give us a hint that we can use the `$0` expression to access the selected node.
11 | You can invoke any [DOM APIs](https://phuoc.ng/collection/html-dom) from `$0`.
12 |
13 | 
14 |
15 | > The DevTools remembers the last five selected nodes. In addition to `$0`, we can use `$1`, `$2`, `$3` and `$4` to access the last selected nodes.
16 |
17 | You also can right-click the node, and choose _Store as global variable_ from the context menu. DevTools creates a variable, `temp1` for example, to represent the selected node.
18 | Now you can use `temp1` to manage the node in the same way as `$0`.
19 |
20 | 
21 |
--------------------------------------------------------------------------------
/contents/add-a-line-break-between-inline-elements.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-05'
4 | tags: CSS
5 | title: Add a line break between inline elements
6 | ---
7 |
8 | It's a common scenario where we want to split a heading into multiple lines. For example, the heading is displayed continuously on a big screen. But on a small screen, it should breaks into different parts.
9 |
10 | Without using the `br` tag, we can construct the heading from various inline `span` elements.
11 |
12 | ```html
13 |
14 | Tip, tricks, best practices
15 | of front-end development
16 |
17 | ```
18 |
19 | By using the `::after` pseudo element, we are able to add a line break after the first inline element:
20 |
21 | ```css
22 | .primary::after {
23 | content: '\A';
24 | white-space: pre;
25 | }
26 | ```
27 |
28 | Where `\A` represents the line break character.
29 |
30 |
31 | ```html
32 |
33 | Tip, tricks, best practices
34 | of front-end development
35 |
36 | ```
37 |
38 | ```css
39 | .demo__heading {
40 | font-weight: 500;
41 | text-align: center;
42 | }
43 | .demo__heading--primary::after {
44 | content: '\\A';
45 | white-space: pre;
46 | }
47 | ```
48 |
49 |
--------------------------------------------------------------------------------
/contents/add-a-subject-to-a-mailto-link.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-25'
4 | tags: HTML
5 | title: Add a subject to a mailto link
6 | ---
7 |
8 | We already know that when users click a mailto link (`
14 | ```
15 |
16 | In addition to the `subject` parameter, there are also `cc`, `bcc` and `body` parameters. They'll be filled in the default email application if specified.
17 |
18 | ```html
19 |
20 | ```
21 |
22 | It's worth noting that the `subject` and `body` parameters must replace the spaces with `%20`. There are some [online tool](https://mailtolinkgenerator.com) to [generate](https://mailtolink.me) the final mailto link for us.
23 |
24 | > If you want to use multiple email addresses in the `cc` and `bcc` parameters, then separate them with commas (`,`)
25 |
--------------------------------------------------------------------------------
/contents/add-an-icon-to-external-links.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-23'
4 | tags: CSS
5 | title: Add an icon to external links
6 | ---
7 |
8 | If you want to add an icon to `a` element that links to an external website, then you can depend on the `href` or `[target="_blank"]` attribute.
9 |
10 | ## The target attribute
11 |
12 | ```css
13 | a[target='_blank'] {
14 | align-items: center;
15 | display: flex;
16 | }
17 | a[target='_blank']:after {
18 | /* The icon can be a SVG or image file */
19 | content: url(/link/to/icon.svg);
20 | margin-left: 0.25rem;
21 | }
22 | ```
23 |
24 | The `content` property can be a string that appends to the link:
25 |
26 | ```css
27 | a[target='_blank']:after {
28 | content: ' (external)';
29 | }
30 | ```
31 |
32 | Using an icon font such as [Font Awesome](https://fontawesome.com) is another option:
33 |
34 | ```css
35 | a[target='_blank']:after {
36 | content: ' \f08e';
37 | font-family: 'FontAwesome';
38 | }
39 | ```
40 |
41 | ## The href attribute
42 |
43 | This approach relies on the `href` attribute. A link is treated as external if
44 |
45 | - It doesn't match with the domain of website
46 | - It isn't an anchor (doesn't start with `#`)
47 | - It doesn't start with `/`
48 |
49 | It's up to you to define more conditions here. But with the set of conditions above, the `:after` looks like
50 |
51 | ```css
52 | a:not([href*='domain.com']):not([href^='#']):not([href^='/']):after {
53 | /* Set the `content` property as mentioned in the first approach */
54 | }
55 | ```
56 |
57 |
58 | ```html
59 |
69 | ```
70 |
71 | ```css
72 | .demo__link {
73 | align-items: center;
74 | display: flex;
75 | }
76 | .demo__link--icon[target='_blank']:after {
77 | content: url(/assets/tips/link.svg);
78 | margin-left: 0.25rem;
79 | }
80 | .demo__link--text[target='_blank']:after {
81 | content: ' (external)';
82 | margin-left: 0.25rem;
83 | }
84 | ```
85 |
86 |
87 | ## See also
88 |
89 | - [Ignore case sensitivity in a CSS attribute selector](https://phuoc.ng/collection/tips/ignore-case-sensitivity-in-a-css-attribute-selector/)
90 |
--------------------------------------------------------------------------------
/contents/add-keyboard-shortcuts.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-03-14'
4 | tags: HTML
5 | title: Add keyboard shortcuts
6 | ---
7 |
8 | By using the `accesskey` attribute, we can specify a shortcut key for the user to activate or focus on an element. The value of the attribute has to be a single character.
9 |
10 | It's worth noting that each browser provides a different combination of keys to access the shortcut.
11 |
12 | | Browser | macOS | Windows | Linux |
13 | | ------- | -------------------- | --------------------- | --------------------- |
14 | | Chrome | `alt` + `ctrl` + key | `alt` + key | `alt` + key |
15 | | Firefox | `alt` + `ctrl` + key | `alt` + `shift` + key | `alt` + `shift` + key |
16 | | Safari | `alt` + `ctrl` + key | n/a | n/a |
17 |
18 | In the following sample code, pressing the combination `alt` + `ctrl` + `e` on Chrome macOS will trigger the button's `click` event.
19 |
20 | ```html
21 |
22 | ```
23 |
--------------------------------------------------------------------------------
/contents/always-pass-the-radix-to-parseint.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-02-23'
4 | tags: JavaScript
5 | title: Always pass the radix to parseInt
6 | ---
7 |
8 | The `parseInt` method takes two parameters:
9 |
10 | ```js
11 | parseInt(value, radix);
12 | ```
13 |
14 | The second parameter specifies the current numeral system. In the case it's not specified, then it will be set automatically based on the value.
15 | If the value starts with `0x` or `0X`, then the radix is 16 (hexadecimal). In other cases, the radix is 10 (decimal).
16 |
17 | In the older versions of JavaScript, if the string starts with `0` then the radix is set as 8 (octal).
18 |
19 | ```js
20 | parseInt('0xF'); // 15
21 | parseInt('0XF'); // 15
22 | parseInt('0xF', 16); // 15
23 |
24 | parseInt('0xF', 10); // 0
25 | ```
26 |
27 | Since the method could be implemented differently in different versions of JavaScript and browsers, it's recommended to pass the radix number.
28 |
--------------------------------------------------------------------------------
/contents/always-put-the-footer-at-the-bottom.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-06'
4 | tags: CSS
5 | title: Always put the footer at the bottom
6 | ---
7 |
8 | Assume that our page is organized by three parts: the header, main content and footer. It's a common layout that the footer is always displayed at the bottom no matter how long the main content is.
9 |
10 | ```html
11 |
12 |
13 | ...
14 | ...
15 |
16 |
17 |
18 | ```
19 |
20 | Using CSS flexbox, the layout can be implemented as following:
21 |
22 | ```css
23 | .container {
24 | display: flex;
25 | flex-direction: column;
26 | min-height: 100vh;
27 | }
28 |
29 | header,
30 | footer {
31 | flex-shrink: 0;
32 | }
33 |
34 | main {
35 | flex-grow: 1;
36 | }
37 | ```
38 |
39 | Setting `flex-grow: 1` to the main content will make it take the available spaces. The footer then [sticks at the bottom](https://phuoc.ng/collection/css-layout/sticky-footer/).
40 |
--------------------------------------------------------------------------------
/contents/always-use-noopener-or-noreferrer-for-links-opened-in-new-tabs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-16'
4 | tags: HTML
5 | title: Always use noopener or noreferrer for links opened in new tabs
6 | ---
7 |
8 | In order to open a link in a new tab, we use the `target="_blank"` attribute. However, it can lead to some issues if you aren't aware of them.
9 |
10 | First, the newly opened tab uses the same process with the opener one. Hence, it can slow down your page.
11 | More importantly, the new tab is able to access the `window` object of the opener page via the `window.opener` object. Imagine that the new tab executes the following code:
12 |
13 | ```js
14 | window.opener.location = 'http://fake.website.here';
15 | ```
16 |
17 | As the code implies, it redirects the original tab to a fake website. What happens if the fake website has the same UI as the real one? Since users already opened it, they may not realize that the website they are looking at isn't real.
18 |
19 | Adding `rel="noopener"` fixes the issues.
20 |
21 | It's good to know that there is the `rel="noreferrer"` attribute. It not only fixes the issues that `noopener` does, but also prevents the `Referer` header from being sent to the new tab.
22 |
23 | ```html
24 |
25 | ...
26 |
27 |
28 | ...
29 |
30 |
31 | ...
32 | ...
33 | ```
34 |
35 | > Some modern browsers, such as Chrome 88+, automatically adds the `noopener` behavior if it's missing.
36 | > However, it's still recommended to add `rel="noopener"` or `rel="noreferrer"` to avoid the security and performance issues in old legacy browsers.
37 |
--------------------------------------------------------------------------------
/contents/append-leading-zeros-to-ordered-list-items.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/append-leading-zeros.png
4 | created: '2021-03-16'
5 | tags: CSS
6 | title: Append leading zeros to ordered list items
7 | ---
8 |
9 | Setting the `list-style-type` property to the below value will append zero number to items of an ordered list (`ol`):
10 |
11 | ```css
12 | ol {
13 | list-style-type: decimal-leading-zero;
14 | }
15 | ```
16 |
17 | However, it only has effect with the items whose indices are less than 10. It means that if our list has more than 100 items, then they will be prefixed as following:
18 |
19 | ```shell
20 | 01. Item
21 | 02. Item
22 | ...
23 | 09. Item
24 | 10. Item
25 | ...
26 | 99. Item
27 | 100. Item
28 | ...
29 | ```
30 |
31 | In order to fix that issue, we can use the CSS counter. Each item holds the current value of the counter which is incremented by one in the next item:
32 |
33 | ```css
34 | ol {
35 | counter-reset: items;
36 | list-style-type: none;
37 | }
38 | li {
39 | counter-increment: items;
40 | }
41 | ```
42 |
43 | To prefix an item with its associate counter value, the `::before` pseudo element comes to the rescue.
44 |
45 | ```css
46 | li:before {
47 | content: '00' counter(items) '. ';
48 | }
49 | li:nth-child(n + 10)::before {
50 | content: '0' counter(items) '. ';
51 | }
52 | li:nth-child(n + 100)::before {
53 | content: counter(items) '. ';
54 | }
55 | ```
56 |
57 | The `:nth-child(n+10)` selector indicates the items whose indices are greater or equal to 10. It will override the styles applied for `li::before` elements.
58 | In the same way, `:nth-child(n+100)` overrides the styles of `:nth-child(n+10)`.
59 |
60 | ## See also
61 |
62 | - [Create a descending list of numbered items](https://phuoc.ng/collection/tips/create-a-descending-list-of-numbered-items/)
63 | - [Number headings and subheadings automatically](https://phuoc.ng/collection/tips/number-headings-and-subheadings-automatically/)
64 | - [Set a numbering type for a list element](https://phuoc.ng/collection/tips/set-a-numbering-type-for-a-list-element/)
65 | - [Style index numbers of list items](https://phuoc.ng/collection/tips/style-index-numbers-of-list-items/)
66 | - [Style list items with special characters](https://phuoc.ng/collection/tips/style-list-items-with-special-characters/)
67 | - [Use negative nth-child and nth-last-child](https://phuoc.ng/collection/tips/use-negative-nth-child-and-nth-last-child/)
68 |
--------------------------------------------------------------------------------
/contents/avoid-boolean-parameters.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/avoid-boolean-parameters.png
4 | created: '2021-05-13'
5 | tags: JavaScript
6 | title: Avoid boolean parameters
7 | ---
8 |
9 | Let's consider a situation where we have a function that writes a string to a file. It allows user to append the content to file, or override the content via the `override` parameter:
10 |
11 | ```js
12 | const writeToFile = (content: string, file: string, override: boolean) => {
13 | ...
14 | };
15 | ```
16 |
17 | With that signature, the function will be invoked as following
18 |
19 | ```js
20 | // Append the content to file
21 | writeToFile(content, file, true);
22 |
23 | // Override the file
24 | writeToFile(content, file, false);
25 | ```
26 |
27 | If you are not the one who creates the function, you have to question what the boolean value represents until looking at the implementation.
28 |
29 | It is worse if the function has a lot of boolean flags. Using boolean flags makes the core harder to read and maintain.
30 |
31 | There are a few ways to get rid of the issue.
32 |
33 | ## Provide explicit methods
34 |
35 | ```js
36 | appendToFile(content, file);
37 | overrideFile(content, file);
38 | ```
39 |
40 | ## Use an object parameter
41 |
42 | ```js
43 | writeToFile(content, file, { override });
44 | ```
45 |
46 | ## Use an enum
47 |
48 | If you're using TypeScript, then you can use `enum` to represent the possible values of a boolean flag.
49 |
50 | ```js
51 | enum SaveMode {
52 | Append,
53 | Override,
54 | }
55 |
56 | writeToFile(content, file, mode: SaveMode);
57 | ```
58 |
59 | It's confident for consumers to call the method:
60 |
61 | ```js
62 | writeToFile(content, file, SaveMode.Append);
63 |
64 | // Or
65 | writeToFile(content, file, SaveMode.Override);
66 | ```
67 |
68 | ## See also
69 |
70 | - [Manage multiple boolean flags](https://phuoc.ng/collection/tips/manage-multiple-boolean-flags/)
71 |
--------------------------------------------------------------------------------
/contents/avoid-invisible-text-when-loading-a-font.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-25'
4 | tags: CSS, Google Font
5 | title: Avoid invisible text when loading a font
6 | ---
7 |
8 | It takes time to load a big font. Most browsers will hide texts until the font is loaded completely. This problem is known as _flash of invisible text_ (FOIT).
9 |
10 | We can prevent it from happening by asking the browser to use the system font while the custom font is still being loaded. Once the font is loaded, it will replace the system font used earlier.
11 | This phrase is also known as _flash of unstyled text_ (FOUT).
12 |
13 | In order to archive it, we can use the `font-display` style:
14 |
15 | ```css
16 | @font-face {
17 | font-family: 'Roboto';
18 | font-display: swap;
19 | }
20 | ```
21 |
22 | If you are using [Google fonts](https://fonts.google.com), then putting the `display=swap` parameter is the equivalent way:
23 |
24 | ```html
25 |
26 | ```
27 |
28 | ## See also
29 |
30 | - [Combine Google font requests](https://phuoc.ng/collection/tips/combine-google-font-requests/)
31 | - [Load given characters in a Google font request](https://phuoc.ng/collection/tips/load-given-characters-in-a-google-font-request/)
32 |
--------------------------------------------------------------------------------
/contents/avoid-skipping-heading-levels.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/avoid-skip-headings.png
4 | created: '2021-05-13'
5 | tags: Accessibility, HTML
6 | title: Avoid skipping heading levels
7 | ---
8 |
9 | It's common to use the heading elements, `
` to `
`, to represent the heading of sections.
10 | The `
` tag is often used in the highest section, whereas the `
`, `
`, ... tags are used in the lower section.
11 |
12 | Heading elements are used not only because of their semantic meanings but also by screen reading tools. Those tools determine the content of the page based on the headings, and allow users to navigate between sections via the headings.
13 |
14 | It's recommended to keep the heading elements in the order, so the users won't be confused that there's a missing heading or section while navigating on the page.
15 |
16 | ```html
17 |
18 |
Main heading
19 |
Heading level 2
20 |
Heading level 3
21 |
22 |
23 |
Main heading
24 |
Heading level 2
25 |
Heading level 3
26 | ```
27 |
28 | ## See also
29 |
30 | - [Avoid using multiple `
` tags per page](https://phuoc.ng/collection/tips/avoid-using-multiple-h1-tags-per-page/)
31 |
--------------------------------------------------------------------------------
/contents/avoid-to-use-colons-and-periods-in-the-id-attribute.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-02-23'
4 | tags: DOM, HTML
5 | title: Avoid to use colons and periods in the id attribute
6 | ---
7 |
8 | According to the [HTML specifications](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute), a valid `id` can consist of almost characters except [ASCII whitespace](https://infra.spec.whatwg.org/#ascii-whitespace).
9 | Assume that we have an element representing an user's email address:
10 |
11 | ```html
12 |
13 | ```
14 |
15 | In order to access the element, the `getElementById()` method accepts all of three ways passing the `id`:
16 |
17 | ```js
18 | // They return the same element
19 | document.getElementById('user.email');
20 | document.getElementById('user\\.email');
21 | document.getElementById('user\\\\.email');
22 | ```
23 |
24 | But these methods return different results if you are using [jQuery](https://jquery.com) library:
25 |
26 | ```js
27 | // Function // Returned element
28 | $('#user.email'); //
29 | $('#user\\.email'); //
30 | $('#user\\\\.email'); //
31 | ```
32 |
33 | As you see, the first two methods will find an element with `id` of `user` and has `email` class.
34 | In order to get the correct element, we have to escape the `id` using double backslashes (`\\`). It also happens if we use the same value in CSS:
35 |
36 | ```css
37 | #user.email {
38 | ...;
39 | }
40 | ```
41 |
42 | All the styles declared within `#user.email { ... }` has effect on the element with `id` of `user` and has the `email` class.
43 |
44 | The styles aren't applied to element with `id` of `user.email`. To define the styles for our element, we have to escape the selector.
45 | But this time, it requires a _single_ backslash only:
46 |
47 | ```css
48 | #user\\.email {
49 | ...;
50 | }
51 | ```
52 |
53 | Avoid using the special characters in the `id` and `class` attributes will help us get rid of the confusion and errors above.
54 | If it's not possible to get rid of colons and periods (for example, the `id` attribute is generated by the server side), then you can use the single backslash as above, or use the attribute selector.
55 | Note that it has a lower specificity than the `id` selector:
56 |
57 | ```css
58 | [id='user.email'] {
59 | ...;
60 | }
61 | ```
62 |
63 | ## See also
64 |
65 | - [Do not use submit to name a submit button](https://phuoc.ng/collection/tips/do-not-use-submit-to-name-a-submit-button/)
66 |
--------------------------------------------------------------------------------
/contents/avoid-using-css-import.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-21'
4 | tags: CSS
5 | title: 'Avoid using CSS @import'
6 | ---
7 |
8 | The `@import` function allows us to include styles from an external file. It's very useful when our project has a lot of styles. Instead of creating a single file to define all styles, we can split them into multiple files and compose them in a master file.
9 |
10 | ```css
11 | /* The main file */
12 | @import 'common.css';
13 | @import 'components.css';
14 | @import 'pages.css';
15 | ...
16 | ```
17 |
18 | Using `@import` makes our styles more organized and easier to maintain. However, the browser has to download and parse each CSS file one by one before continuing rendering the page. The CSS files are downloaded sequentially instead of parallelly.
19 |
20 | It also can slow down the website depending on how many the number of CSS files are.
21 |
22 | There are a few ways to get rid of the issues while the styles are still organized.
23 |
24 | # Using CSS preprocessors
25 |
26 | We can use CSS preprocessors such as [Less](http://lesscss.org), [SASS](https://sass-lang.com). They not only provide the ability of using `@import` as normal CSS, but also merge styles in a single, final CSS file.
27 |
28 | # Using multiple link tags
29 |
30 | Each CSS can be downloaded by a separate `link` tag as following:
31 |
32 | ```html
33 |
34 |
35 |
36 | ...
37 |
38 | ```
39 |
40 | > **Good to know**
41 | >
42 | > In the old versions of Internet Explorer, the `@import` function behaves the same as the target CSS is inserted at the bottom of the page
43 |
--------------------------------------------------------------------------------
/contents/avoid-using-multiple-h1-tags-per-page.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/avoid-multiple-h1.png
4 | created: '2021-05-13'
5 | tags: HTML
6 | title: 'Avoid using multiple
tags per page'
7 | ---
8 |
9 | The `
` tag is often used at the top of the page to indicate the overall purpose of the page's content.
10 | It plays an important role in SEO because the search engines weigh it higher than other factors. It can be used to display in the search results as well.
11 |
12 | It's also good to know that in the old versions of HTML, HTML 4 specifically, allow one `
` tag per page. HTML 5 allows multiple `
` tags which each one can be used on each section.
13 |
14 | It means that using multiple `
` tags is valid in terms of HTML syntax, but it is not considered a best practice.
15 |
16 | ```html
17 |
18 |
Main heading
19 |
20 |
21 |
A section heading
22 |
23 |
24 |
25 |
Another section heading
26 |
27 |
28 |
29 |
Main heading
30 |
31 |
32 |
A section heading
33 |
34 |
35 |
36 |
Another section heading
37 |
38 | ```
39 |
40 | ## See also
41 |
42 | - [Avoid skipping heading levels](https://phuoc.ng/collection/tips/avoid-skipping-heading-levels/)
43 |
--------------------------------------------------------------------------------
/contents/avoid-using-the-b-i-s-and-u-tags.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/avoid-bisu-tags.png
4 | created: '2021-05-13'
5 | tags: Accessibility, HTML
6 | title: 'Avoid using the , , and tags'
7 | ---
8 |
9 | These tags are often used for styling purposes. It's recommended not to use them. Instead, use the semantic tags or CSS styles that provide the same appearances.
10 |
11 | | Tag | Recommended way |
12 | | ----- | ------------------------------- |
13 | | `` | `` |
14 | | `` | `` |
15 | | `` | `text-decoration: line-through` |
16 | | `` | `text-decoration: underline` |
17 |
18 | For more information about the differences between these tags , please take a look at [``, `` vs ``, ``](https://phuoc.ng/collection/this-vs-that/b-i-vs-strong-em/).
19 |
--------------------------------------------------------------------------------
/contents/capture-a-screenshot-without-shadow-on-macos.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-18'
4 | tags: macOS
5 | title: Capture a screenshot without shadow on macOS
6 | ---
7 |
8 | When taking a screenshot with the combination of cmd + shift + 4, macOS adds a shadow to the screenshot automatically.
9 |
10 | If you don't want to have the shadow, then after pressing the combination keys above, pressing and holding the alt + space keys.
11 | Then clicking the target window will produce a screenshot without the shadow.
12 |
13 | The shadowless screenshot below is an example when capturing the [1 LOC](https://phuoc.ng/collection/1-loc) series page:
14 |
15 | 
16 |
17 | ## See also
18 |
19 | - [Copy screenshots to the clipboard on macOS](https://phuoc.ng/collection/tips/copy-screenshots-to-the-clipboard-on-macos/)
20 | - [Move the screenshot area on macOS](https://phuoc.ng/collection/tips/move-the-screenshot-area-on-macos/)
21 |
--------------------------------------------------------------------------------
/contents/center-an-element-vertically-and-horizontally.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-17'
4 | tags: CSS
5 | title: Center an element vertically and horizontally
6 | ---
7 |
8 | There're different ways to [center](https://phuoc.ng/collection/css-layout/centering/) a given element in both directions.
9 |
10 | ## Using flexbox
11 |
12 | ```css
13 | .container {
14 | align-items: center;
15 | display: flex;
16 | flex-direction: column;
17 | justify-content: center;
18 | }
19 | ```
20 |
21 | ## Using grid
22 |
23 | ```css
24 | .container {
25 | display: grid;
26 | place-content: center;
27 | }
28 | ```
29 |
30 | ## Using positions
31 |
32 | We position the child element absolutely to the parent element.
33 |
34 | ```css
35 | .parent {
36 | position: relative;
37 | }
38 | .child {
39 | left: 50%;
40 | position: absolute;
41 | top: 50%;
42 | transform: translate(-50%, -50%);
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/contents/check-if-the-browser-supports-for-an-element-attribute.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-24'
4 | tags: DOM, JavaScript
5 | title: Check if the browser supports for an element attribute
6 | ---
7 |
8 | We can detect if the current browser supports for given attribute, `pattern` for example, as following:
9 |
10 | ```js
11 | const isPatternSupported = 'pattern' in document.createElement('input');
12 | ```
13 |
14 | If you would like to check for the value of attribute, it takes more steps. The sample code below determines whether the [native date input](https://phuoc.ng/collection/html-dom/check-if-the-native-date-input-is-supported/) is supported:
15 |
16 | ```js
17 | const isDateInputSupported = () => {
18 | // Create new input element
19 | const ele = document.createElement('input');
20 |
21 | // Set the type attribute
22 | ele.setAttribute('type', 'date');
23 |
24 | const invalidValue = 'not-a-valid-date';
25 |
26 | // Set an invalid value
27 | ele.setAttribute('value', invalidValue);
28 |
29 | return ele.value !== invalidValue;
30 | };
31 | ```
32 |
33 | If the browser supports the date input, invoking `setAttribute` with an invalid date won't have effect on the `value` attribute. As a result, `ele.value` will be an empty string.
34 |
35 | Otherwise, the input is treated as normal text input and `ele.value` returns the original value.
36 |
--------------------------------------------------------------------------------
/contents/checkout-the-previous-branch.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-07'
4 | tags: Git
5 | title: Checkout the previous branch
6 | ---
7 |
8 | It's common that we have multiple branches in the same Git repository. Each branch is used to fix a given bug, or implements a new feature.
9 |
10 | Switching between branches is one of popular tasks in our daily work:
11 |
12 | ```shell
13 | $ git checkout
14 | ```
15 |
16 | Without specifying the branch name, we can quickly checkout the previous branch with the following command:
17 |
18 | ```shell
19 | $ git checkout -
20 | ```
21 |
22 | ## See also
23 |
24 | - [Go to the previous directory](https://phuoc.ng/collection/tips/go-to-the-previous-directory/)
25 |
--------------------------------------------------------------------------------
/contents/combine-google-font-requests.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-22'
4 | tags: CSS, Google Font
5 | title: Combine Google font requests
6 | ---
7 |
8 | Usually we add separated links when loading different [Google font](https://fonts.google.com) as following:
9 |
10 | ```html
11 |
12 |
13 | ```
14 |
15 | The number of HTTP requests sent to Google can be increased depending on how many fonts we want to load. It can affect the page's loading time.
16 |
17 | It's recommended to combine the requests into a single one using the `|` character. The link might look like:
18 |
19 | ```html
20 |
21 | ```
22 |
23 | Note that the syntax is a little bit different when using the Google Font v2. It allows to pass multiple `family` parameters:
24 |
25 | ```html
26 |
30 | ```
31 |
32 | ## See also
33 |
34 | - [Avoid invisible text when loading a font](https://phuoc.ng/collection/tips/avoid-invisible-text-when-loading-a-font/)
35 | - [Load given characters in a Google font request](https://phuoc.ng/collection/tips/load-given-characters-in-a-google-font-request/)
36 |
--------------------------------------------------------------------------------
/contents/combine-styles-with-the-is-pseudo-class-selector.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-23'
4 | tags: CSS
5 | title: 'Combine styles with the :is pseudo-class selector'
6 | ---
7 |
8 | The `:is` pseudo-class selector applies the styles for any element that matches a selector listed in the arguments.
9 |
10 | Rather than writing separate selectors:
11 |
12 | ```css
13 | header a:hover,
14 | nav a:hover,
15 | footer a:hover {
16 | text-decoration: underline;
17 | }
18 | ```
19 |
20 | We can combine them into a single one as following:
21 |
22 | ```css
23 | :is(header, nav, footer) a:hover {
24 | text-decoration: underline;
25 | }
26 | ```
27 |
--------------------------------------------------------------------------------
/contents/compose-multiple-react-providers.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/compose-react-providers.png
4 | created: '2021-05-13'
5 | tags: React
6 | title: Compose multiple React providers
7 | ---
8 |
9 | Nowadays, it is common to use React's context at the root of an application to manage a shared state between any components.
10 |
11 | For example, checking whether or not the current user has logged in might be accomplished by the `AuthProvider` provider:
12 |
13 | ```js
14 | const App = () => {
15 | return (
16 |
17 | {...}
18 |
19 | );
20 | };
21 | ```
22 |
23 | Using multiple providers could make the code harder to read because there are a lot of nested components:
24 |
25 | ```js
26 | const App = () => {
27 | return (
28 |
29 |
30 |
31 |
32 | {...}
33 |
34 |
35 |
36 |
37 | );
38 | };
39 | ```
40 |
41 | The providers can be composed together by using the `reduce` function:
42 |
43 | ```js
44 | const compose = (providers) =>
45 | providers.reduce((Prev, Curr) => ({ children }) => (
46 |
47 | {children}
48 |
49 | ));
50 | ```
51 |
52 | The provider declarations in the root can be shorten as below:
53 |
54 | ```js
55 | const Provider = compose([
56 | Router,
57 | AuthProvider,
58 | ThemeProvider,
59 | LocalizationProvider,
60 | ]);
61 |
62 | const App = () => {
63 | return (
64 |
65 | {...}
66 |
67 | );
68 | };
69 | ```
70 |
--------------------------------------------------------------------------------
/contents/conditional-logging-in-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: DevTools
5 | title: Conditional logging in the Console
6 | ---
7 |
8 | Use `console.assert()` to print out something in the _Console_ when a given condition doesn't match.
9 |
10 | ```js
11 | if (condition) {
12 | // Do something
13 | } else {
14 | console.log('Error message');
15 | }
16 |
17 | // Better
18 | console.assert(!condition, 'Error message');
19 | ```
20 |
21 | ## See also
22 |
23 | - [Count how many times a function has been called](https://phuoc.ng/collection/tips/count-how-many-times-a-function-has-been-called/)
24 | - [Log a value to the Console](https://phuoc.ng/collection/tips/log-a-value-to-the-console/)
25 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
26 | - [Log a variable to the console using conditional breakpoints](https://phuoc.ng/collection/tips/log-a-variable-to-the-console-using-conditional-breakpoints/)
27 | - [Log an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/)
28 |
--------------------------------------------------------------------------------
/contents/convert-string-to-number.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-22'
4 | tags: JavaScript
5 | title: Convert string to number
6 | ---
7 |
8 | Instead of using the `Number()` constructor to convert a string to number, you can use the `+` operator:
9 |
10 | ```js
11 | +'010'; // 10
12 | +'2e1'; // 20
13 | +'0xF'; // 15
14 | ```
15 |
--------------------------------------------------------------------------------
/contents/copy-a-long-variable-from-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-23'
4 | tags: DevTools
5 | title: Copy a long variable from the Console
6 | ---
7 |
8 | The Chrome DevTools' Console will collapse and split a long variable into smaller ranges. The following screenshot shows how you see `console.log(range)` in the _Console_, where `range` is an array of numbers between 0 and 500.
9 |
10 | 
11 |
12 | It's not easy to see the full value or copy the content to clipboard. Here is the tip to do that with ease:
13 |
14 | - In the _Console_, right click the output and click _Store object as global variable_
15 | - _Console_ creates a temporary variable, `temp1` for example, that holds the variable value
16 | - Type `temp1 + ''` in the _Console_, and press the _Enter_ key
17 |
18 | _Console_ will try to convert the result to `String`. As a result, you get the full content of original variable.
19 |
20 | Here is the output which was omitted to fit in the screen:
21 |
22 | ```js
23 | "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,...,
24 | 490,491,492,493,494,495,496,497,498,499"
25 | ```
26 |
--------------------------------------------------------------------------------
/contents/copy-screenshots-to-the-clipboard-on-macos.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-14'
4 | tags: macOS
5 | title: Copy screenshots to the clipboard on macOS
6 | ---
7 |
8 | On macOS, when capturing a screenshot using the combination cmd + shift + 4 keys, the screenshot is then saved to the desktop.
9 | The desktop soon will contain a lot of temporary files, since the screenshots are mostly used once.
10 |
11 | In order to copy the screenshot directly to the clipboard rather then creating new file, we can press ctrl + cmd + shift + 4 when creating a screenshot.
12 | As soon as the screenshot is available in the clipboard, we can press ctrl + v to paste or upload it if the application suports.
13 |
14 | It's very helpful when we need the screenshot to report an issue, on GitHub for example, as you see in the following video.
15 |
16 |
19 |
20 | ## See also
21 |
22 | - [Capture a screenshot without shadow on macOS](https://phuoc.ng/collection/tips/capture-a-screenshot-without-shadow-on-macos/)
23 | - [Move the screenshot area on macOS](https://phuoc.ng/collection/tips/move-the-screenshot-area-on-macos/)
24 |
--------------------------------------------------------------------------------
/contents/copy-the-base64-data-of-an-image.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-04'
4 | tags: DevTools
5 | title: Copy the base64 data of an image
6 | ---
7 |
8 | If you want to copy the base64 data of an image, probably you will right-click on the associate element under the _Elements_ tab. However, there's no menu item there to copy the data URI.
9 |
10 | In fact, Chrome DevTools provides the ability of doing that but it's a little bit hidden.
11 |
12 | Under the _Sources_ tab, click the _Page_ tab and you will see all the images of the page listed in the `top > WEB-URL > image` branch where `WEB-URL` represents the website's URL.
13 |
14 | Clicking an image will show it up on the right side. The _Copy image as data URI_ functionality is available when you right-click the image.
15 |
16 | 
17 |
--------------------------------------------------------------------------------
/contents/copy-the-full-path-of-a-file-on-macos.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-06'
4 | tags: macOS
5 | title: Copy the full path of a file on macOS
6 | ---
7 |
8 | On macOS, we can copy the full path of a file by right-clicking the file, and move to the _Copy_ menu item of the context menu.
9 | Press and hold the _Alt_ key, then the menu item's label turns to _Copy ... as Pathname_.
10 |
11 | Clicking the menu item will save the full path to the clipboard.
12 |
13 |
16 |
--------------------------------------------------------------------------------
/contents/copy-variables-from-the-browser-console-to-the-clipboard.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2023-10-04'
4 | description: Copy variables from the browser console to the clipboard
5 | openGraphCover: /og/tips/copy-variables-browser-console.png
6 | tags: Browser DevTools
7 | title: Copy variables from the browser console to the clipboard
8 | ---
9 |
10 | If you work with the browser console often, you might need to copy a variable's value to the clipboard from time to time. Luckily, you can easily do this using the `copy()` function.
11 |
12 | Not only is it convenient, but using `copy()` also has several advantages over manual copying. For example, copying the values of large or complex objects using the console is faster and more accurate than manual selection. Additionally, some variables might contain invisible or hard-to-select characters, such as non-breaking spaces. With `copy()`, you can ensure that you copy the exact value of the variable without any unintended changes. Lastly, if you need to copy a value from a script that runs too quickly to select manually, `copy()` is often your only option.
13 |
14 | ## How to use the copy() function
15 |
16 | The `copy()` function is a built-in function in the browser console. It allows you to copy the value of a variable to the clipboard. To use this function, simply execute it with the variable as its argument.
17 |
18 | Here's an example:
19 |
20 | ```js
21 | const myVariable = "Hello, world!";
22 | copy(myVariable);
23 | ```
24 |
25 | When you run this code in the browser console, the value of `myVariable` gets copied to the clipboard. You can then paste this value into any other application you'd like.
26 |
27 | ## Limitations
28 |
29 | It's important to know that the `copy()` function is only available in modern browsers. If you're using an older browser, you'll need to use a different method to copy the values of your variables to the clipboard.
30 |
31 | Also, some browsers may limit the use of the `copy()` function for security reasons. If you run into this issue, you can adjust your browser settings or try another method to copy the values of your variables.
32 |
33 | In conclusion, the `copy()` function is a handy tool for copying variable values from the browser console to the clipboard. With this function, you can easily transfer data between the console and other applications.
34 |
--------------------------------------------------------------------------------
/contents/count-how-many-times-a-function-has-been-called.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2022-09-27'
4 | tags: JavaScript
5 | title: Count how many times a function has been called
6 | ---
7 |
8 | If you want to count how many times a function has been called, you probably think of a global variable:
9 |
10 | ```js
11 | let counter = 0;
12 |
13 | const expensiveFunctionToDebug = () => {
14 | counter++;
15 | console.log(`This function has been called: ${counter}`);
16 |
17 | // Function body ...
18 | };
19 | ```
20 |
21 | We simply increase the counter by 1 and log the latest value whenever the `expensiveFunctionToDebug` function is invoked. `console` has a great method to do the same thing:
22 |
23 | ```js
24 | const expensiveFunctionToDebug = () => {
25 | console.count('expensiveFunctionToDebug');
26 |
27 | // Function body ...
28 | };
29 | ```
30 |
31 | In the _Console_ tab of browsers, you will see something as following:
32 |
33 | ```shell
34 | expensiveFunctionToDebug: 1
35 | expensiveFunctionToDebug: 2
36 | ...
37 | expensiveFunctionToDebug: 10
38 | ```
39 |
40 | The `console.count` function prints the label you pass to it followed by the number of times the function is executed.
41 |
42 | ## See also
43 |
44 | - [Conditional logging in the Console](https://phuoc.ng/collection/tips/conditional-logging-in-the-console/)
45 | - [Log a value to the Console](https://phuoc.ng/collection/tips/log-a-value-to-the-console/)
46 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
47 | - [Log a variable to the console using conditional breakpoints](https://phuoc.ng/collection/tips/log-a-variable-to-the-console-using-conditional-breakpoints/)
48 | - [Log an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/)
49 |
--------------------------------------------------------------------------------
/contents/create-a-big-file-on-linux.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/create-big-file.png
4 | created: '2021-05-17'
5 | tags: Command Line
6 | title: Create a big file on Linux
7 | ---
8 |
9 | Sometimes we need to have a random file of given size. We can name some common examples such as
10 |
11 | - Test the file upload functionality
12 | - Test if the application is able to handle a large set of data
13 |
14 | On Linux we can create an empty file of particular size with the `dd` command. The following command creates `sample.txt` file of `1GB`:
15 |
16 | ```shell
17 | $ dd if=/dev/urandom of=sample.txt bs=1G count=1
18 | ```
19 |
20 | The file size is the result of `bs * count` in bytes.
21 |
22 | ## See also
23 |
24 | - [Create a file of any size](https://phuoc.ng/collection/tips/create-a-file-of-any-size/)
25 |
--------------------------------------------------------------------------------
/contents/create-a-custom-emoji-cursor.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-04-12'
4 | tags: CSS
5 | title: Create a custom emoji cursor
6 | ---
7 |
8 | There are two popular ways to create a custom cursor:
9 |
10 | - Using an image
11 | - Creating a canvas element and generate the base 64 image
12 |
13 | Both approaches finally change the cursor by setting the image's URL to the `cursor` property:
14 |
15 | ```css
16 | .custom-cursor {
17 | cursor: url(/path/to/image.png), auto;
18 | }
19 |
20 | /* Or */
21 | .custom-cursor {
22 | cursor: url('data:image/png;base64,...'), auto;
23 | }
24 | ```
25 |
26 | To create a custom emoji cursor, we can use an inline SVG element which displays the emoji at the center as following:
27 |
28 | ```css
29 | .custom-cursor {
30 | cursor: url('data:image/svg+xml;utf8,')
31 | 16 0, auto;
32 | }
33 | ```
34 |
35 |
36 | ```html
37 |
Let's fly!
38 | ```
39 |
40 | ```css
41 | .demo__cursor {
42 | /* Custom cursor */
43 | cursor: url('data:image/svg+xml;utf8,')
44 | 16 0,
45 | auto;
46 | /* Center the content */
47 | align-items: center;
48 | display: flex;
49 | justify-content: center;
50 | /* Size */
51 | height: 16rem;
52 | width: 16rem;
53 | /* Misc */
54 | border: 1px solid rgba(0, 0, 0, 0.2);
55 | }
56 | ```
57 |
58 |
--------------------------------------------------------------------------------
/contents/create-a-descending-list-of-numbered-items.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/descending-list.png
4 | created: '2021-02-24'
5 | tags: HTML
6 | title: Create a descending list of numbered items
7 | ---
8 |
9 | By default, an ordered list element (`ol` tag) will display the number for each item in the ascending order.
10 | The first item is shown with the number 1. The second item comes with number 2, and so forth.
11 |
12 | There are some cases that we would like to reverse the order of numbers, but the actual items are kept.
13 | Coundowns such as top 5 movies, top 10 songs, etc., are the best examples for the use case.
14 |
15 | To do that, we just simply add the `reversed` attribute:
16 |
17 | ```html
18 |
19 |
...
20 |
...
21 |
...
22 |
23 | ```
24 |
25 | > You can use the `start` attribute which indicates the start number
26 |
27 | ## Demo
28 |
29 | _Top 10 movies rated on [IMDB](https://www.imdb.com/chart/top)_
30 |
31 |
32 |
The Lord of the Rings: The Fellowship of the Ring (2001)
33 |
The Good, the Bad and the Ugly (1966)
34 |
Pulp Fiction (1994)
35 |
The Lord of the Rings: The Return of the King (2003)
36 |
Schindler's List (1993)
37 |
12 Angry Men (1957)
38 |
The Dark Knight (2008)
39 |
The Godfather: Part II (1974)
40 |
The Godfather (1972)
41 |
The Shawshank Redemption (1994)
42 |
43 |
44 | ## See also
45 |
46 | - [Append leading zeros to ordered list items](https://phuoc.ng/collection/tips/append-leading-zeros-to-ordered-list-items/)
47 | - [Number headings and subheadings automatically](https://phuoc.ng/collection/tips/number-headings-and-subheadings-automatically/)
48 | - [Set a numbering type for a list element](https://phuoc.ng/collection/tips/set-a-numbering-type-for-a-list-element/)
49 | - [Style index numbers of list items](https://phuoc.ng/collection/tips/style-index-numbers-of-list-items/)
50 | - [Style list items with special characters](https://phuoc.ng/collection/tips/style-list-items-with-special-characters/)
51 |
--------------------------------------------------------------------------------
/contents/create-a-download-link.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-01'
4 | tags: HTML
5 | title: Create a download link
6 | ---
7 |
8 | It's common to use an `a` tag to create a download link. The browser will open the file for some well-supported file types, such as image, pdf, etc.
9 |
10 | If you want to enforce the browser to download the file, then use the HTML 5 `download` attribute. The value of attribute, if specified, will be used as the name of download file.
11 |
12 | ```html
13 |
14 |
15 |
16 |
18 | ```
19 |
20 | > It's worth noting that the `download` attribute only works if the file belongs to the same domain as the current website. It doesn't have effect if the `href` attribute has the different origin to the site
21 |
--------------------------------------------------------------------------------
/contents/create-a-file-of-any-size.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | contributors:
4 | - dylankenneally
5 | created: '2021-03-19'
6 | tags: macOS
7 | title: Create a file of any size
8 | updated: '2022-09-08'
9 | ---
10 |
11 | Sometimes we need big files for testing purposes, without any concern for the files' content. For example, when testing uploads, downloads & responding to changes in network performance, we are likely to want files of various sizes.
12 |
13 | macOS comes with a helpful utility command to create a file of any size over 512 bytes called `mkfile`:
14 |
15 | ```shell
16 | $ mkfile FILE_SIZE FILE_NAME
17 | ```
18 |
19 | In the `FILE_SIZE` option, we can optionally use a suffixes to note the unit of the value `b` - bytes (the deafult), `k` - kilobytes, `m` - megabytes or `g` - gigabytes.
20 |
21 | For example, the following command produces an empty two gigabyte file named `big-file.ext`:
22 |
23 | ```shell
24 | $ mkfile 2g big-file.ext
25 | ```
26 |
27 | Note the following multiplication factors are applied to the `FILE_SIZE` value:
28 |
29 | | Unit | Factor | Examples |
30 | | ---- | ---------- | ------------------------------------------------------------------------ |
31 | | `b` | 512 | For 512 bytes: `1b`, for 1kb (1024 bytes): `2b` |
32 | | `k` | 1024 | For 1kb (1024 bytes): `1k`, for 1mb (1,048,576 bytes): `1024k` |
33 | | `m` | 1048576 | For 1mb (1,048,576 bytes): `1m`, for 1gb (1,073,741,824 bytes): `1024m` |
34 | | `g` | 1073741824 | For 1mb (1,073,741,824 bytes): `1g`, for 2gb (2,147,483,648 bytes): `1g` |
35 |
36 | See the man page for `mkfile` for more information.
37 |
38 | ```shell
39 | $ man mkfile
40 | ```
41 |
42 | ## See also
43 |
44 | - [Create a big file on Linux](https://phuoc.ng/collection/tips/create-a-big-file-on-linux/)
45 |
--------------------------------------------------------------------------------
/contents/create-a-function-that-accepts-a-single-parameter.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | contributors:
4 | - dylankenneally
5 | created: '2021-03-15'
6 | tags: JavaScript
7 | title: Create a function that accepts a single parameter
8 | updated: '2022-09-08'
9 | ---
10 |
11 | Quite often, we use the `map` function to transform each item of an array into a new one. However, it's common to see an issue if we don't pass the parameter to the mapper function.
12 |
13 | For example, the following code converts each item of array into a number:
14 |
15 | ```js
16 | ['1', '2', '3', '4', '5'].map((v) => parseInt(v));
17 |
18 | // [1, 2, 3, 4, 5]
19 | ```
20 |
21 | However, the result isn't correct if we shorten it as below:
22 |
23 | ```js
24 | ['1', '2', '3', '4', '5'].map(parseInt);
25 |
26 | // [1, NaN, NaN, NaN, NaN]
27 | ```
28 |
29 | The issue is caused by the fact that the mapper function accepts three parameters which are the array item, index, and the array.
30 | Calling `.map(parseInt)` means that we pass the item index to `parseInt` as the second parameter. As a result, we will see `NaN`.
31 |
32 | This leads to a requirement of building a function that [accepts only the first parameter](https://phuoc.ng/collection/1-loc/create-a-function-that-accepts-a-single-argument/), and ignore the remaining parameters.
33 |
34 | ```js
35 | const unary = (fn) => (params) => fn(params);
36 | ```
37 |
38 | The `unary` function creates a wrapper of a function, and ignores all parameters except the first one. With that function in our hand, we can pass the mapper to the `map` function like this:
39 |
40 | ```js
41 | ['1', '2', '3', '4', '5'].map(unary(parseInt));
42 |
43 | // [1, 2, 3, 4, 5]
44 | ```
45 |
46 | ## See also
47 |
48 | - [Accept any numbers of parameters](https://phuoc.ng/collection/tips/accept-any-numbers-of-parameters/)
49 |
--------------------------------------------------------------------------------
/contents/create-a-line-on-sides-heading.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-03'
4 | tags: CSS
5 | title: 'Create a line-on-sides heading'
6 | ---
7 |
8 | A heading whose left and right sides are horizontal lines can be structured as a grid with three columns:
9 |
10 | ```css
11 | .heading {
12 | display: grid;
13 | grid-template-columns: 1fr auto 1fr;
14 | grid-gap: 1rem;
15 | text-align: center;
16 | }
17 | ```
18 |
19 | `1fr auto 1fr` indicates that the left and right columns will have the same widths and they'll take the remaining spaces.
20 |
21 | We can use the `::before` and `::after` pseudo elements to represent the left and right sides of the heading respectively:
22 |
23 | ```css
24 | .heading::before,
25 | .heading::after {
26 | align-self: center;
27 | border-top: 0.25rem double #e5e7eb;
28 | content: '';
29 | }
30 | ```
31 |
32 |
33 | ```html
34 |
Front-end Tips
35 | ```
36 |
37 | ```css
38 | .demo__heading {
39 | display: grid;
40 | grid-template-columns: 1fr auto 1fr;
41 | grid-gap: 1rem;
42 | text-align: center;
43 | }
44 | .demo__heading::before,
45 | .demo__heading::after {
46 | align-self: center;
47 | border-top: 0.25rem double #e5e7eb;
48 | content: '';
49 | }
50 | ```
51 |
52 |
--------------------------------------------------------------------------------
/contents/create-a-multiline-strings.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-13'
4 | tags: JavaScript
5 | title: Create a multiline strings
6 | ---
7 |
8 | To create a multiline strings, the most common way is to concatenate them as shown below:
9 |
10 | ```js
11 | const multilineStrings = 'This is a\n' + 'multiline\n' + 'strings';
12 | ```
13 |
14 | Joining an array of strings is another approach:
15 |
16 | ```js
17 | const multilineStrings = ['This is a', 'multiline', 'strings'].join('\n');
18 | ```
19 |
20 | ES6 introduces an easier way to do that. It uses the template literal which is delimited by backticks:
21 |
22 | ```js
23 | const multilineStrings = `This is a
24 | multiline
25 | strings`;
26 | ```
27 |
28 | ## See also
29 |
30 | - [Get rid of escaping quotes with template literal](https://phuoc.ng/collection/tips/get-rid-of-escaping-quotes-with-template-literal/)
31 | - [Prevent a string from being escaped](https://phuoc.ng/collection/tips/prevent-a-string-from-being-escaped/)
32 | - [Use template literal to concatenate strings](https://phuoc.ng/collection/tips/use-template-literal-to-concatenate-strings/)
33 |
--------------------------------------------------------------------------------
/contents/create-a-polyfill.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-10'
4 | tags: JavaScript
5 | title: Create a polyfill
6 | ---
7 |
8 | Due to the fact that JavaScript APIs have their own specifications, not all the browsers support a particular specification at the same time.
9 | A JavaScript API can be implemented in a browser sooner or later than the other browsers.
10 |
11 | Because of that, we have to provide a _patch_ version of API to make sure that it still works on browsers that don't support it natively. That kind of patch is called _polyfill_.
12 |
13 | The following sample code provides a patch for the `startsWith` method which doesn't override the API if it exists:
14 |
15 | ```js
16 | if (!String.prototype.startsWith) {
17 | String.prototype.startsWith = function (searchString) {
18 | // The implementation
19 | // return `true` or `false`
20 | };
21 | }
22 | ```
23 |
24 | If we provide a polyfill as a library, then we can use the following approach:
25 |
26 | ```js
27 | // The polyfill implementation
28 | const startsWithPolyfill = function (searchString) {
29 | // ...
30 | // return `true` or `false`
31 | };
32 |
33 | const startsWith = String.prototype.startsWith || startsWithPolyfill;
34 |
35 | // Export the function
36 | export default startsWith;
37 | ```
38 |
--------------------------------------------------------------------------------
/contents/create-an-array-with-conditional-elements.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-12'
4 | tags: JavaScript
5 | title: Create an array with conditional elements
6 | ---
7 |
8 | There are a few ways to initialize an array with conditional elements.
9 |
10 | ## Using the `push` function
11 |
12 | ```js
13 | const arr = ['foo', 'bar'];
14 | if (condition) {
15 | arr.push('fuzz');
16 | }
17 | ```
18 |
19 | We can use the [short-circuits conditionals](https://phuoc.ng/collection/tips/use-short-circuits-conditionals/) to make it shorter:
20 |
21 | ```js
22 | condition && arr.push('fuzz');
23 | ```
24 |
25 | ## Using the `concat` function
26 |
27 | ```js
28 | const arr = ['foo', 'bar'].concat(condition ? 'fuzz' : []);
29 | ```
30 |
31 | The `push` and `concat` functions only work if we want to insert the elements at the end of the array.
32 |
33 | ## Filtering the falsy values
34 |
35 | ```js
36 | const arr = ['foo', condition ? 'fuzz' : undefined, 'bar'].filter(Boolean);
37 | ```
38 |
39 | The array always inserts new item which can be the new element or `undefined` depending on whether or not the condition matches.
40 | At the end, the `undefined` items are removed from the array.
41 |
42 | This approach allows us to insert element at any position, however we have to be careful with [removing the _falsy_ values](https://phuoc.ng/collection/1-loc/remove-falsy-values-from-array/) from the array because the original array can contains the falsy items.
43 |
44 | ## Tip: using the spread operator
45 |
46 | By using the spread operator (`...`), we can create an array with a single expression as following:
47 |
48 | ```js
49 | const arr = ['foo', ...(condition ? ['fuzz'] : []), 'bar'];
50 | ```
51 |
52 | The following code demonstrates a simple use case. It adds a product to the cart on an ecommerce website.
53 |
54 | To do that, we have to check if the `products` property exists on the `cart` object first. If not, it will create an empty `products`. The conditional elements are the current `products`.
55 |
56 | ```js
57 | if (!cart['products']) {
58 | cart['products'] = [];
59 | }
60 | cart['products'].push(product);
61 |
62 | // Better version
63 | cart['products'] = [...(cart['products'] || []), product];
64 | ```
65 |
66 | Do you want to see one more advanced example? The function below uses the spread and [comma operator](https://phuoc.ng/collection/tips/shorten-codes-with-the-comma-operator/) to [group an array of objects by a key](https://phuoc.ng/collection/1-loc/group-an-array-of-objects-by-a-key/):
67 |
68 | ```js
69 | const groupBy = (arr, key) => arr.reduce((a, i) => ((a[i[key]] = [...(a[i[key]] || []), i]), a), {});
70 | ```
71 |
72 | ## See also
73 |
74 | - [Include properties conditionally](https://phuoc.ng/collection/tips/include-properties-conditionally/)
75 |
--------------------------------------------------------------------------------
/contents/create-an-autocomplete-list-with-the-datalist-element.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: HTML
5 | title: Create an autocomplete list with the datalist element
6 | ---
7 |
8 | The HTML 5 `datalist` element comes in handly when you want to create an autocomplete input from a pre-defined list.
9 |
10 | The element generates a dropdown that allow user to choose its options. In comparison to the normal `select` element, the `datalist` element provide the ability of filter the options.
11 |
12 | Users also can enter what they want in the same way the normal text box does.
13 |
14 | ```html
15 |
16 |
22 | ```
23 |
24 |
25 | ```html
26 |
Choose your favorite color:
27 |
28 |
38 | ```
39 |
40 |
--------------------------------------------------------------------------------
/contents/create-an-object-with-dynamic-keys.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-25'
4 | tags: JavaScript
5 | title: Create an object with dynamic keys
6 | ---
7 |
8 | We often use the bracket notation to add a dynamic key to an object.
9 |
10 | ```js
11 | const key = 'ages';
12 | const person = {
13 | name: 'John Doe',
14 | };
15 |
16 | person[key] = 42;
17 | ```
18 |
19 | ES6 allows us to do that in a declarative way as following:
20 |
21 | ```js
22 | const key = 'ages';
23 | const person = {
24 | name: 'John Doe',
25 | [key]: 42,
26 | };
27 | ```
28 |
29 | Here is a simple usage. The sample code below returns the list of name and value of given input fields in a form:
30 |
31 | ```js
32 | // `formEle` is the form element
33 | const data = [...formEle.querySelectorAll('input')].map((field) => {
34 | return {
35 | [field.getAttribute('name')]: field.getAttribute('value'),
36 | };
37 | });
38 | ```
39 |
--------------------------------------------------------------------------------
/contents/create-an-one-time-event-handler.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-24'
4 | tags: DOM, JavaScript
5 | title: 'Create an one-time event handler'
6 | ---
7 |
8 | Sometimes we want a given event of an element to happen once. Usually, it can be done by attaching a handler which removes itself:
9 |
10 | ```js
11 | const handler = (e) => {
12 | // Do something ...
13 | element.removeEventListener('click', handler);
14 | };
15 |
16 | element.addEventListener('click', handler);
17 | ```
18 |
19 | We can use a named function expression to shorten the code a little bit:
20 |
21 | ```js
22 | element.addEventListener('click', function handler(e) {
23 | // Do something ...
24 |
25 | // Remove the handler
26 | e.currentTarget.removeEventListener(e.type, handler);
27 | });
28 | ```
29 |
30 | However, the [modern browsers](https://caniuse.com/once-event-listener) provide the new `once` option that makes things easier. We don't have to track the reference of the handler anymore.
31 |
32 | ```js
33 | element.addEventListener(
34 | 'click',
35 | (e) => {
36 | // Do something ...
37 | },
38 | {
39 | once: true,
40 | }
41 | );
42 | ```
43 |
--------------------------------------------------------------------------------
/contents/disable-all-fields-of-a-form.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/disabled-fieldset.png
4 | created: '2022-09-27'
5 | tags: HTML
6 | title: Disable all fields of a form
7 | ---
8 |
9 | It's easy to disable a particular HTML input or button element by using the `disabled` attribute.
10 | The following piece of code demonstrates a simple signing in form that consists of two disabled inputs:
11 |
12 | ```html
13 |
14 | ```
15 |
16 | There is the other way that you don't have to apply the `disabled` attribute to fields one by one.
17 |
18 | Instead, you can use the `fieldset` element to organize the input. Using the `disabled` attribute to the `fieldset` will disable all input belonging to it:
19 |
20 | ```html
21 |
27 | ```
28 |
--------------------------------------------------------------------------------
/contents/display-links-in-the-print-mode.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-25'
4 | tags: CSS
5 | title: Display links in the print mode
6 | ---
7 |
8 | When users print a web page, they will not see the actual links. It would be more useful if a link displays both the text and its link.
9 |
10 | We can do it by including the link in the `:after` element:
11 |
12 | ```css
13 | @media print {
14 | a::after {
15 | content: ' (' attr(href) ') ';
16 | }
17 | }
18 | ```
19 |
20 | In the print mode, users will see the link included right after its content:
21 |
22 | ```html
23 |
24 | Front-End Tips
25 |
26 |
27 | Front-End Tips (https://phuoc.ng/collection/tips)
28 | ```
29 |
--------------------------------------------------------------------------------
/contents/do-not-add-custom-methods-to-primitive-objects.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-02-22'
4 | tags: JavaScript
5 | title: Do not add custom methods to primitive objects
6 | ---
7 |
8 | It is not recommended to add a custom method to primitive objects such as `Array`, `Boolean`, `Number`, `String`, etc.
9 | Since the `for ... in` statement loops over the enumerable properties, it will include new methods which are added to the prototype.
10 |
11 | ```js
12 | Array.prototype.isEmpty = function () {
13 | return (this.length = 0);
14 | };
15 |
16 | const a = ['cat', 'dog', 'mouse'];
17 | for (let i in a) {
18 | console.log(i); // '0', '1', '2', 'isEmpty'
19 | }
20 | ```
21 |
--------------------------------------------------------------------------------
/contents/do-not-mix-styles-of-an-element-with-its-container.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/do-not-mix-styles.png
4 | created: '2021-05-12'
5 | tags: CSS
6 | title: Do not mix styles of an element with its container
7 | ---
8 |
9 | When developing a reusable component, we should follow the rule: the component should look like the same regardless where it's placed.
10 |
11 | For example, the `margin` style in the following CSS is used for a specific use case where we want to have a space between `.item` and its sibling element.
12 |
13 | ```css
14 | .item {
15 | margin: 1rem 0;
16 |
17 | /* Other styles */
18 | ...;
19 | }
20 | ```
21 |
22 | It isn't recommended because it could be broken when you use `item` in another container. Instead, you should use a wrapper to separate the styles of element and its container:
23 |
24 | ```css
25 | .item-wrapper {
26 | margin: 1rem 0;
27 | }
28 | ```
29 |
30 | The `item` class can be reused in different places without breaking the look and feel:
31 |
32 | ```html
33 |
34 |
...
35 |
36 |
37 |
38 |
39 |
...
40 |
41 | ```
42 |
--------------------------------------------------------------------------------
/contents/do-not-use-magic-numbers-when-manipulating-strings.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-09'
4 | tags: JavaScript
5 | title: Do not use magic numbers when manipulating strings
6 | ---
7 |
8 | It's quite often that we pass the index parameter when using string manipulation functions such as `slice`, `splice`, `substr` or `substring`.
9 |
10 | Let's take a look at a simple example which [converts a given date](https://phuoc.ng/collection/1-loc/convert-a-date-to-yyyy-mm-dd-format/) object to _YYYY-MM-DD_ format.
11 |
12 | Given a `date` object, we can get the string representing the date in the ISO format:
13 |
14 | ```js
15 | date.toISOString();
16 | // '2021-03-09T01:42:46.002Z'
17 | ```
18 |
19 | The output is constructed by different parts which starts with the full date, `2021-03-09` as we see in the example above.
20 | So, it's easy to get the full date by extracting it from the ISO format:
21 |
22 | ```js
23 | const format = (date) => date.toISOString().slice(0, 10);
24 | ```
25 |
26 | `10` is called a magic number, because when the other people look at the code, they don't have idea where the number comes from.
27 |
28 | Rather than using a magic number, it's recommended to explain what it is and how it's calculated:
29 |
30 | ```js
31 | // The better version
32 | const FULL_DATE_LENGTH = 'YYYY-MM-DD'.length;
33 |
34 | const format = (date) => date.toISOString().slice(0, FULL_DATE_LENGTH);
35 | ```
36 |
37 | ## See also
38 |
39 | - [Keep the calculation of a magic number](https://phuoc.ng/collection/tips/keep-the-calculation-of-a-magic-number/)
40 |
--------------------------------------------------------------------------------
/contents/do-not-use-submit-to-name-a-submit-button.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-04'
4 | tags: HTML
5 | title: Do not use submit to name a submit button
6 | ---
7 |
8 | Given a form element, we often call the `submit()` method to submit the form after validating its fields.
9 |
10 | If the submit button of the form has either `name="submit"` or `id="submit"` attribute, then `formEle.submit` will return the submit button instance.
11 | As a result, `formEle.submit()` throws an exception because it's not an actual function anymore.
12 |
13 | We can face the similar issue when using special properties of form such as `reset`, `length`, `method`.
14 |
15 | ```html
16 |
17 |
18 |
19 |
20 |
21 |
22 | ```
23 |
24 | ## See also
25 |
26 | - [Avoid to use colons and periods in the id attribute](https://phuoc.ng/collection/tips/avoid-to-use-colons-and-periods-in-the-id-attribute/)
27 |
--------------------------------------------------------------------------------
/contents/early-return.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-21'
4 | tags: JavaScript
5 | title: Early return
6 | ---
7 |
8 | Using the `if` statement is a common technique to deal with conditional logics. The code flow is split into different branches based on a given logic.
9 |
10 | Let's take a look at a simple function that [suffixes a given hour](https://phuoc.ng/collection/1-loc/add-am-pm-suffix-to-an-hour/) number with _am_ or _pm_. The suffix is determined based on which range the hour belongs to as you can see in the following table:
11 |
12 | | Hour | With suffix |
13 | | ------- | ----------- |
14 | | 0 | 12am |
15 | | 1 - 11 | 1am - 11am |
16 | | 12 | 12pm |
17 | | 13 - 23 | 1pm - 11pm |
18 |
19 | An initial implementation of the function could look like:
20 |
21 | ```js
22 | const suffixAmPm = (hour) => {
23 | if (hour === 0) {
24 | return '12am';
25 | } else {
26 | if (hour < 12) {
27 | return `${hour}am`;
28 | } else {
29 | if (hour === 12) {
30 | return '12pm';
31 | } else {
32 | return `${hour % 12}pm`;
33 | }
34 | }
35 | }
36 | };
37 | ```
38 |
39 | Imagine how the code looks like if we use multiple nested `if` statements. It's very hard to follow and maintain. Rather than using `else` or nested `if` statements, the function can return as soon as the condition matches:
40 |
41 | ```js
42 | const fn = (args) => {
43 | if (condition) {
44 | return 'foo';
45 | } else {
46 | // Long implementation
47 | return 'bar';
48 | }
49 | };
50 |
51 | // Better
52 | const fn = (args) => {
53 | if (condition) {
54 | return 'foo';
55 | }
56 |
57 | // Long implementation
58 | // Don't need to wrap within an `else`
59 | return 'bar';
60 | };
61 | ```
62 |
63 | Using this practice, a new version of the `suffixAmPm` function looks like:
64 |
65 | ```js
66 | const suffixAmPm = (hour) => {
67 | if (hour === 0) {
68 | return '12am';
69 | }
70 |
71 | if (hour < 12) {
72 | return `${hour}am`;
73 | }
74 |
75 | if (hour === 12) {
76 | return '12pm';
77 | }
78 |
79 | return `${hour % 12}pm`;
80 | };
81 | ```
82 |
83 | ## See also
84 |
85 | - [Replace multiple if statements with a lookup table](https://phuoc.ng/collection/tips/replace-multiple-if-statements-with-a-lookup-table/)
86 | - [Replace multiple if statements with a single switch statement](https://phuoc.ng/collection/tips/replace-multiple-if-statements-with-a-single-switch-statement/)
87 | - [Use Array.includes for multiple conditionals](https://phuoc.ng/collection/tips/use-array-includes-for-multiple-conditionals/)
88 |
--------------------------------------------------------------------------------
/contents/enforce-required-parameters.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-24'
4 | tags: JavaScript
5 | title: Enforce required parameters
6 | ---
7 |
8 | In ES6, the default value of a parameter is evaluated if the parameter is missing. It allows us to enfore that a given parameter is required.
9 |
10 | ```js
11 | const required = () => {
12 | throw new Error('Missing parameter');
13 | };
14 |
15 | const getAges = (yearOfBirth = required()) => new Date().getFullYear() - yearOfBirth;
16 | ```
17 |
18 | Calling `getAges()` without parameter will throw the exception `Missing parameter`.
19 |
--------------------------------------------------------------------------------
/contents/escape-css-class-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/escape-css-class.png
4 | created: '2021-05-17'
5 | tags: CSS
6 | title: Escape CSS class names
7 | ---
8 |
9 | CSS class names can't contain the `:` character. For example, it's not possible to declare the following class in CSS:
10 |
11 | ```css
12 | .lg:flex {
13 | ...;
14 | }
15 | ```
16 |
17 | However we can use the `\` character to correct it:
18 |
19 | ```css
20 | .lg\:flex {
21 | ...;
22 | }
23 | ```
24 |
25 | The class name is usable in HTML as usual:
26 |
27 | ```html
28 |
...
29 | ```
30 |
31 | Using `\` to escape CSS class names has been used a lot in some CSS frameworks such as [Tailwind](https://tailwindcss.com).
32 |
--------------------------------------------------------------------------------
/contents/filter-file-types-of-a-file-input.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/file-accept.png
4 | created: '2021-03-01'
5 | tags: HTML
6 | title: Filter file types of a file input
7 | ---
8 |
9 | By default, when clicking a file input (``) the browser will list all files in the destination folder.
10 |
11 | The file input provides the [accept attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept) which hints the browser to list particular file types. The attribue value can be one or many file extensions, MIME types, separated by a comma.
12 |
13 | For example, the input asking users for choosing an avatar looks like:
14 |
15 | ```html
16 |
17 |
18 |
19 |
20 | ```
21 |
22 | > The `accept` attribute does NOT prevent user from selecting a file whose type isn't in the list. Do NOT use it to validate the selected file
23 |
24 | ## See also
25 |
26 | - [Select a folder to upload](https://phuoc.ng/collection/tips/select-a-folder-to-upload/)
27 |
--------------------------------------------------------------------------------
/contents/find-scrollable-elements.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-03'
4 | tags: Firefox DevTools
5 | title: Find scrollable elements
6 | ---
7 |
8 | The page has a scrollbar but you don't know which element on page creates that scrollbar.
9 | The Firefox Developer Tools allows us to see all elements causing overflow.
10 |
11 | - In Firefox, open its Developer Tools
12 | - Under the _Inspector_ tab, if an element has a scrollbar (by using either `overflow: scroll` or `overflow: auto` but the content is longer than the element dimension), it will be shown with a `scroll` badge.
13 |
14 | In the following screenshot, the highlighted element is displayed with the `scroll` badge indicating that it produces the scrollbar. Clicking the `scroll` badge will reveal the associate element.
15 |
16 | 
17 |
--------------------------------------------------------------------------------
/contents/find-the-root-npm-package-to-update.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/find-root-package.png
4 | created: '2021-05-17'
5 | tags: NPM
6 | title: Find the root NPM package to update
7 | ---
8 |
9 | If your repositories are stored on GitHub, you probably often receive notifications from GitHub asking to update a given npm package.
10 |
11 | It's a great feature as GitHub keeps your repository up-to-date and provides the patches to fix possible security issues. However, it doesn't tell you the actual packages that need to be updated.
12 |
13 | In order to see which the root package should be updated due to one of its dependencies updates, you can use the `npm ls` command.
14 |
15 | ```shell
16 | $ npm ls acorn
17 |
18 | ├─┬ @11ty/eleventy-plugin-syntaxhighlight@3.0.6
19 | │ └─┬ jsdom@16.4.0
20 | │ ├─┬ acorn-globals@6.0.0
21 | │ │ └── acorn@7.4.1 deduped
22 | │ └── acorn@7.4.1
23 | └─┬ @11ty/eleventy@0.11.1
24 | └─┬ pug@2.0.4
25 | ├─┬ pug-code-gen@2.0.2
26 | │ └─┬ with@5.1.1
27 | │ ├─┬ acorn-globals@3.1.0
28 | │ │ └── acorn@4.0.13
29 | │ └── acorn@3.3.0
30 | └─┬ pug-lexer@4.1.0
31 | └─┬ is-expression@3.0.0
32 | └── acorn@4.0.13
33 | ```
34 |
--------------------------------------------------------------------------------
/contents/fold-css-declarations-with-region-markers.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/fold-css.png
4 | created: '2021-05-12'
5 | tags: CSS, Visual Studio Code
6 | title: Fold CSS declarations with region markers
7 | ---
8 |
9 | In Visual Studio Code, we can make given CSS declarations foldable with the region markers.
10 |
11 | Just wrap the declarations between `/*#region*/` and `/*#endregion*/` if you're using CSS, SCSS or Less.
12 |
13 | SCSS and Less also accept `//` as a valid CSS comment, hence you can use `// #region` and `// #endregion` in the SCSS/Less files.
14 |
15 | ```css
16 | /* === Header === */
17 | /* #region */
18 |
19 | /* The CSS styles for header go here */
20 |
21 | /* #endregion */
22 |
23 | /* === Footer === */
24 | /* #region */
25 |
26 | /* The CSS styles for footer go here */
27 |
28 | /* #endregion */
29 |
30 | /* === Responsive === */
31 | /* #region */
32 |
33 | /* The responsive styles go here */
34 |
35 | /* #endregion */
36 | ```
37 |
38 | The following screenshot is an example:
39 |
40 | 
41 |
--------------------------------------------------------------------------------
/contents/force-the-browsers-to-download-new-favicon.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-12'
4 | tags: HTML
5 | title: Force the browsers to download new favicon
6 | ---
7 |
8 | To reduce the loading time, the browsers often cache static assets such as images, JavaScript, CSS files, etc. A favicon is one of them.
9 |
10 | Some browsers, Chrome for example, maintain a separate storage to cache favicons because they're frequently used in different places such as bookmarks, history.
11 |
12 | When we replace the favicon with a new one, our visitors probably still see the old one, even if they clear the browser cache.
13 |
14 | To prevent this from happening, we can always force the browsers to download the new favicon by adding a query parameter:
15 |
16 | ```html
17 |
18 | ```
19 |
20 | Of course, it's up to you to pick another name and value of parameter.
21 |
--------------------------------------------------------------------------------
/contents/format-a-list.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-27'
4 | tags: JavaScript
5 | title: Format a list
6 | ---
7 |
8 | We can take the advantage of the [Intl.ListFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) object to format a list for a given locale:
9 |
10 | ```js
11 | const people = ['Foo', 'Bar', 'Fuzz'];
12 |
13 | new Intl.ListFormat('en', { type: 'conjunction' }).format(people);
14 | // 'Foo, Bar, and Fuzz'
15 |
16 | new Intl.ListFormat('en-GB', { type: 'disjunction' }).format(people);
17 | // 'Foo, Bar, or Fuzz'
18 | ```
19 |
--------------------------------------------------------------------------------
/contents/format-a-number-as-a-currency-string.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-03'
4 | tags: JavaScript
5 | title: Format a number as a currency string
6 | ---
7 |
8 | Given a number, we can format it as a currency string without using an external libary.
9 |
10 | The `NumberFormat` API provides the easy way to format a currency of a given country:
11 |
12 | ```js
13 | const formatter = new Intl.NumberFormat('en-US', {
14 | style: 'currency',
15 | currency: 'USD',
16 | minimumFractionDigits: 2,
17 | });
18 | ```
19 |
20 | The optional `minimumFractionDigits` parameter indicates the minium number of digits in the fraction part. Calling the `format` function will format the input, and prefix or suffix the currency depending on the country.
21 |
22 | ```js
23 | formatter.format(2345); // '$2,345.00'
24 | formatter.format('2345'); // '$2,345.00'
25 | formatter.format('10000000'); // '$10,000,000.00'
26 | ```
27 |
--------------------------------------------------------------------------------
/contents/get-characters-of-a-string.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-22'
4 | tags: JavaScript
5 | title: Get characters of a string
6 | ---
7 |
8 | The following line returns all characters of a given string:
9 |
10 | ```js
11 | const message = 'Hello';
12 | const chars = [...message]; // ['H', 'e', 'l', 'l', 'o']
13 | ```
14 |
15 | If you want to get the first and the remaining characters of a string, then use ES6 destructing:
16 |
17 | ```js
18 | const [first, ...rest] = message;
19 | // first = 'H'
20 | // rest = ['e', 'l', 'l', 'o']
21 | ```
22 |
23 | We can use it to [capitalize](https://phuoc.ng/collection/1-loc/capitalize-a-string/) or [decapitalize](https://phuoc.ng/collection/1-loc/decapitalize-a-string/) a string:
24 |
25 | ```js
26 | capitalize = ([first, ...rest]) => `${first.toUpperCase()}${rest.join('')}`;
27 |
28 | decapitalize = ([first, ...rest]) => `${first.toLowerCase()}${rest.join('')}`;
29 |
30 | capitalize('hello world'); // 'Hello world'
31 | ```
32 |
33 | ## See also
34 |
35 | - [Ignore items from array destructuring](https://phuoc.ng/collection/tips/ignore-items-from-array-destructuring/)
36 | - [Pick the first and last items of an array](https://phuoc.ng/collection/tips/pick-the-first-and-last-items-of-an-array/)
37 |
--------------------------------------------------------------------------------
/contents/get-rid-of-escaping-quotes-with-template-literal.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-23'
4 | tags: JavaScript
5 | title: Get rid of escaping quotes with template literal
6 | ---
7 |
8 | You can use the template literal syntax in ES6 to get rid of escaping quote. For example:
9 |
10 | ```js
11 | // Instead of
12 | const message = "It's a message";
13 |
14 | // We can do this which is more convenient
15 | const message = `It's a message`;
16 | ```
17 |
18 | ## See also
19 |
20 | - [Create a multiline strings](https://phuoc.ng/collection/tips/create-a-multiline-strings/)
21 | - [Prevent a string from being escaped](https://phuoc.ng/collection/tips/prevent-a-string-from-being-escaped/)
22 | - [Use template literal to concatenate strings](https://phuoc.ng/collection/tips/use-template-literal-to-concatenate-strings/)
23 |
--------------------------------------------------------------------------------
/contents/get-the-current-timestamp.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-27'
4 | tags: JavaScript
5 | title: Get the current timestamp
6 | ---
7 |
8 | There are a few ways to get the current timestamp. All method return the number of milliseconds:
9 |
10 | ```js
11 | new Date().getTime();
12 |
13 | Date.now();
14 |
15 | // These method are the same
16 | // The unary operator (`+`) calls the `valueOf` method automatically
17 | +new Date();
18 | new Date().valueOf();
19 | ```
20 |
21 | > Take a look at this [snippet](https://phuoc.ng/collection/1-loc/get-the-current-timestamp-in-seconds/) if you want to get the current time in seconds
22 |
--------------------------------------------------------------------------------
/contents/go-to-the-previous-directory.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/go-to-the-previous-directory.png
4 | created: '2021-10-17'
5 | tags: "'Command Line'"
6 | title: Go to the previous directory
7 | ---
8 |
9 | We probably know that the `cd` command can be used to go to the given directory:
10 |
11 | ```shell
12 | $ cd /path/to/directory
13 | ```
14 |
15 | If you work with the terminal more often, then you might need to switch back and forth between different directories. If you see that typing the full path in the `cd` command is not convenient, then you can replace the full path with `-`.
16 |
17 | The following command will bring you to the previous working directory:
18 |
19 | ```shell
20 | $ cd -
21 | ```
22 |
23 | ## See also
24 |
25 | - [Checkout the previous branch](https://phuoc.ng/collection/tips/checkout-the-previous-branch/)
26 |
--------------------------------------------------------------------------------
/contents/hide-an-element-with-chrome-devtools.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-15'
4 | tags: DevTools
5 | title: Hide an element with Chrome DevTools
6 | ---
7 |
8 | We can hide an element by inspecting it with Chrome DevTools, right-clicking the element under the _Elements_ tab, and choosing the _Hide element_ menu from the context menu.
9 |
10 | 
11 |
12 | If you're a fan of using the shortcut, then pressing the h key has the same effect.
13 |
14 |
17 |
18 | Both ways add a specical CSS class named `__web-inspector-hide-shortcut__` to the element:
19 |
20 | ```css
21 | .__web-inspector-hide-shortcut__ {
22 | visibility: hidden !important;
23 | }
24 | ```
25 |
26 | > Setting `display: none`, `opacity: 0`, and `visibility: hidden` are the common ways to hide an element with CSS. If you want to see the differences between them, take a look at this [post](https://phuoc.ng/collection/this-vs-that/display-none-vs-opacity-0-vs-visibility-hidden/)
27 |
--------------------------------------------------------------------------------
/contents/highlight-text-with-the-mark-element.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-12'
4 | tags: HTML
5 | title: Highlight text with the mark element
6 | ---
7 |
8 | The semantic `` element provides the ability of highlighting text inside an element. Highlighting keywords in search results is a popular example of using the `` element.
9 |
10 | ```html
11 | Smooth scrolling with pure CSS
12 | ```
13 |
14 |
15 | ```html
16 |
Search results for scroll:
17 |
18 |
Smooth scrolling with pure CSS
19 |
Find scrollable elements
20 |
21 | ```
22 |
23 |
--------------------------------------------------------------------------------
/contents/ignore-case-sensitivity-in-a-css-attribute-selector.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-23'
4 | tags: CSS
5 | title: Ignore case sensitivity in a CSS attribute selector
6 | ---
7 |
8 | By default, CSS attribute selectors are case-sensitive. It means that `a[href$=".png"]` has effect with the links whose extensions are `.png` only.
9 |
10 | Imagine that you're building a files management application. It would add an icon to a file based on its extension. For example, the following CSS inserts an icon to any `.png` file.
11 |
12 | ```css
13 | a[href$='.png']:after {
14 | content: url(/assets/png.svg);
15 | }
16 | ```
17 |
18 | In reality, the files are uploaded by the users and we can't control the file extensions. A png file can be named as `.png`, `.PNG`, `.pNG`.
19 |
20 | In order to accept all of these variants, we can add `i` right before `]` in the selector.
21 |
22 | ```css
23 | a[href$='.png' i]:after {
24 | content: url(/assets/png.svg);
25 | }
26 | ```
27 |
28 | ## See also
29 |
30 | - [Add an icon to external links](https://phuoc.ng/collection/tips/add-an-icon-to-external-links/)
31 |
--------------------------------------------------------------------------------
/contents/ignore-items-from-array-destructuring.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-28'
4 | tags: JavaScript
5 | title: Ignore items from array destructuring
6 | ---
7 |
8 | When destructuring an array, you can skip certain items by using blanks:
9 |
10 | ```js
11 | const dateTime = '2021-02-28T14:57:00';
12 |
13 | // Ignore the date part
14 | const [, time] = dateTime.split('T');
15 |
16 | // Ignore the seconds
17 | const [hours, minutes] = time.split(':');
18 |
19 | hours; // '14'
20 | minutes; // '57'
21 | ```
22 |
23 | If you are working in a team, then it's a good idea to add comments for skipped items. It also makes the code more readable:
24 |
25 | ```js
26 | const [
27 | ,
28 | // date
29 | time,
30 | ] = dateTime.split('T');
31 |
32 | const [hours, minutes /* seconds */] = time.split(':');
33 | ```
34 |
35 | ## See also
36 |
37 | - [Get characters of a string](https://phuoc.ng/collection/tips/get-characters-of-a-string/)
38 | - [Pick the first and last items of an array](https://phuoc.ng/collection/tips/pick-the-first-and-last-items-of-an-array/)
39 | - [Use an underscore to name unused argument](https://phuoc.ng/collection/tips/use-an-underscore-to-name-unused-argument/)
40 |
--------------------------------------------------------------------------------
/contents/include-properties-conditionally.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2023-08-28'
4 | description: How to include properties conditionally using the spread syntax
5 | openGraphCover: /og/tips/include-properties-conditionally.png
6 | tags: JavaScript
7 | title: Include properties conditionally
8 | ---
9 |
10 | To define a property of an object in JavaScript, we typically declare it directly. For example:
11 |
12 | ```js
13 | const obj = {
14 | foo: 'Baz',
15 | bar: 'Qux',
16 | };
17 | ```
18 |
19 | Here's an example: we have an object with two properties named `foo` and `bar`, which have the values `Baz` and `Qux`, respectively.
20 |
21 | Now, let's say we only want to include the `bar` property if a certain condition is met. The standard approach is to declare the object as a variable using the `let` declaration, and then set the property later.
22 |
23 | ```js
24 | let obj = {
25 | foo: 'Bar',
26 | };
27 | if (condition) {
28 | obj.bar = 'Qux';
29 | }
30 | ```
31 |
32 | While the `if` statement approach works, it's not so convenient when we have to conditionally set many properties at once. Additionally, it turns the object from immutable to mutable due to the `let` declaration.
33 |
34 | Fortunately, JavaScript offers the object spread syntax, which is quite handy in this scenario. It allows us to selectively include properties from the source objects, depending on our conditions.
35 |
36 | To simplify the sample code above, we can use the following code:
37 |
38 | ```js
39 | const obj = {
40 | foo: 'Baz',
41 | ...(condition && { bar: 'Qux' }),
42 | };
43 | ```
44 |
45 | ## See also
46 |
47 | - [Create an array with conditional elements](https://phuoc.ng/collection/tips/create-an-array-with-conditional-elements/)
48 |
--------------------------------------------------------------------------------
/contents/increase-or-decrease-css-values-with-chrome-devtools.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-16'
4 | tags: DevTools
5 | title: Increase or decrease CSS values with Chrome DevTools
6 | ---
7 |
8 | There are some CSS properties whose values contain numbers, such as `height`, `font-size`, `margin`, `padding`, `width`, etc.
9 | Rather than changing the value manually, we can quickly increase or decrease the value by using one of the following key combinations:
10 |
11 | | Key combinations | Value change |
12 | | ------------------------------- | --------------- |
13 | | ↑ | Increase by 1 |
14 | | ↓ | Decrease by 1 |
15 | | alt + ↑ | Increase by 0.1 |
16 | | alt + ↓ | Decrease by 0.1 |
17 | | shift + ↑ | Increase by 10 |
18 | | shift + ↓ | Decrease by 10 |
19 | | cmd + ↑ | Increase by 100 |
20 | | cmd + ↓ | Decrease by 100 |
21 |
22 | On Windows and Linux, the cmd key should be replaced with the ctrl key.
23 |
24 |
27 |
--------------------------------------------------------------------------------
/contents/indicate-img-elements-that-miss-alt-attribute.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-22'
4 | tags: A11y, Accessibility, CSS
5 | title: Indicate img elements that miss alt attribute
6 | ---
7 |
8 | The following CSS gives a red outline to any img having a missing or blank alt attribute:
9 |
10 | ```css
11 | img:not([alt]),
12 | img[alt=''] {
13 | outline: 8px solid red;
14 | }
15 | ```
16 |
17 | If you are using Visual Studio Code, you can install the [webhint extension](https://marketplace.visualstudio.com/items?itemName=webhint.vscode-webhint). It will automatically detect the issue and show the details when you hover on the element.
18 |
19 | 
20 |
--------------------------------------------------------------------------------
/contents/insert-a-link-into-a-markdown-editor-quickly.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2022-09-24'
4 | tags: Markdown
5 | title: Insert a link into a Markdown editor quickly
6 | ---
7 |
8 | In order to insert a link into a Markdown editor, we often put the link right after the target text to follow the standard Markdown format:
9 |
10 | ```html
11 | ... [target text](link goes here) ...
12 | ```
13 |
14 | The link then is attached to the target text. However, there is a quick way to do that.
15 |
16 | 1. Copy the link into the clipboard
17 | 2. Select the target text
18 | 3. Then press command + V (on macOS) or Ctrl + V (on other operating systems) to paste the link
19 |
20 | I see these steps work for the most popular Markdown editors such as GitHub, Slack, etc.
21 |
--------------------------------------------------------------------------------
/contents/inspect-an-element-shown-on-hover.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-01'
4 | tags: DevTools
5 | title: Inspect an element shown on hover
6 | ---
7 |
8 | To inspect an element with Chrome DevTools, we usually right-click the element and choose _Inspect_ from the context menu.
9 | However, it doesn't work with a dynamic element that is displayed when we hover on a given element. A JavaScript tooltip is a common example.
10 |
11 | There are a few ways to inspect that kind of elements.
12 |
13 | ## Trigger the mouseover event
14 |
15 | - Right-click the original element, and choose the _Inspect_ menu item
16 | - Click the _Console_ tab
17 | - Fire the `mouseover` event by excuting the following code in the _Console_:
18 |
19 | ```js
20 | $0.dispatchEvent(
21 | new MouseEvent('mouseover', {
22 | view: window,
23 | bubbles: true,
24 | cancelable: true,
25 | })
26 | );
27 | ```
28 |
29 | > `$0` represents the current inspected element
30 |
31 | It simulates the `mouseover` event that is supposed to happen when we hover on the original element.
32 |
33 | ## Pause the script execution
34 |
35 | - Open the _Chrome Developer Tools_, and click the _Sources_ tab
36 | - Hover on the target element, and click the _F8_ key
37 | - Move the mouse over the target element
38 | - Activate the _Elements_ tab, and you will see the dynamic element shown up here
39 |
40 | ## Use debugger
41 |
42 | It's similar to the previous way.
43 |
44 | - In the _Console_, execute the following code:
45 |
46 | ```js
47 | handler = (e) => {
48 | if (e.key === 'Enter') debugger;
49 | };
50 | document.addEventListener('keydown', handler);
51 | ```
52 |
53 | Running `debugger` here will pause the script execution when we press the _Enter_ key. Of course, you can replace it with other key.
54 |
55 | - Hover on the target element, and click the _Enter_ key
56 | - The dynamic element is displayed and visible under the _Elements_ tab
57 |
58 | Once you don't want to monitor the dynamic element anymore, you can [stop listening](https://phuoc.ng/collection/html-dom/attach-or-detach-an-event-handler/) to the `keydown` event:
59 |
60 | ```js
61 | document.removeEventListener('keydown', handler);
62 | ```
63 |
64 | ## Track subtree modifications
65 |
66 | - Open the _Chrome Developer Tools_, and click the _Elements_ tab
67 | - Right-click the `body` element, and choose _Break on > subtree modifications_ from the context menu
68 |
69 | 
70 |
71 | > If the dynamic element, a tooltip for example, is generated in the parent element of the target element, then you should choose the parent instead of the `body` element
72 |
73 | - Move the mouse over the target element
74 | - You will see the dynamic element shown in the _Elements_ tab
75 |
--------------------------------------------------------------------------------
/contents/keep-the-calculation-of-a-magic-number.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-11'
4 | tags: JavaScript
5 | title: Keep the calculation of a magic number
6 | ---
7 |
8 | Let's say that we want to calculate the number of [difference days](https://phuoc.ng/collection/1-loc/calculate-the-number-of-difference-days-between-two-dates/) between two given dates.
9 | By simply using the `-` operator, it's easy for us to calculate the differences between two date objects, `date` and `otherDate` for example:
10 |
11 | ```js
12 | const diff = Math.abs(date - otherDate);
13 | ```
14 |
15 | The function returns the number of milliseconds. In order to get the number of days, we have to divide it by `86400000`:
16 |
17 | ```js
18 | Math.ceil(diff / 86400000);
19 | ```
20 |
21 | We know that `86400000` is the total number of milliseconds in a day. The code is still working fine until there's another people on the team looking at it, and it takes them time to realize what the number `86400000` is.
22 |
23 | To make the code more readable and easy to maintain, we should replace the magic number with its calculation:
24 |
25 | ```js
26 | // Better version
27 | const diffDays = (date, otherDate) => Math.ceil((Math.abs(date - otherDate) / 1000) * 60 * 60 * 24);
28 | ```
29 |
30 | If the number is used across different places, for example when we create a library for manipulating dates, then it's recommended to declare the magic number as a constant:
31 |
32 | ```js
33 | // The best version
34 | // constants.js
35 | const MILLISECOND_DAY = 1000 * 60 * 60 * 24;
36 | export { MILLISECOND_DAY };
37 | ```
38 |
39 | Then reuse it later:
40 |
41 | ```js
42 | import { MILLISECOND_DAY } from './constants';
43 |
44 | const diffDays = (date, otherDate) => Math.ceil(Math.abs(date - otherDate) / MILLISECOND_DAY);
45 | ```
46 |
47 | ## See also
48 |
49 | - [Do not use magic numbers when manipulating strings](https://phuoc.ng/collection/tips/do-not-use-magic-numbers-when-manipulating-strings/)
50 |
--------------------------------------------------------------------------------
/contents/lazy-loading-images-with-the-loading-attribute.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-12'
4 | tags: HTML
5 | title: Lazy loading images with the loading attribute
6 | ---
7 |
8 | By default, the browser will download all images found on a page no matter where they are located. To improve the user experience, we often use the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) or external libraries to lazy load images.
9 |
10 | The lazy loading means that an image is only downloaded to the client when it's available in the browser's viewport.
11 |
12 | In the latest version of Chromium-powered (including Chrome, Edge and Opera) and Firefox browsers support it natively with the `loading="lazy"` attribute:
13 |
14 | ```html
15 |
16 | ```
17 |
18 | The browser now will defer the loading of images until they are visible in the viewport.
19 |
20 | It's common to see that the layout is shifted when an image is loaded completely. To avoid the issue, it's recommended to set the size of image using either inline styles or the attributes:
21 |
22 | ```html
23 |
24 |
25 |
26 |
27 | ```
28 |
--------------------------------------------------------------------------------
/contents/list-branches-sorted-by-most-recent-commit-date.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/sort-git-branches.png
4 | created: '2021-05-12'
5 | tags: Git
6 | title: List branches sorted by most recent commit date
7 | ---
8 |
9 | If you're working on multiple projects or multiple features in the same project, you probably have different branches locally.
10 |
11 | Sometimes, it's not easy for us to remember exactly the branch that we worked on most recently.
12 | The `branch` command lists all branches sorted by alphabetical order:
13 |
14 | ```shell
15 | $ git branch
16 |
17 | avoid-mixing-styles
18 | fold-css
19 | * quick-color-variables
20 | skip-questions
21 | sort-branches
22 | ```
23 |
24 | The `*` symbol indicates the current branch. The list might be longer if you don't often remove the merged branches which are usually not relevant anymore.
25 |
26 | Fortunately, Git provides the `sort` parameter allowing us to list branches which are sorted by the commit date:
27 |
28 | ```shell
29 | $ git branch --sort=-committerdate
30 |
31 | * sort-branches
32 | fold-css
33 | quick-color-variables
34 | skip-questions
35 | avoid-mixing-styles
36 | ```
37 |
38 | You can reverse the sorting direction by omitting`-`:
39 |
40 | ```shell
41 | $ git branch --sort=committerdate
42 |
43 | avoid-mixing-styles
44 | skip-questions
45 | quick-color-variables
46 | fold-css
47 | * sort-branches
48 | ```
49 |
50 | It's good to know that we can make this behaviour as the default when listing branches:
51 |
52 | ```shell
53 | $ git config --global branch.sort -committerdate
54 | ```
55 |
--------------------------------------------------------------------------------
/contents/load-given-characters-in-a-google-font-request.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-21'
4 | tags: CSS, Google Font
5 | title: Load given characters in a Google font request
6 | ---
7 |
8 | By default, [Google font](https://fonts.google.com) loads the completed font that contains the full set of characters it supports.
9 |
10 | There are associations that we only use a Google font for a logo or heading which has specific letters. Instead of downloading the entire font, we can ask Google for downloading a part of it that contains required characters.
11 |
12 | We can pass the characters to the `text` parameter:
13 |
14 | ```html
15 |
16 | ```
17 |
18 | If you want to have Unicode characters, then encode them with their UTF-8 representations. For example `tips & tricks` are represented as `tips+%26+tricks`.
19 |
20 | Reducing the size of the font file improves the loading time, especially on devices where the network speed is usually limited.
21 |
22 | ## See also
23 |
24 | - [Avoid invisible text when loading a font](https://phuoc.ng/collection/tips/avoid-invisible-text-when-loading-a-font/)
25 | - [Combine Google font requests](https://phuoc.ng/collection/tips/combine-google-font-requests/)
26 |
--------------------------------------------------------------------------------
/contents/locate-an-element-with-a-given-selector.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-27'
4 | tags: DevTools
5 | title: Locate an element with a given selector
6 | ---
7 |
8 | We can use the arrow keys to navigate between elements under the _Elements_ tab. However, it's not convenient for us to find a given element when the page has a lot of nested elements.
9 |
10 | You probably see the problem when you're working on a unit of page (a component or widget, for example), and don't have the full knowledge of the entire page. In that situation, you might don't know exactly where your element is.
11 |
12 | Fortunately, DevTools gives us the `inspect` function to locate an element if we know its selector. The following code finds an element that has the `.rpv-default-layout-main` class and reveals it under the _Elements_ tab if there's any.
13 |
14 | ```js
15 | inspect($('.rpv-default-layout-main'));
16 | ```
17 |
18 | > The `$` function is equivalent to the [`document.querySelector`](https://phuoc.ng/collection/tips/quick-query-elements-in-the-console/) function
19 |
20 |
23 |
24 | ## See also
25 |
26 | - [Quick query elements in the Console](https://phuoc.ng/collection/tips/quick-query-elements-in-the-console/)
27 |
--------------------------------------------------------------------------------
/contents/log-a-value-to-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-27'
4 | tags: JavaScript
5 | title: Log a value to the Console
6 | ---
7 |
8 | There are a few ways to log a value to the _Console_, but using object destructuring is the convenient and short one.
9 |
10 | ```js
11 | const fullName = 'John Doe';
12 |
13 | console.log('full name' + fullName);
14 | console.log('full name', fullName);
15 |
16 | // Better: use template string
17 | console.log(`full name: ${fullName}`);
18 |
19 | // Best: use object destructuring
20 | console.log({ fullName }); // { fullName: 'John Doe' }
21 | ```
22 |
23 | ## See also
24 |
25 | - [Conditional logging in the Console](https://phuoc.ng/collection/tips/conditional-logging-in-the-console/)
26 | - [Count how many times a function has been called](https://phuoc.ng/collection/tips/count-how-many-times-a-function-has-been-called/)
27 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
28 | - [Log a variable to the console using conditional breakpoints](https://phuoc.ng/collection/tips/log-a-variable-to-the-console-using-conditional-breakpoints/)
29 | - [Log an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/)
30 |
--------------------------------------------------------------------------------
/contents/log-a-variable-in-an-arrow-function.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-25'
4 | tags: JavaScript
5 | title: Log a variable in an arrow function
6 | ---
7 |
8 | Have you ever had to debug an 1 line arrow function using `console.log`? It usually requires us to switch to a multiple lines version such as:
9 |
10 | ```js
11 | const formatYmd = (date) => {
12 | console.log(date.toISOString());
13 | return date.toISOString().slice(0, 10);
14 | };
15 | ```
16 |
17 | You can get rid of that conversion by using the `||` operator. It works because `console.log()` returns `undefined`, so the `||` will enforce the function to evaluate and return the right side which is our actual function.
18 |
19 | ```js
20 | const formatYmd = (date) => console.log(date.toISOString()) || date.toISOString().slice(0, 10);
21 |
22 | formatYmd(new Date());
23 | // Print something like `2021-02-25T04:52:39.720Z` in the Console
24 | ```
25 |
26 | There is another, less known tip which uses the [comma operator](https://phuoc.ng/collection/tips/shorten-codes-with-the-comma-operator/):
27 |
28 | ```js
29 | const formatYmd = date => (console.log(...), date.toISOString().slice(0, 10));
30 | ```
31 |
32 | > You will find more useful 1 line-of-code functions on the [1 LOC](https://phuoc.ng/collection/1-loc) series.
33 |
34 | ## See also
35 |
36 | - [Conditional logging in the Console](https://phuoc.ng/collection/tips/conditional-logging-in-the-console/)
37 | - [Count how many times a function has been called](https://phuoc.ng/collection/tips/count-how-many-times-a-function-has-been-called/)
38 | - [Log a value to the Console](https://phuoc.ng/collection/tips/log-a-value-to-the-console/)
39 | - [Log a variable to the console using conditional breakpoints](https://phuoc.ng/collection/tips/log-a-variable-to-the-console-using-conditional-breakpoints/)
40 | - [Log an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/)
41 |
--------------------------------------------------------------------------------
/contents/log-a-variable-to-the-console-using-conditional-breakpoints.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-03-01'
4 | tags: DevTools
5 | title: Log a variable to the console using conditional breakpoints
6 | ---
7 |
8 | Sometimes it's not possible for us to log data to the _Console_ directly in the code. For example, when we debug codes coming from external libraries.
9 |
10 | Perform the steps below to print the variable:
11 |
12 | - Open the Chrome DevTools, and activate the _Sources_ tab
13 | - Navigate to the line of a given function that you want to log the data
14 | - Right-click the line number, and choose _Add conditional breakpoint ..._ from the context menu
15 | - Enter the condition
16 |
17 | ```js
18 | console.log('replace-it-with-your-variable');
19 | ```
20 |
21 | When the associcate line is hit by the debug process, it will try to evaluate the condition meaning that the variable is printed out.
22 |
23 | The nice thing is that the script execution on the page is still being processed because the condition never happens (the `console.log` function returns `undefined`).
24 |
25 | In the following screenshot, the `params` variable is logged to the _Console_ when the `sum` function is invoked.
26 |
27 | 
28 |
29 | ## See also
30 |
31 | - [Conditional logging in the Console](https://phuoc.ng/collection/tips/conditional-logging-in-the-console/)
32 | - [Count how many times a function has been called](https://phuoc.ng/collection/tips/count-how-many-times-a-function-has-been-called/)
33 | - [Log a value to the Console](https://phuoc.ng/collection/tips/log-a-value-to-the-console/)
34 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
35 | - [Log an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/)
36 |
--------------------------------------------------------------------------------
/contents/log-an-array-to-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-27'
4 | tags: JavaScript
5 | title: Log an array to the Console
6 | ---
7 |
8 | Instead of using the `console.log` function, `console.table` produces a better output. It works pretty well with an array or object.
9 |
10 | ```js
11 | const resources = [
12 | {
13 | name: '1 LOC',
14 | description: 'Favorite JavaScript utilities in single line of code',
15 | link: 'https://phuoc.ng/collection/1-loc',
16 | },
17 | {
18 | name: 'CSS Layout',
19 | description: 'A collection of popular layouts and patterns made with CSS',
20 | link: 'https://phuoc.ng/collection/css-layout',
21 | },
22 | {
23 | name: 'HTML DOM',
24 | description: 'How to manage HTML DOM with vanilla JavaScript',
25 | link: 'https://phuoc.ng/collection/html-dom',
26 | },
27 | {
28 | name: 'Front-end Tips',
29 | description: 'Super tiny, quick tips, tricks and best practices of front-end development',
30 | link: 'https://phuoc.ng/collection/tips',
31 | },
32 | {
33 | name: 'this VS that',
34 | description: 'The differences between ___ and ___ in the front-end development',
35 | link: 'https://phuoc.ng/collection/this-vs-that',
36 | },
37 | ];
38 |
39 | console.table(resources);
40 | ```
41 |
42 | Here is the screenshot compares the output of two methods above:
43 |
44 | 
45 |
46 | If you don't want to see all the columns, then you can indicate the columns explicitly:
47 |
48 | ```js
49 | // Show `name` and `link` properties
50 | console.table(resources, ['name', 'link']);
51 | ```
52 |
53 | 
54 |
55 | This tip also has effect when you want to [pick some particular properties from a JSON representation](https://phuoc.ng/collection/tips/pick-given-properties-from-a-json-representation/).
56 |
57 | ## See also
58 |
59 | - [Conditional logging in the Console](https://phuoc.ng/collection/tips/conditional-logging-in-the-console/)
60 | - [Count how many times a function has been called](https://phuoc.ng/collection/tips/count-how-many-times-a-function-has-been-called/)
61 | - [Log a value to the Console](https://phuoc.ng/collection/tips/log-a-value-to-the-console/)
62 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
63 | - [Log a variable to the console using conditional breakpoints](https://phuoc.ng/collection/tips/log-a-variable-to-the-console-using-conditional-breakpoints/)
64 |
--------------------------------------------------------------------------------
/contents/log-the-full-object-in-nodejs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-06'
4 | tags: NodeJS
5 | title: Log the full object in NodeJS
6 | ---
7 |
8 | Using the `console.log` method in NodeJS might not be ideal if the input has deep nested properties. It will replace the deep nested property with `[Object]`.
9 |
10 | Let's say that the `person` variable holds the information of a person. `console.log(person)` will produce the following output:
11 |
12 | ```json
13 | {
14 | username: 'johndoe',
15 | meta: {
16 | firstName: 'John',
17 | lastName: 'Doe',
18 | profile: { address: [Object] }
19 | }
20 | }
21 | ```
22 |
23 | To get rid of `[Object]`, you can use the `console.dir` method to see the full object:
24 |
25 | ```js
26 | console.dir(person, { depth: null });
27 | /*
28 | {
29 | username: 'johndoe',
30 | meta: {
31 | firstName: 'John',
32 | lastName: 'Doe',
33 | profile: {
34 | address: { street: '123 Main St', city: 'AnyTown' }
35 | }
36 | }
37 | }
38 | */
39 | ```
40 |
41 | It's also possible to use the same technique mentioned in the [_Pretty format JSON_](https://phuoc.ng/collection/tips/pretty-format-json/) tip which works in both NodeJS and browser environments.
42 |
43 | ## See also
44 |
45 | - [Transform values from a JSON representation](https://phuoc.ng/collection/tips/transform-values-from-a-json-representation/)
46 |
--------------------------------------------------------------------------------
/contents/make-a-table-with-equal-column-widths.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-02'
4 | tags: CSS
5 | title: Make a table with equal column widths
6 | ---
7 |
8 | Setting the width for each cell explicitly is the straightforward way to give all columns the same width.
9 | For example, the CSS declaration below splits a table of four columns into parts whose widths are the same:
10 |
11 | ```css
12 | table td {
13 | width: 25%;
14 | }
15 | ```
16 |
17 | However, the approach doesn't work if the table has a dynamic number of columns. Fortunately, we can use the `table-layout` property to do that.
18 | No matter what how many columns the table has, they will have the same widths.
19 |
20 | ```css
21 | table {
22 | table-layout: fixed;
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/contents/merge-different-arrays.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-22'
4 | tags: JavaScript
5 | title: Merge different arrays
6 | ---
7 |
8 | We can use the ES6 spread operator to merge different arrays into one:
9 |
10 | ```js
11 | const a = [1, 2, 3];
12 | const b = [4, 5, 6];
13 | const c = [7, 8, 9];
14 |
15 | const final = [...a, ...b, ...c]; // [1, 2, 3, 4, 5, 6, 7, 8, 9]
16 | ```
17 |
18 | The spread operator is handy if we use the `push` method as well:
19 |
20 | ```js
21 | const a = [1, 2, 3];
22 | const b = [4, 5, 6];
23 |
24 | a.push(...b);
25 | a; // [1, 2, 3, 4, 5, 6]
26 | ```
27 |
--------------------------------------------------------------------------------
/contents/move-the-cursor-to-any-position-in-a-macos-command.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | contributors:
4 | - dylankenneally
5 | created: '2021-03-27'
6 | tags: Command Line, macOS
7 | title: Move the cursor to any position in a macOS command
8 | updated: '2022-09-08'
9 | ---
10 |
11 | We often use the arrow keys to move the cursor when editing the command line on the terminal.
12 |
13 | Imagine that the target position is somewhere in the middle of the command text, pressing the arrow keys multiple times takes time and isn't a comfortable process.
14 |
15 | Fortunately, there is a shortcut that makes the process easier - holding down the alt key while clicking the mouse at the desired position.
16 | The following video demonstrates how to fix a typo in a command using this tip.
17 |
18 |
21 |
--------------------------------------------------------------------------------
/contents/move-the-screenshot-area-on-macos.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-15'
4 | tags: macOS
5 | title: Move the screenshot area on macOS
6 | ---
7 |
8 | On macOS, we can press the combination of cmd + shift + 4 keys to start capturing a screenshot.
9 |
10 | Even though we can move the mouse to resize the captured area, the top left corner is fixed. Sometime it's not convenient if we want to adjust the top left corner.
11 |
12 | In that case, rather than pressing the escape key and try again, you can hit and hold the space key. Moving your mouse will also move the screenshot area.
13 |
14 |
17 |
18 | ## See also
19 |
20 | - [Capture a screenshot without shadow on macOS](https://phuoc.ng/collection/tips/capture-a-screenshot-without-shadow-on-macos/)
21 | - [Copy screenshots to the clipboard on macOS](https://phuoc.ng/collection/tips/copy-screenshots-to-the-clipboard-on-macos/)
22 |
--------------------------------------------------------------------------------
/contents/number-headings-and-subheadings-automatically.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/number-headings-subheadings.png
4 | created: '2021-03-14'
5 | tags: CSS
6 | title: Number headings and subheadings automatically
7 | ---
8 |
9 | It's a common approach to use the [DOM APIs](https://phuoc.ng/collection/html-dom) to find all headings on the page, and number each of them. This post introduces another way to do that with CSS only.
10 |
11 | If you follow some search engine optimization (known as _SEO_) practices, you are adviced to use only one `h1` tag on the page. The single `h1` tag is used to display the main title. Hence, we will ignore the `h1` tag when numbering the headings.
12 |
13 | Given the following markup:
14 |
15 | ```html
16 |
Chapter 1
17 |
18 |
Section 1
19 |
Sub section A
20 |
21 |
Section 2
22 |
Sub section A
23 |
Sub section B
24 |
25 |
Chapter 2
26 | ```
27 |
28 | It should produce the content as below:
29 |
30 | ```shell
31 | 1. Chapter 1
32 |
33 | 1.1. Section 1
34 | 1.1.1. Sub section A
35 |
36 | 1.2. Section 2
37 | 1.2.1. Sub section A
38 | 1.2.2. Sub section B
39 |
40 | 2. Chapter 2
41 | ```
42 |
43 | We can archive it by using the CSS counter.
44 |
45 | Going down from the `body` element, we will reset the counter for the first `h2`. The numbers are inserted before the content via the `::before` pseudo element:
46 |
47 | ```css
48 | body,
49 | h1 {
50 | counter-reset: h2;
51 | }
52 | h2::before {
53 | counter-increment: h2;
54 | content: counter(h2) '. ';
55 | }
56 | ```
57 |
58 | The `counter-increment` property indicates that when we see the next `h2` tag, the number will be increased by one.
59 |
60 | We continue using the same technique for `h3` tags. The number for a `h3` heading must be prefixed with the number of its parent `h2` tag:
61 |
62 | ```css
63 | h2 {
64 | counter-reset: h3;
65 | }
66 | h3::before {
67 | counter-increment: h3;
68 | content: counter(h2) '.' counter(h3) '. ';
69 | }
70 | ```
71 |
72 | Here we use the `.` character to separate the numbers. If you want to number deeper headings such as `h4`, `h5`, then the CSS should look like as following:
73 |
74 | ```css
75 | h3 {
76 | counter-reset: h4;
77 | }
78 | h4 {
79 | counter-reset: h5;
80 | }
81 | h4::before {
82 | counter-increment: h4;
83 | content: counter(h2) '.' counter(h3) '.' counter(h4) '. ';
84 | }
85 | h5::before {
86 | counter-increment: h5;
87 | content: counter(h2) '.' counter(h3) '.' counter(h4) '.' counter(h5) '. ';
88 | }
89 | ```
90 |
91 | ## See also
92 |
93 | - [Append leading zeros to ordered list items](https://phuoc.ng/collection/tips/append-leading-zeros-to-ordered-list-items/)
94 |
--------------------------------------------------------------------------------
/contents/omit-properties-of-a-svelte-component.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/omit-properties-svelte-component.png
4 | created: '2021-11-03'
5 | tags: Svelte
6 | title: Omit properties of a Svelte component
7 | ---
8 |
9 | [Svelte](https://svelte.dev) is one of the libraries for building user interfaces. We usually declare the properties and values of a component in the same syntax of declaring a HTML tag.
10 |
11 | The following component has three different properties. Two of them represent the first and last name of an account, and the last one updates these values.
12 |
13 | ```html
14 |
23 |
24 |
25 | ```
26 |
27 | The cool thing is that, if you use the same name for both property and the variable representing its value, you can omit the property. The `Account` component can be shorten as following:
28 |
29 | ```js
30 |
35 | ```
36 |
37 | ## See also
38 |
39 | - [Omit values of HTML boolean attributes](https://phuoc.ng/collection/tips/omit-values-of-html-boolean-attributes/)
40 |
--------------------------------------------------------------------------------
/contents/omit-values-of-html-boolean-attributes.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | cover: /assets/tips/omit-boolean-attribute-value.png
4 | created: '2021-04-19'
5 | tags: HTML
6 | title: Omit values of HTML boolean attributes
7 | ---
8 |
9 | There are some HTML boolean attributes such as `checked`, `disabled`, `readonly`, `required`, `selected`, etc.
10 |
11 | According to the [HTML specification](https://html.spec.whatwg.org/#boolean-attribute), a boolean attribute has three possible declarations. All of them have the same effect:
12 |
13 | ```html
14 |
15 |
16 |
17 | ```
18 |
19 | `true` and `false` are invalid values:
20 |
21 | ```html
22 |
23 |
24 |
25 | ```
26 |
27 | The only way to represent a `false` value is to remove the attribute. Hence, to avoid the incorrect and misleading usages, it's recommended to remove the value:
28 |
29 | ```html
30 |
31 | ```
32 |
33 | ## See also
34 |
35 | - [Omit properties of a Svelte component](https://phuoc.ng/collection/tips/omit-properties-of-a-svelte-component/)
36 |
--------------------------------------------------------------------------------
/contents/open-a-package-s-homepage-or-repo.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-17'
4 | tags: NPM
5 | title: "Open a package's homepage or repo"
6 | ---
7 |
8 | When looking for the documentation of a npm package, we often use Google to search for its homepage and npm page.
9 |
10 | It turns out that we can go to the homepage quickly by running the following command:
11 |
12 | ```shell
13 | $ npm home PACKAGE_NAME
14 | ```
15 |
16 | Opening its repository is easy too:
17 |
18 | ```shell
19 | $ npm repo PACKAGE_NAME
20 | ```
21 |
22 | Both commands open the destination website in your default browser.
23 |
--------------------------------------------------------------------------------
/contents/open-all-links-in-new-tabs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-04-13'
4 | tags: HTML
5 | title: Open all links in new tabs
6 | ---
7 |
8 | We all know that setting the `target="_blank"` attribute to an individual link will open it in a new tab.
9 |
10 | It's uncommon to see a real use case where you want all links on the page to be opened in a new tab. A site that only collects links of external resources might be an example.
11 |
12 | In that case, rather than adding the attribute to all links, you just set it to the `base` tag:
13 |
14 | ```html
15 |
16 |
17 | ...
18 |
19 | ```
20 |
--------------------------------------------------------------------------------
/contents/override-the-behavior-of-instanceof.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-23'
4 | tags: JavaScript
5 | title: Override the behavior of instanceof
6 | ---
7 |
8 | `instanceof` doesn't work for primitive types.
9 |
10 | If you want to use `instanceof` all the time, then you can override the behavior of `instanceof` by implementing a static method with the key of `Symbol.hasInstance`.
11 | In the following code, we create a class called `PrimitiveNumber` that checks if a value is a number:
12 |
13 | ```js
14 | class PrimitiveNumber {
15 | static [Symbol.hasInstance](value) {
16 | return typeof value === 'number';
17 | }
18 | }
19 |
20 | 12345 instanceof PrimitiveNumber; // true
21 | 'helloworld' instanceof PrimitiveNumber; // false
22 | ```
23 |
--------------------------------------------------------------------------------
/contents/pass-an-array-as-function-arguments.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-03'
4 | tags: JavaScript
5 | title: Pass an array as function arguments
6 | ---
7 |
8 | JavaScript has some built-in functions that accept a list of individuals arguments, but passing an array doen't work. `Math.max`, `Math.min` are some of them.
9 |
10 | They are used to find the [biggest](https://phuoc.ng/collection/1-loc/find-the-maximum-item-of-an-array/) and [smallest numbers](https://phuoc.ng/collection/1-loc/find-the-minimum-item-of-an-array/) in the passed arguments, repectively.
11 |
12 | ```js
13 | Math.max(1, 2, 3, 4); // 4
14 |
15 | // Doesn't work because it treats the array as a single parameter
16 | // That parameter isn't a number, so the function returns `NaN`
17 | Math.max([1, 2, 3, 4]); // NaN
18 | ```
19 |
20 | If we want to pass a dynamic array of numbers, then the ES6 spread operator (`...`) can help. It turns a varible to a list of individual parameters:
21 |
22 | ```js
23 | const array = [1, 2, 3, 4];
24 | Math.max(...array); // 4
25 | ```
26 |
27 | JavaScript engines implemented by different browsers have the limited number of parameters. Using the `...` operator doesn't work if you have a big array. Using the `reduce` method doesn't have this problem.
28 |
29 | ```js
30 | const max = (arr) => arr.reduce((a, b) => Math.max(a, b));
31 | max([1, 2, 3, 4]); // 4
32 | ```
33 |
34 | ## See also
35 |
36 | - [Accept any numbers of parameters](https://phuoc.ng/collection/tips/accept-any-numbers-of-parameters/)
37 |
--------------------------------------------------------------------------------
/contents/persist-console-logs-between-page-refreshes.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-13'
4 | tags: DevTools
5 | title: Persist Console logs between page refreshes
6 | ---
7 |
8 | Imagine a situation where you print something in the _Console_ when clicking or following a link. When you navigate to another page, the _Console_ is cleared and all the messages are lost.
9 |
10 | Fortunately, both Chrome DevTools and Firefox Developer Tools give us options to persist the logs between page loads.
11 |
12 | Under the _Console_ panel of Chrome DevTools, click the cog icon on the right, and turn on the _Preserve log_ option:
13 |
14 | 
15 |
16 | In addition to the logs of each page, DevTools also lets you know when you navigate to another page. It's easy for us to determine where a log message comes from.
17 |
--------------------------------------------------------------------------------
/contents/pick-given-properties-from-a-json-representation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/pick-given-properties.png
4 | created: '2021-04-19'
5 | tags: JavaScript
6 | title: Pick given properties from a JSON representation
7 | ---
8 |
9 | By default, `JSON.stringify(value)` will pull all serializable properties of `value`.
10 |
11 | ```js
12 | const resources = [
13 | {
14 | name: '1 LOC',
15 | description: 'Favorite JavaScript utilities in single line of code',
16 | link: 'https://phuoc.ng/collection/1-loc',
17 | },
18 | {
19 | name: 'CSS Layout',
20 | description: 'A collection of popular layouts and patterns made with CSS',
21 | link: 'https://phuoc.ng/collection/css-layout',
22 | },
23 | {
24 | name: 'HTML DOM',
25 | description: 'How to manage HTML DOM with vanilla JavaScript',
26 | link: 'https://phuoc.ng/collection/html-dom',
27 | },
28 | {
29 | name: 'Front-end Tips',
30 | description: 'Super tiny, quick tips, tricks and best practices of front-end development',
31 | link: 'https://phuoc.ng/collection/tips',
32 | },
33 | {
34 | name: 'this VS that',
35 | description: 'The differences between ___ and ___ in the front-end development',
36 | link: 'https://phuoc.ng/collection/this-vs-that',
37 | },
38 | ];
39 |
40 | JSON.stringify(resources);
41 | /*
42 | [
43 | {
44 | "name": "1 LOC",
45 | "description": "...",
46 | "link": "..."
47 | },
48 | {
49 | "name": "CSS Layout",
50 | "description": "...",
51 | "link": "..."
52 | },
53 | {
54 | "name": "HTML DOM",
55 | "description": "...",
56 | "link": "..."
57 | },
58 | ...
59 | ]
60 | */
61 | ```
62 |
63 | If you want to receive some particular properties, then pass them to the second parameter:
64 |
65 | ```js
66 | // Only pick the `name` property
67 | JSON.stringify(resources, ['name']);
68 |
69 | // Pick the `name` and `link` properties
70 | JSON.stringify(resources, ['name', 'link']);
71 | ```
72 |
73 | You can use the same tip when [logging an array to the Console](https://phuoc.ng/collection/tips/log-an-array-to-the-console/).
74 |
75 | ## See also
76 |
77 | - [Log the full object in NodeJS](https://phuoc.ng/collection/tips/log-the-full-object-in-nodejs/)
78 | - [Pretty format JSON](https://phuoc.ng/collection/tips/pretty-format-json/)
79 | - [Transform values from a JSON representation](https://phuoc.ng/collection/tips/transform-values-from-a-json-representation/)
80 |
--------------------------------------------------------------------------------
/contents/pick-the-first-and-last-items-of-an-array.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | contributors:
4 | - PixelT
5 | cover: /assets/tips/pick-first-last.png
6 | created: '2021-05-17'
7 | tags: JavaScript
8 | title: Pick the first and last items of an array
9 | updated: '2021-05-17'
10 | ---
11 |
12 | If you want to receive the first and last items of a given array, you might think of the common way as following:
13 |
14 | ```js
15 | const length = arr.length;
16 | const first = arr[0];
17 | const last = arr[arr.length - 1];
18 | ```
19 |
20 | Because an array is also an object, the `length` property can be accessed with the destructuring syntax:
21 |
22 | ```js
23 | const { length } = arr;
24 | ```
25 |
26 | Also, an array item at any position can be accessed with its index. Hence, we can shorten three lines at the top with a single line:
27 |
28 | ```js
29 | const { length, 0: first, [length - 1]: last } = arr;
30 | ```
31 |
32 | ## See also
33 |
34 | - [Get characters of a string](https://phuoc.ng/collection/tips/get-characters-of-a-string/)
35 | - [Ignore items from array destructuring](https://phuoc.ng/collection/tips/ignore-items-from-array-destructuring/)
36 |
--------------------------------------------------------------------------------
/contents/prefix-class-name-with-js-to-manipulate-with-javascript.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-02-23'
4 | tags: DOM, HTML
5 | title: Prefix class name with js to manipulate with javascript
6 | ---
7 |
8 | Have you ever seen a class name starting with `js-`?
9 |
10 | If the answer is not, then you can visit the [GitHub repository](https://github.com/phuocng) and see the source of generated HTML. There are bunch of elements whose class are prefixed with `js-` such as:
11 |
12 | ```html
13 |
14 | ```
15 |
16 | They are often used to manage the list of elements that we don't want to set id for (dynamic elements, for example). In these cases, we use the `document.querySelectorAll('js-xxx')` method to retrieve the elements list.
17 |
18 | In the other words, the `js-` classes don't define the styles, but for managing the elements in JavaScript instead. That's why they are prefixed with `js-`.
19 |
20 | Follow this naming convention is helpful if you're working in a team. Other engineers will be aware of the class name, and don't remove them when refactoring the page.
21 |
--------------------------------------------------------------------------------
/contents/pretty-format-json.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | contributors:
4 | - dnrm
5 | created: '2021-02-27'
6 | tags: JavaScript
7 | title: Pretty format JSON
8 | updated: '2021-03-03'
9 | ---
10 |
11 | We often use `JSON.stringify` to generate JSON for a given object. By default, it removes all spaces between items. It's not easy to scan the output, especially when using with `console.log`.
12 |
13 | Do you know that it's possible for us to generate a pretty output by passing the indentation level to the third parameter?
14 |
15 | ```js
16 | const person = {
17 | firstName: 'John',
18 | lastName: 'Doe',
19 | ages: 42,
20 | };
21 |
22 | JSON.stringify(person, null, 2);
23 |
24 | /*
25 | "{
26 | "firstName": "John",
27 | "lastName": "Doe",
28 | "ages": 42
29 | }"
30 | */
31 | ```
32 |
33 | If you prefer tab for the indentations, then passing the `\t` character:
34 |
35 | ```js
36 | JSON.stringify(person, null, '\t');
37 |
38 | /*
39 | "{
40 | "firstName": "John",
41 | "lastName": "Doe",
42 | "ages": 42
43 | }"
44 | */
45 | ```
46 |
47 | ## See also
48 |
49 | - [Log the full object in NodeJS](https://phuoc.ng/collection/tips/log-the-full-object-in-nodejs/)
50 | - [Pick given properties from a JSON representation](https://phuoc.ng/collection/tips/pick-given-properties-from-a-json-representation/)
51 | - [Transform values from a JSON representation](https://phuoc.ng/collection/tips/transform-values-from-a-json-representation/)
52 |
--------------------------------------------------------------------------------
/contents/prevent-a-string-from-being-escaped.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-01'
4 | tags: JavaScript
5 | title: Prevent a string from being escaped
6 | ---
7 |
8 | By default, JavaScript will escape the next characters whenever it sees an escape character.
9 | For example, the backslash (`\`) is one of escape character, the actual value of following path isn't the same as it is represented.
10 |
11 | ```js
12 | 'C:\\Foo\Bar\Fuzz\document.pdf'; // C:\FooBarFuzzdocument.pdf
13 | ```
14 |
15 | If you want to keep a string from being escaped, then use the [String.raw](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw) function:
16 |
17 | ```js
18 | String.raw`C:\\Foo\Bar\Fuzz\document.pdf`; // C:\\Foo\Bar\Fuzz\document.pdf
19 | ```
20 |
21 | - [Create a multiline strings](https://phuoc.ng/collection/tips/create-a-multiline-strings/)
22 | - [Get rid of escaping quotes with template literal](https://phuoc.ng/collection/tips/get-rid-of-escaping-quotes-with-template-literal/)
23 | - [Use template literal to concatenate strings](https://phuoc.ng/collection/tips/use-template-literal-to-concatenate-strings/)
24 |
--------------------------------------------------------------------------------
/contents/prevent-anchor-links-from-disappearing-behind-a-sticky-header.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-24'
4 | tags: CSS
5 | title: Prevent anchor links from disappearing behind a sticky header
6 | ---
7 |
8 | [Sticky headers](https://phuoc.ng/collection/css-layout/sticky-header/) is a common layout that can be seen in a lot of websites. The problem is that it doesn't play nicely with anchor links.
9 |
10 | Imagine that we have a table of contents that contains different anchor links. Each anchor brings the user to a particular section in the page.
11 |
12 | When users click an anchor, the page scrolls to the destination section. But some parts of the section are displayed under the header which is not a good experience for users.
13 |
14 | To prevent it from happening, we would like to add a margin to the top of the destination, but it only has effect when scrolling. It's the time where the scroll-margin-top comes in handy.
15 |
16 | ```css
17 | header {
18 | height: 2rem;
19 | }
20 |
21 | section {
22 | scroll-margin-top: 2rem;
23 | }
24 | ```
25 |
26 | ## See also
27 |
28 | - [Smooth scrolling with pure CSS](https://phuoc.ng/collection/tips/smooth-scrolling-with-pure-css/)
29 |
--------------------------------------------------------------------------------
/contents/prevent-browsers-from-asking-to-translate.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-04-06'
4 | tags: HTML
5 | title: Prevent browsers from asking to translate
6 | ---
7 |
8 | If the contents of a website are written in a particular language, the visitors might be prompted to translate them into another language when using certain browsers ([Google Chrome](https://www.google.com/chrome), for example).
9 |
10 | You can prevent this from happening by setting the `translate` attribute for the whole document:
11 |
12 | ```html
13 |
14 | ```
15 |
16 | The `translate` attribute has an effect on some translation tools such as [Google Translate](https://translate.google.com).
17 |
18 | Moreover, it's a global attribute meaning that we can use it for any elements. It's very useful if we don't want to translate a brand name even if the page is translated:
19 |
20 | ```html
21 |
22 | ```
23 |
--------------------------------------------------------------------------------
/contents/prevent-macos-from-going-to-sleep.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-22'
4 | tags: macOS
5 | title: Prevent macOS from going to sleep
6 | ---
7 |
8 | By default, macOS goes to sleep if it hasn't been used for a certain amount of time. However, there are cases we want to prevent it from happening such as during a presentation, downloading a big file.
9 |
10 | macOS provides a command to do that:
11 |
12 | ```shell
13 | $ caffeinate
14 | ```
15 |
16 | Pressing ctrl + C will bring it back to the normal mode.
17 |
18 | The command also supports the time parameter. It indicates that macOS won't enter the sleep mode after a given number of seconds.
19 | For example, the command below asks macOS to awake in the next 10 minutes.
20 |
21 | ```shell
22 | $ caffeinate -u -t 600
23 | ```
24 |
--------------------------------------------------------------------------------
/contents/prevent-the-default-behavior-with-jquery-event-handler.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-02-23'
4 | tags: DOM, JavaScript
5 | title: Prevent the default behavior with jQuery event handler
6 | ---
7 |
8 | If you're using jQuery to manage the events, then you're able to use `return false` within the event handler:
9 |
10 | ```js
11 | $(element).on('click', function (e) {
12 | return false;
13 | });
14 | ```
15 |
16 | Before returning the value of `false`, the handler would do something else. The problem is that if there's any runtime error occurring in the handler, we will not reach the `return false` statement at the end.
17 |
18 | In that case, the default behavior will be taken:
19 |
20 | ```js
21 | $(element).on('click', function (e) {
22 | // Do something here, but if there's error at runtime
23 | // ...
24 | return false;
25 | });
26 | ```
27 |
28 | We can avoid this situation by using the `preventDefault` method before performing any custom handler:
29 |
30 | ```js
31 | $(element).on('click', function (e) {
32 | e.preventDefault();
33 |
34 | // Do something here
35 | // The default behavior is prevented regardless errors at runtime
36 | // ...
37 | });
38 | ```
39 |
--------------------------------------------------------------------------------
/contents/pure-collapsible-element.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-06'
4 | tags: HTML
5 | title: Pure collapsible element
6 | ---
7 |
8 | We can create an expandable element with pure HTML tags as following:
9 |
10 | ```html
11 |
12 | Title
13 |
17 |
18 | ```
19 |
20 | I often use the tags when including a long error log on GitHub:
21 |
22 | ```html
23 |
24 | Error log
25 |
TLDR stands for "too long, don't read". But it's too short, isn't it?
34 |
35 |
36 |
37 | A question?
38 |
The answer is hidden initially
39 |
40 | ```
41 |
42 |
--------------------------------------------------------------------------------
/contents/put-special-operators-at-the-beginning-of-a-function.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-23'
4 | tags: JavaScript
5 | title: Put special operators at the beginning of a function
6 | ---
7 |
8 | Usually, we can invoke a function by using the form of Immediately Invoked Function Expression (IIFE).
9 |
10 | ```js
11 | (function (a, b) {
12 | return a + b;
13 | })(4, 2);
14 |
15 | // 6
16 | ```
17 |
18 | Do you know that we get the same result if we omit the parentheses and put + at the beginning as follow:
19 |
20 | ```js
21 | +(function (a, b) {
22 | return a + b;
23 | })(4, 2);
24 |
25 | // 6
26 | ```
27 |
28 | It works because putting `+` at the beginning of function declaration will turn it to an expression, and passing the parameters with `()` at the end will invoke the expression.
29 | It is rare to see that code in development, but it is used in the minifications to save the file size.
30 | In addition to `+`, you can use other operators such as `-`, `!`, `~` and void in the similar way to invoke a function:
31 |
32 | ```js
33 | -function() { ... }();
34 | !function() { ... }();
35 | ~function() { ... }();
36 | void function() { ... }();
37 | ```
38 |
39 | Note that the return value could be different from the original function, for example:
40 |
41 | ```js
42 | !(function () {
43 | return false;
44 | })(); // true
45 | ```
46 |
--------------------------------------------------------------------------------
/contents/quick-query-elements-in-the-console.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-06'
4 | tags: DevTools
5 | title: Quick query elements in the Console
6 | ---
7 |
8 | The Chrome DevTools provides two shortcut methods to query the elements in the page.
9 |
10 | | Method | Indentical method |
11 | | ------ | --------------------------- |
12 | | `$` | `document.querySelector` |
13 | | `$$` | `document.querySelectorAll` |
14 |
15 | The following screenshot demonstrates how to use them in the _Console_.
16 |
17 | 
18 |
19 | ## See also
20 |
21 | - [Locate an element with a given selector](https://phuoc.ng/collection/tips/locate-an-element-with-a-given-selector/)
22 |
--------------------------------------------------------------------------------
/contents/quickly-insert-alternate-characters-while-typing.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2023-09-30'
4 | description: Quickly insert alternate characters while typing
5 | openGraphCover: /og/tips/insert-alternate-characters.png
6 | title: Quickly insert alternate characters while typing
7 | ---
8 |
9 | Do you find yourself typing in languages that use a lot of accent marks or diacritical marks? Or maybe you need to type special characters like currency or mathematical symbols? If so, inserting alternate characters can save you a lot of time and effort.
10 |
11 | Here's how to do it:
12 |
13 | - Click on the text area where you want to insert the character.
14 | - Press and hold down the key for the character you want to insert. For example, if you want to type 'é', hold down the 'e' key.
15 | - After a moment, a menu will appear above the cursor with a list of alternate characters.
16 | - Use your mouse or trackpad to select the character you want to insert, and then release the key.
17 | - The alternate character will be inserted into the text area.
18 |
19 | This technique can be especially helpful when typing in languages with a lot of accent marks or other diacritical marks. It can also be useful for typing special characters like currency symbols or mathematical symbols.
20 |
21 | Try it out for yourself! Type one of the characters 'a', 'e', 'i', 'o' or 'u' and hold it down to see the alternate characters appear.
22 |
23 |
24 | ```css demo.css hidden
25 | textarea {
26 | border: 1px solid rgb(203 213 225);
27 | border-radius: 0.5rem;
28 | padding: 0.5rem;
29 | width: 100%;
30 | height: 16rem;
31 | }
32 | ```
33 | ```html index.html hidden
34 |
35 | ```
36 |
37 |
--------------------------------------------------------------------------------
/contents/quickly-type-color-variables.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/quickly-type-colors.png
4 | created: '2021-05-12'
5 | tags: Visual Studio Code
6 | title: Quickly type color variables
7 | ---
8 |
9 | We often declare variables for colors, mostly at the top of file, as following:
10 |
11 | ```css
12 | :root {
13 | --color-primary: #...;
14 | }
15 | ```
16 |
17 | The colors then can be reused with the `var` function:
18 |
19 | ```css
20 | .btn--primary {
21 | background-color: var(--color-primary);
22 | }
23 | ```
24 |
25 | If you're using Visual Studio Code, then you don't have to type `var(...)` completely. Instead, just type `--` and Visual Studio Code suggests the existing color variables.
26 |
27 | 
28 |
--------------------------------------------------------------------------------
/contents/remove-a-property-from-an-object.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-22'
4 | tags: JavaScript
5 | title: Remove a property from an object
6 | ---
7 |
8 | We can use the ES6 spread operator to remove a property from an object:
9 |
10 | ```js
11 | const { name, ...rest } = { name: 'Foo', age: 20 };
12 |
13 | console.log(name); // 'Foo'
14 | console.log(rest); // { age: '20' }
15 | ```
16 |
17 | It's also possible to remove a dynamic property:
18 |
19 | ```js
20 | const property = 'name';
21 | const { [property]: value, ...rest } = { name: 'Foo', age: 20 };
22 |
23 | console.log(value); // 'Foo'
24 | console.log(rest); // { age: '20' }
25 | ```
26 |
--------------------------------------------------------------------------------
/contents/remove-the-border-from-the-last-navigation-item.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: CSS
5 | title: Remove the border from the last navigation item
6 | ---
7 |
8 | We often use the `:last-child` selector to unapply particular styles (such as `border`) for the last item.
9 | Creating a navigation that each item has a bottom border usually looks like:
10 |
11 | ```css
12 | li {
13 | border-bottom: 1px solid #e5e7eb;
14 | }
15 |
16 | /* Don't add border to the last item */
17 | li:last-child {
18 | border-bottom: none;
19 | }
20 | ```
21 |
22 | Using the `:not` pseudo-class, we can make the code shorter and more easy to maintain with a single CSS declaration:
23 |
24 | ```css
25 | /* Add the border to all items except the last one */
26 | li:not(:last-child) {
27 | border-bottom: 1px solid #e5e7eb;
28 | }
29 | ```
30 |
31 | Another approach is to use the `+` selector:
32 |
33 | ```css
34 | li + li {
35 | border-top: 1px solid #e5e7eb;
36 | }
37 | ```
38 |
39 |
40 | ```html
41 |
47 | ```
48 |
49 | ```css
50 | .demo__list {
51 | border: 1px solid #e5e7eb;
52 | list-style-type: none;
53 | margin: 0;
54 | padding: 0;
55 | width: 16rem;
56 | }
57 | .demo__list li {
58 | padding: 0.25rem 0.5rem;
59 | }
60 | .demo__list li:not(:last-child) {
61 | border-bottom: 1px solid #e5e7eb;
62 | }
63 | .demo__list a {
64 | display: block;
65 | text-decoration: none;
66 | }
67 | ```
68 |
69 |
--------------------------------------------------------------------------------
/contents/replace-multiple-if-statements-with-a-single-switch-statement.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-02-26'
4 | tags: JavaScript
5 | title: Replace multiple if statements with a single switch statement
6 | ---
7 |
8 | We demonstrate the trick with a simple issue: Determine the quarter of a given date.
9 | Since the month in JavaScript is zero-based, the month of a given `date` can be determined as
10 |
11 | ```js
12 | // `date` is the input date
13 | const month = date.getMonth() + 1;
14 | ```
15 |
16 | The quarter is calculated based on the range of month:
17 |
18 | ```js
19 | let quarter = 1;
20 | if (month <= 3) {
21 | quarter = 1;
22 | } else if (month <= 6) {
23 | quarter = 2;
24 | } else if (month <= 9) {
25 | quarter = 3;
26 | } else {
27 | quarter = 4;
28 | }
29 | ```
30 |
31 | It is not easy for us to scan multiple `if` statements above. We can make it more readable with a single `switch (true)` statement:
32 |
33 | ```js
34 | switch (true) {
35 | case month <= 3:
36 | quarter = 1;
37 | break;
38 | case month <= 6:
39 | quarter = 2;
40 | break;
41 | case month <= 9:
42 | quarter = 3;
43 | break;
44 | default:
45 | quarter = 4;
46 | break;
47 | }
48 | ```
49 |
50 | > This trick gives us an idea of using `switch (true)` to make the code more readable. The specific issue in this post, calculating the [quarter of a given date](https://phuoc.ng/collection/1-loc/get-the-current-quarter-of-a-date/), can be done with a [single line-of-code](https://phuoc.ng/collection/1-loc).
51 |
52 | ## See also
53 |
54 | - [Early return](https://phuoc.ng/collection/tips/early-return/)
55 | - [Replace multiple if statements with a lookup table](https://phuoc.ng/collection/tips/replace-multiple-if-statements-with-a-lookup-table/)
56 | - [Use Array.includes for multiple conditionals](https://phuoc.ng/collection/tips/use-array-includes-for-multiple-conditionals/)
57 |
--------------------------------------------------------------------------------
/contents/return-an-object-in-an-arrow-function-quickly.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-25'
4 | tags: JavaScript
5 | title: Return an object in an arrow function quickly
6 | ---
7 |
8 | If you want to return an object literal in an arrow function, just wrap the object in `()`:
9 |
10 | ```js
11 | // Doesn't work
12 | const formatName = (first, last) => {
13 | full: `${first} ${last}`,
14 | short: `${first.charAt(0)} ${last.charAt(0)}`.toUpperCase(),
15 | };
16 |
17 | // Work
18 | const formatName = (first, last) => ({
19 | full: ...,
20 | short: ...,
21 | });
22 |
23 | formatName('John', 'Doe'); // { full: 'John Doe', short: 'J D' }
24 | ```
25 |
--------------------------------------------------------------------------------
/contents/reuse-the-current-color.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Practice
3 | created: '2021-03-03'
4 | tags: CSS
5 | title: Reuse the current color
6 | ---
7 |
8 | Instead of repeating colors in a few places, we can define a value for the `color` property once and reuse it with the `currentColor` keyword.
9 |
10 | ```css
11 | /* Bad */
12 | div {
13 | color: #d1d5db;
14 | background-image: linear-gradient(to bottom, #d1d5db, #fff);
15 | }
16 |
17 | /* Good */
18 | div {
19 | color: #d1d5db;
20 | background-image: linear-gradient(to bottom, currentColor, #fff);
21 | }
22 | ```
23 |
24 | Because the `color` property of an element, if not specified, is inherited from its parent, we can use the `currentColor` keyword in the children of element.
25 |
26 | For example, let's say that we want the color of a link to be the same with its container, a given `div` element:
27 |
28 | ```css
29 | /* Bad: Declare same color in three places */
30 | div {
31 | color: #fff;
32 | }
33 | div a {
34 | border-bottom: 1px solid #fff;
35 | color: #fff;
36 | text-decoration: none;
37 | }
38 |
39 | /* Good */
40 | div {
41 | color: #fff;
42 | }
43 | div a {
44 | border-bottom: 1px solid currentColor;
45 | color: currentColor;
46 | text-decoration: none;
47 | }
48 | ```
49 |
50 | > We often use the `currentColor` keyword in the _camelCase_ format. However, CSS is case-insensitive meaning that `CurrentColor`, `currentcolor` or even `cUrReNtCoLoR` are valid keywords and have the same effect as `currentColor`
51 |
--------------------------------------------------------------------------------
/contents/run-the-last-command-as-the-root-user.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-07'
4 | tags: Command Line
5 | title: Run the last command as the root user
6 | ---
7 |
8 | We often see an error when executing a command that requires the root user permission. For example, installing a npm package as global probably requires `sudo`:
9 |
10 | ```shell
11 | $ npm install -g package-name
12 | ```
13 |
14 | In order to sudo the previous command, usually we press the _Arrow_ key, append `sudo` to the beginning of the command and run it again.
15 |
16 | It turns out that there is an easier, shorter command:
17 |
18 | ```shell
19 | $ sudo !!
20 | ```
21 |
22 | Here `!!` references the last command.
23 |
--------------------------------------------------------------------------------
/contents/save-a-few-bytes-when-checking-the-existence-of-module.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-03-15'
4 | tags: JavaScript
5 | title: Save a few bytes when checking the existence of module
6 | ---
7 |
8 | Say we are creating a library and want to export our APIs to the consumers. In order to support the CommonJS module, we use the following check:
9 |
10 | ```js
11 | if (typeof module !== 'undefined') {
12 | module.exports = ourFunction;
13 | }
14 | ```
15 |
16 | However, it can be shorten and saved a few bytes when being minified:
17 |
18 | ```js
19 | if (typeof module < 'u') {
20 | // ...
21 | }
22 | ```
23 |
24 | I saw that trick when looking at the [Preact](https://preactjs.com)'s [source](https://github.com/preactjs/preact/blob/master/src/cjs.js).
25 |
--------------------------------------------------------------------------------
/contents/select-a-folder-to-upload.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/webkitdirectory.png
4 | created: '2022-09-27'
5 | tags: HTML
6 | title: Select a folder to upload
7 | ---
8 |
9 | We often use the file input to give users a way to choose and upload one or multiple files:
10 |
11 | ```html
12 |
13 |
14 |
15 |
16 |
17 | ```
18 |
19 | The input also has a special attribute named `webkitdirectory` which allows users to choose and upload an entire folder including all of its sub directories recursively.
20 |
21 | ```html
22 |
23 | ```
24 |
25 | It's [supported](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory) in all modern browsers.
26 |
27 | ## See also
28 |
29 | - [Filter file types of a file input](https://phuoc.ng/collection/tips/filter-file-types-of-a-file-input/)
30 |
--------------------------------------------------------------------------------
/contents/select-the-direct-children-of-an-element.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2023-08-29'
4 | description: Select the direct children of an element with JavaScript DOM
5 | openGraphCover: /og/tips/select-direct-children.png
6 | tags: DOM, JavaScript
7 | title: Select the direct children of an element
8 | ---
9 |
10 | When we use the [`querySelectorAll` function](https://phuoc.ng/collection/html-dom/select-an-element-or-list-of-elements/) to select all children of a given element, it fetches all the children, including the nested ones. But what if you only want to retrieve the direct children? That's where the `>` selector comes in handy.
11 |
12 | To make things clearer, here's an example: let's say you want to select all direct children of an element with the ID of `parent`. You can simply use the following code:
13 |
14 | ```js
15 | const parentElement = document.getElementById('parent');
16 | const directChildren = parentElement.querySelectorAll('> *');
17 | ```
18 |
19 | In the code above, we're using `getElementById` to select a parent element with a specific `id` attribute. Then, we use `querySelectorAll` to choose all direct children of that parent element using the `>` selector. The `*` selector matches any element, so this code selects all direct children, no matter their tag name.
20 |
21 | If you only want to select a specific type of tag, you can use that tag name instead of the `*` selector. For example, the following code selects only the `li` tags that are direct children of a given `ul` element.
22 |
23 | This approach excludes any nested `li` tags.
24 |
25 | ```js
26 | const ul = document.getElementById('list');
27 | const directItems = ul.querySelectorAll('> li');
28 | ```
29 |
--------------------------------------------------------------------------------
/contents/separate-list-items.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: CSS
5 | title: Separate list items
6 | ---
7 |
8 | The `content` property of the `:after` selector can be used to separate list items:
9 |
10 | ```css
11 | /* Inline items */
12 | span:not(:last-child):after {
13 | content: ' • ';
14 | }
15 |
16 | /* List items */
17 | li:not(:last-child):after {
18 | content: ',';
19 | }
20 | ```
21 |
22 | The footer of this website uses the same technique.
23 |
24 |
25 | ```html
26 |
39 | ```
40 |
41 | ```css
42 | .demo__tag:not(:last-child):after {
43 | content: ' • ';
44 | }
45 | .demo__item:not(:last-child):after {
46 | content: ',';
47 | }
48 | ```
49 |
50 |
51 | ## See also
52 |
53 | - [Style list items with special characters](https://phuoc.ng/collection/tips/style-list-items-with-special-characters/)
54 |
--------------------------------------------------------------------------------
/contents/set-a-numbering-type-for-a-list-element.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/numbering-type-list-element.png
4 | created: '2022-09-28'
5 | tags: HTML
6 | title: Set a numbering type for a list element
7 | ---
8 |
9 | By default, an unordered list element (`ul`) prepends any of its items a circle. Whereas an ordered list element (`ol`) adds an auto-incremented number at the beginning of each item.
10 |
11 | We can set or change the behavior by setting the `list-style-type` property:
12 |
13 | ```css
14 | ol {
15 | list-style-type: decimal;
16 | }
17 | ul {
18 | list-style-type: disc;
19 | }
20 | ```
21 |
22 | However, the `ul` and `ol` elements come with a more powerful attribute named `type`. It allows us to set the numbering type:
23 |
24 | | Attribute declaration | Items prefixed by |
25 | | --------------------- | ----------------- |
26 | | `` | a, b, c, ... |
27 | | `` | A, B, C, ... |
28 | | `` | i, ii, iii, ... |
29 | | `` | I, II, III, ... |
30 |
31 | ```html
32 |
33 |
34 |
First item
35 |
Second item
36 |
Third item
37 |
38 | ```
39 |
40 | ## See also
41 |
42 | - [Append leading zeros to ordered list items](https://phuoc.ng/collection/tips/append-leading-zeros-to-ordered-list-items/)
43 | - [Create a descending list of numbered items](https://phuoc.ng/collection/tips/create-a-descending-list-of-numbered-items/)
44 | - [Number headings and subheadings automatically](https://phuoc.ng/collection/tips/number-headings-and-subheadings-automatically/)
45 | - [Style index numbers of list items](https://phuoc.ng/collection/tips/style-index-numbers-of-list-items/)
46 | - [Style list items with special characters](https://phuoc.ng/collection/tips/style-list-items-with-special-characters/)
47 |
--------------------------------------------------------------------------------
/contents/set-content-for-an-empty-link.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: CSS
5 | title: Set content for an empty link
6 | ---
7 |
8 | For a link whose content is empty, we can replace the content with the `href` attribute:
9 |
10 | ```css
11 | a[href^='http']:empty:before {
12 | content: attr(href);
13 | }
14 | ```
15 |
16 |
17 | ```html
18 |
19 | ```
20 |
21 | ```css
22 | .demo__link[href^='http']:empty:before {
23 | content: attr(href);
24 | }
25 | ```
26 |
27 |
--------------------------------------------------------------------------------
/contents/share-recommendation-visual-studio-code-extensions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | cover: /assets/tips/share-recommendation-extensions.png
4 | created: '2021-05-17'
5 | tags: Visual Studio Code
6 | title: Share recommendation Visual Studio Code extensions
7 | ---
8 |
9 | When we're working in a team, we would like to use the same set of tools to make the development process more robust and productive.
10 |
11 | With Visual Studio Code, it makes sense to use the same set of extensions.
12 |
13 | You can share the extensions with other people in your team by creating a file `extensions.json` located in the `.vscode` folder.
14 |
15 | ```json
16 | {
17 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "svelte.svelte-vscode"]
18 | }
19 | ```
20 |
21 | The `recommendations` key contains the list of extensions which each of them is defined by an extension identifier.
22 |
23 | > You can see the list of installed extensions by running the command:
24 | >
25 | > ```shell
26 | > $ code --list-extensions
27 | >
28 | > # Example output
29 | > dbaeumer.vscode-eslint
30 | > rvest.vs-code-prettier-eslint
31 | > svelte.svelte-vscode
32 | > ```
33 |
34 | Visual Studio Code will ask your teammates to install the recommended extensions when they open the workspace for the first time. They can do it anytime when clicking `Extensions: Show Recommended Extensions command`.
35 |
--------------------------------------------------------------------------------
/contents/shorten-codes-with-the-comma-operator.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-03-06'
4 | tags: JavaScript
5 | title: Shorten codes with the comma operator
6 | ---
7 |
8 | The comma operator (`,`) used in the following form evaluates the left-hand side (`a`) and returns the value of the right-hand side (`b`):
9 |
10 | ```js
11 | const c = (a, b);
12 | ```
13 |
14 | We can use it to shorten codes in some cases.
15 |
16 | ## Shorten an if statement
17 |
18 | ```js
19 | let result;
20 | if (condition) {
21 | doSomething();
22 | result = value;
23 | } else {
24 | result = other;
25 | }
26 |
27 | // Shorten version
28 | const result = condition ? (doSomething(), value) : other;
29 | ```
30 |
31 | ## Shorten an arrow function
32 |
33 | ```js
34 | const arrowFn = (arg) => {
35 | doSomething();
36 | return result;
37 | };
38 |
39 | // Shorten version
40 | const arrowFn = (arg) => (doSomething(), result);
41 | ```
42 |
43 | Here is a few examples of using the comma operator to [shorten](https://phuoc.ng/collection/1-loc) arrow functions:
44 |
45 | - [Count the occurrences of array elements](https://phuoc.ng/collection/1-loc/count-the-occurrences-of-array-elements/)
46 |
47 | ```js
48 | const countOccurrences = (arr) => arr.reduce((p, c) => ((p[c] = ++p[c] || 1), p), {});
49 |
50 | countOccurrences([2, 1, 3, 3, 2, 3]);
51 | // { '1': 1, '2': 2, '3': 3 }
52 | ```
53 |
54 | - [Split an array into chunks](https://phuoc.ng/collection/1-loc/split-an-array-into-chunks/)
55 |
56 | ```js
57 | const chunk = (arr, s) => arr.reduce((a, e, i) => (i % s ? a[a.length - 1].push(e) : a.push([e]), a), []);
58 |
59 | chunk([1, 2, 3, 4, 5, 6, 7, 8], 3);
60 | // [[1, 2, 3], [4, 5, 6], [7, 8]]
61 | ```
62 |
63 | ## See also
64 |
65 | - [Log a variable in an arrow function](https://phuoc.ng/collection/tips/log-a-variable-in-an-arrow-function/)
66 |
--------------------------------------------------------------------------------
/contents/shorten-import-paths-in-typescript.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-16'
4 | tags: TypeScript
5 | title: Shorten import paths in TypeScript
6 | ---
7 |
8 | In TypeScript, we often use the relative path to import a particular file. Here is an example of importing multiple files from the `helper` and `services` folder:
9 |
10 | ```js
11 | import { validator } from '../../../helpers/validator';
12 | import { authService } from '../../../services/authService';
13 | ```
14 |
15 | The disadvantage is that when we change the directory structure, these imports must be updated accordingly. Despite the fact that popular editors such as Visual Studio Code updates the paths automatically, it doesn't ensure that the process works all the time.
16 |
17 | Fortunately, TypeScript provides the ability of using the absolute paths. In the TypeScript configuration file, `tsconfig.json`, we can indicate the alias for particular paths under the `paths` property.
18 |
19 | For example, the following settings will find all imports starting with `@` in the `src` folder:
20 |
21 | ```json
22 | {
23 | "paths": {
24 | "@/*": ["src/*"]
25 | }
26 | }
27 | ```
28 |
29 | | Import path | Identical absolute path |
30 | | ------------------------ | -------------------------- |
31 | | `@/helpers/validator` | `src/helpers/validator` |
32 | | `@/services/authService` | `src/services/authService` |
33 |
34 | Our imports can be shorten as following:
35 |
36 | ```js
37 | import { validator } from '@/helpers/validator';
38 | import { authService } from '@/services/authService';
39 | ```
40 |
41 | Some libraries, Angular for instance, follows a specific pattern of directory structure, we can pre-define the path for given folders:
42 |
43 | ```json
44 | {
45 | "paths": {
46 | "@helpers/*": ["src/helpers/*"],
47 | "@models/*": ["src/models/*"],
48 | "@services/*": ["src/services/*"]
49 | }
50 | }
51 | ```
52 |
53 | Then the imports of files belonging to these folders can be shorten as below:
54 |
55 | ```js
56 | import { validator } from '@helpers/validator';
57 | import { userModel } from '@models/user';
58 | import { authService } from '@services/authService';
59 | ```
60 |
61 | ## See also
62 |
63 | - [Shorten import paths in Webpack](https://phuoc.ng/collection/tips/shorten-import-paths-in-webpack/)
64 |
--------------------------------------------------------------------------------
/contents/shorten-import-paths-in-webpack.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-03-17'
4 | tags: Webpack
5 | title: Shorten import paths in Webpack
6 | ---
7 |
8 | Say you're using [Webpack](https://webpack.js.org) to bundle your application.
9 | If you think that using a relative path when importing a given file is ugly and hard to maintain when changing the directory structure:
10 |
11 | ```js
12 | import { formatDate } from '../../../helpers/formatDate';
13 | ```
14 |
15 | Then using the aliases is one of the solutions. Webpack allows to map a prefix of import path to a given path. For example, we would like to map all the imports starting with `@` to the `src` folder.
16 |
17 | The Webpack config looks like:
18 |
19 | ```js
20 | // webpack.config.js
21 | const path = require('path');
22 |
23 | module.exports = {
24 | resolve: {
25 | alias: {
26 | // Assume that the `src` folder is located at the root folder
27 | '@': path.join(__dirname, 'src'),
28 | },
29 | },
30 | };
31 | ```
32 |
33 | The helper function mentioned at the top can be shorten as below:
34 |
35 | ```js
36 | import { formatDate } from '@/helpers/formatDate';
37 | ```
38 |
39 | Webpack will be looking for the helper in the `src/helpers/formatDate` file.
40 |
41 | ## See also
42 |
43 | - [Shorten import paths in TypeScript](https://phuoc.ng/collection/tips/shorten-import-paths-in-typescript/)
44 |
--------------------------------------------------------------------------------
/contents/show-a-placeholder-for-an-empty-list.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Tip
3 | created: '2021-02-24'
4 | tags: CSS
5 | title: Show a placeholder for an empty list
6 | ---
7 |
8 | By using the `:empty` selector, it's possible for us to show a custom placeholder.
9 |
10 | ```css
11 | ul:empty::after {
12 | content: 'Empty';
13 | }
14 | ```
15 |
16 | If you want the placeholder to be more flexible instead of being hardcoded in CSS, it can be taken from an attribute, such as `data-placeholder`:
17 |
18 | ```css
19 | ul:empty::after {
20 | content: attr(data-placeholder);
21 | }
22 | ```
23 |
24 | > The `:empty` selector doesn't have effect if the list contains a whitespace or an empty child node
25 |
26 |
27 | ```html
28 |
29 | ```
30 |
31 | ```css
32 | .demo__list {
33 | margin: 2rem;
34 | }
35 | .demo__list:empty::after {
36 | border: 1px solid #e5e7eb;
37 | content: attr(data-placeholder);
38 | padding: 1rem;
39 | }
40 | ```
41 |
42 |
--------------------------------------------------------------------------------
/contents/show-the-first-letter-only.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | category: Trick
3 | created: '2021-03-04'
4 | tags: CSS
5 | title: Show the first letter only
6 | ---
7 |
8 | There are cases that we would like to populate the full text in generated HTML, but only the first letter is displayed.
9 | The remaining characters are hidden visually. It's usesful when we have to support a smaller screen, for example.
10 |
11 | The trick here is to set zero font size for the element, whereas the first letter has a normal font size.
12 |
13 | ```css
14 | .element {
15 | font-size: 0;
16 | }
17 | .element::first-letter {
18 | font-size: 1.5rem;
19 | }
20 | ```
21 |
22 |
23 | ```html
24 |