Sometimes, I'm surprised about how smart TypeScript is. Let's assume you want to type your app's release version numbers. The format is [releaseMonth]-[releaseYear]
. There won't be a new version every month or year.
How hard is it to define a type for this? It's not hard at all.
js
// define release yearstypereleaseYear = '2022' | '2023' | '2024';// define release monthstypereleasteMonth = '03' | '07' | '11';// combine the two types in a template literal typetypeversionNumber = `${releasteMonth }.${releaseYear }`;constType '"04.2022"' is not assignable to type '"03.2022" | "03.2023" | "03.2024" | "07.2022" | "07.2023" | "07.2024" | "11.2022" | "11.2023" | "11.2024"'. Did you mean '"03.2022"'?2820Type '"04.2022"' is not assignable to type '"03.2022" | "03.2023" | "03.2024" | "07.2022" | "07.2023" | "07.2024" | "11.2022" | "11.2023" | "11.2024"'. Did you mean '"03.2022"'?: version versionNumber = '04.2022';
For these situations, TypeScript defines Template Literal Types based on good old template strings. Sweet!
Reply to Stefan ]]>Disclaimer: be aware that turning off password manager functionality is generally a bad idea. The general rule is that it should always be the user's choice when to autofill an input field.
The WCAG Accessible Authentication criterion defines that filling forms with assistance like password managers must be possible. If you go against this rule, there must be a good reason. Ideally, this reason is evaluated with user testing.
A situation I can think of is a field that must have name="name"
for technical reasons but doesn't match the account user name. Constantly popping up completion dialogs can harm UX in this situation. There are probably more situations. But the rule stands: always evaluate if it's worth breaking password autofill functionality for all users.
Thanks to Sandrine and Scott for providing feedback.
Let's get to it!
Flavio shared how to tell 1Password not to interact with an input element on localhost
.
Ignoring the question of when you would want to turn off automatic password filling, how hard can it be to tell browsers and password managers not to mess with an input field?
As Flavio shared, 1Password relies on data-1p-ignore
. Fair enough. This attribute is oddly specific, though.
How can you turn off auto-completion for LastPass users, then? Add data-lpignore="true"
to your input field and change the "Advanced Settings". Easy. For Bitwarden it's data-bwignore
.
But I'm a happy Dashlane user; how about this one? Dashlane created an entire spec called Semantically Annotated Web Forms to handle form completion. An easy data-form-type="other"
will do it for this password manager.
Here's a complete attribute list.
PW manager | Turn off attribute |
---|---|
1Password | data-1p-ignore (Source) |
Lastpass | data-lpignore="true" (Source) |
Dashlane | data-form-type="other" (Source) |
Bitwarden | data-bwignore (Source) |
And all these data attributes bring us to this beautiful markup to disable password managers for a form element.
<input type="password"
autocomplete="off" data-1p-ignore data-bwignore
data-lpignore="true" data-form-type="other">
Isn't it beautiful? And that's only the four password managers I know. I bet there are many others out there...
But to take a step back, isn't the autocomplete="off"
's job to turn off field completion?
As far as I understand, that was the idea behind the attribute, but here's a quote from MDN:
In most modern browsers, setting autocomplete to "off" will not prevent a password manager from asking the user if they would like to save username and password information, or from automatically filling in those values in a site's login form.
Ignoring autocomplete="off"
is a conscious security, accessibility and UX decision. People often know best when they want to autofill a field.
Anyways... do you know any other tricks to turn off password managers? If so, I'd love to hear them because collecting the required attributes for 1Password, Dashlane, Bitwarden and LastPass took me reading way more Stack Overflow threads than it should have.
๐ Shoutout to Kasper Mikiewicz, who provided the Bitwarden attribute.
Reply to Stefan ]]>Here's Kilian over on the Polypane blog with a nice explanation of how to create text that fades out with CSS mask-image
.
Using mask-image
is smart.
[Interactive component: visit the article to see it...]
Nice one! Bookmarked for later.๐
Reply to Stefan ]]>David Darnes shared a nifty tiny trick to build a "scroll to top" component that automatically shows up after you scroll down a little.
And thanks to modern CSS, position: sticky
is all you need. ๐
Here's a scroll-top link in action. ๐
[Interactive component: visit the article to see it...]
But why use 100vh
instead of 100%
?
Fun fact: percentages in margin-top
refer to the logical width of the containing block. margin-top: 50%
isn't half the container's height, but width.
Either way, this is a nifty little CSS trick. Thanks, David!
Reply to Stefan ]]>Removing event listeners from DOM elements is pretty annoying because you must have the registered event handler function at hand.
function handleEvent() { /* ... */ }
// add an event listener
document.querySelector('button')
.addEventListener('click', handleEvent);
// to remove an event listener, the event type ('click')
// and the function ('handleEvent') have to match
// the `addEventListener` call
document.querySelector('button')
.removeEventListener('click', handleEvent);
With somewhat modern JavaScript, you could also use AbortSignals
or the once
config option to remove event listeners, but what if it isn't your source code that's adding event listeners? What if there's one annoying 3rd party script messing with your events and you have to keep it?
Today I learned about a nuclear option to remove all existing event listeners.
const button = document.querySelector('button);
// replace the element with a copy of itself
// and nuke all the event listeners
button.replaceWith(button.cloneNode(true));
Boom! And that's it โ replacing a DOM element with a cloned version of itself removes all previously registered event handlers. Pretty handy!
Event handlers defined in an on*
HTML attribute will be preserved when you clone a DOM element.
<!-- This event handler will be copied when you cloned the node -->
<button onclick="console.log('Still here!')"`>click me</button>
If you want to see this pattern in action, here we go. ๐
[Interactive component: visit the article to see it...]
Reply to Stefan ]]>How do you replace an array element at a given index?
Is this a trick question? Not really.
const numbers = [1, 2, 3, 4, 5];
numbers[1] = 'new value';
console.log(numbers); // [1, 'new value', 3, 4, 5]
But when you're using React or any framework betting on immutability, just changing an array entry will lead to subtle bugs because the array will still be the same object with the same reference. In an immutable world, data updates always have to result in a new object reference.
How can you change an array element via its index and spit out a new array in the same go then? A 12-years-old Stack Overflow question with 1.5m views has tons of advice, but because it's so old and has hundreds of upvotes, it's not showing the most modern solution in the top area.
Let's have a quick look at creative answers.
You certainly could iterate over the array and build up a new one with a good old forEach
loop.
function changeItem(array, index, newValue) {
const newArray = [];
array.forEach((num, i) => {
if (i === index ) return newNumbers.push(newValue);
newArray.push(num);
})
return newArray;
}
const numbers = [1, 2, 3, 4, 5];
const updatedNumbers = changeItem(numbers, 1, 'new value');
console.log(updatedNumbers); // [1, 'new value', 3, 4, 5]
console.log(numbers === updatedNumbers); // false
But this isn't great. Doing it with map
is already a bit nicer...
function changeItem(array, index, newValue) {
return array.map((value, i) => {
if (i === index) return newValue;
return value;
})
}
const numbers = [1, 2, 3, 4, 5];
const updatedNumbers = changeItem(numbers, 1, 'new value');
console.log(updatedNumbers); // [1, 'new value', 3, 4, 5]
console.log(numbers === updatedNumbers); // false
And for the creatives, if you belong to this small group of folks who remember the difference between slice and splice, you could also use one of these.
function changeItem(array, index, newValue) {
return [
...array.slice(0, index),
newValue,
...array.slice(index + 1)
];
}
const numbers = [1, 2, 3, 4, 5];
const updatedNumbers = changeItem(numbers, 1, 'new value');
console.log(updatedNumbers); // [1, 'new value', 3, 4, 5]
console.log(numbers === updatedNumbers); // false
Okay, this one (โ๏ธ) is actually terrible.
But what if I told you that copying an array and changing one entry is cross-browser supported in JavaScript today?
All modern browsers support with()
these days.
And with it, this entire exercise becomes a nifty one-liner.
const numbers = [1, 2, 3, 4, 5];
const updatedNumbers = numbers.with(1, 'new value');
console.log(updatedNumbers); // [1, 'new value', 3, 4, 5]
console.log(numbers === updatedNumbers); // false
Thank you modern JavaScript!
Reply to Stefan ]]>I live in Germany, and while many think we have things in order here, we still need to sort out bureaucracy. It's a clu**fu** at best because German officials rely on paper โ paper and handwritten signatures.
It can go so far that a scanned document image is preferred over a nice and sharp PDF. I don't own a printer, let alone a scanner, so occasionally, I drag myself to the local copy shop to do official business.
Until now! I've been browsing Hacker News, and the shell script below takes a PDF and makes it look like it's been scanned.
#!/bin/sh
ROTATION=$(shuf -n 1 -e '-' '')$(shuf -n 1 -e $(seq 0.05 .5))
convert -density 150 $1 \
-linear-stretch '1.5%x2%' \
-rotate ${ROTATION} \
-attenuate '0.01' \
+noise Multiplicative \
-colorspace 'gray' $2
Suppose you called the script index
, you can now run the following command...
./index.sh original.pdf scanned.pdf
... to scan your PDFs without a physical scanner.
Side note: the convert
command is part of the imagemagick
tools and wasn't available on my machine. You might have to install it first. A brew install imagemagick
did the trick for me, though.
As Thomas Steiner pointed out; you can reach similar visual effects with modern browser APIs right within your browser.
Yoav Kadosh shared a very useGlobalState
React hook.
const store = {};
const listeners = {};
function useGlobalState(key, initialValue) {
const [state, _setState] = useState(store[key] || initialValue);
const setState = useCallback((stateOrSetter) => {
let next = stateOrSetter;
if (typeof stateOrSetter === "function") {
next = stateOrSetter(store[key]);
}
listeners[key].forEach((l) => l(next));
store[key] = next;
}, []);
useEffect(() => {
// Store the initial state on the first call with this key
if (!store[key]) {
store[key] = initialValue;
}
// Create an empty array of listener on the first call with this key
if (!listeners[key]) {
listeners[key] = [];
}
// Register the observer
const listener = (state) => _setState(state);
listeners[key].push(listener);
// Cleanup when unmounting
return () => {
const index = listeners[key].indexOf(listener);
listeners[key].splice(index, 1);
};
}, []);
return [state, setState];
}
You can then use the defined useGlobalState
hook to share or update application state across components. ๐ช
const [state, setState] = useGlobalState('someUniqueKey', INITIAL_STATE);
Are there downsides to this pattern? I don't know, and I'm a little puzzled why I haven't seen this userland hook before, but if you think useGlobalState
is a bad idea, let me know!
If you want to learn more, Yoav explains how it works on his blog!
Reply to Stefan ]]>I've been writing JavaScript for so long that I sometimes don't see its quirks. To be fair, there are fewer quirks than ten years ago, but some things would still make great language additions. One thing that would make a lot of sense is required function parameters.
Assume you have a function that defines required arguments. And you really really want to make sure the function is called with the correct arguments by throwing an error.
Here's what you might do:
function doSomethingWithAThing(theThing) {
if (typeof theThing === "undefined") {
throw new Error("theThing is required");
}
}
That's quite a bit of code for such a simple thing.
Unfortunately, you can't do something like this in JavaScript ...
function doSomethingWithAThing(
theThing = throw new Error("theThing Is Undefined")
) {
// ...
}
... because JavaScript doesn't like it.
Peter Krรถner published a handy snippet that he carries around from project to project. The post is in German, so here's the English gist of it.
First, define a fail
function.
function fail(reason, ErrorConstructor = Error) {
throw new ErrorConstructor(reason);
}
And second... use it. ๐ซฃ
By restructuring the code and transforming the error throwing portion into an expression, you can immediately throw in case a function is called without the required parameters.
function doSomethingWithAThing(
theThing = fail('theThing Is Undefined')
) {
// ...
}
What a beautiful sort of one-line workaround.
Thanks Peter!
Reply to Stefan ]]>Did Chris just publish a post and unveil the solution to a long-lasting CSS problem almost in a side sentence? It seems like it!
What of the many are we walking about? We're talking about animating (or transitioning) an element's height from 0 to something (auto
) just with CSS. That's right โ this is possible in all modern browsers today. No JavaScript required!
Not too long ago, browsers shipped animatable CSS grid columns and rows which you could use to fly in a side menu or do fancy stuff like Michelle Barker. ๐
I've never considered using animated grid rows to hide grid cells, though.
Here's the CSS trick to transition or animate an element's height to auto
:
0fr
grid-template-rows
overflow: hidden
to the one element inside the gridgrid-template-rows
to be 1fr
with a CSS class or attribute selector.grid {
display: grid; /* 1 */
grid-template-rows: 0fr; /* 2 */
transition: grid-template-rows 0.5s ease-in-out; /* 3 */
}
.grid.open {
grid-template-rows: 1fr; /* 5 */
}
.grid-inner {
overflow: hidden; /* 4 */
}
And voila! You can now animate an element's height without any hardcoded values. It's just CSS โ toggle a class and call it a day! CSS Grid figures out all the rest.
[Interactive component: visit the article to see it...]
I. Am. Blown. Away!
Zero-height elements come with accessibility issues because their content can still be reached with assistive technology. The example above implements a mix of aria-hidden
, aria-controls
and aria-expanded
to work around the issues.
Accessibility is tough, though, and I'm no expert. If you have any feedback on the implementation, let me know!
But to Chris' point, even though this technique works, it'd be nice to have an easier way to animate element heights in CSS. I 100% agree but for now, I'll take this approach because it's the best we got! Thanks Chris! ๐ฏ
Reply to Stefan ]]>Wes Bos has advocated using the trash-cli
instead of the rm
command this week.
The reasoning: rm
is unrecoverable. Once you remove files or directories using rm
, they're gone. Ergo, you can bring yourself into real trouble using rm
.
I assume one could do some wild forensic stuff to restore bits and bytes on your hard drive, but the deleted files have to be very important to go this route.
trash
can help out here! The command doesn't delete files but moves them to your operating system's trash directory. When you then accidentally delete something important, head over to your lovely bin icon and recover files from there!
But here's the kicker: installing the global trash-cli package comes with 829 dependency files. Markdown, JavaScript, JSON and license files are all at your service, making up a whopping 4.3MB heavy node_modules
directory.
Sure, 4 MB isn't an issue for me on my developer machine. And the command runs fast enough to protect me and my fat fingers from self-destruction. But still, that's a lot of dependency code to move files into another directory, isn't it?
Liran Tal thought the same and shared a nifty four-liner giving you all of trash
's functionality.
trash() {
echo "[x] moving files to trash..."
mv "$@" "$HOME/.trash"
}
I played around with Liran's function and adjusted it also to support multiple files.
# โ
globs โ `trash file-*-.txt`
# โ
directories โ `trash directory`
# โ
multiple files - `trash file-1 dir-1 file-2`
function trash() {
echo "๐๏ธ Moving files to trash..."
for var in "$@"
do
mv "$var" "$HOME/.trash"
done
}
So far, my new trash
command has been working great. I'll let you know when I'll discover any issues!
Disclaimer: I only tested this shell function in my environment: macOS and ZSH.
Not too long ago, I was looking into refactoring my site to go all in with CSS grid!
I've been aiming for a classical centered blog layout with flexible side margins. The content should be 65 characters wide at max. But (!) I wanted to be able to break free, and some components should go full-width by setting a class. And it should all work with CSS grid.
After fiddling with it for a while, I didn't reach a satisfying result and decided to do it another day. In the meantime, Ryan Mulligan's post "Layout Breakouts with CSS Grid" provided the CSS solution I was looking for (see it in action on CodePen).
Put all your content in a content
wrapper element that defines named grid areas:
.content {
--gap: clamp(1rem, 6vw, 3rem);
--full: minmax(var(--gap), 1fr);
--content: min(50ch, 100% - var(--gap) * 2);
--popout: minmax(0, 2rem);
--feature: minmax(0, 5rem);
display: grid;
grid-template-columns:
[full-start] var(--full)
[feature-start] var(--feature)
[popout-start] var(--popout)
[content-start] var(--content) [content-end]
var(--popout) [popout-end]
var(--feature) [feature-end]
var(--full) [full-end];
}
And then control all the component widths using grid template areas. ๐ฏ
.content > * {
grid-column: content;
}
.popout {
grid-column: popout;
}
.feature {
grid-column: feature;
}
.full {
grid-column: full;
}
The post even includes a beautiful visualization that explains what's going on. ๐
I love it. ๐ Thank you, Ryan!
Reply to Stefan ]]>Jim Nielsen shared how to detect Appleโs โNew Yorkโ font face, and while reading the post, I realized that I'd forgotten about document
. ๐
The CSS Font Loading API enables you to access or load fonts via JavaScript. It's good stuff!
And if you want to test if a browser supports a font, the check
method's your friend!
document.fonts.check('12px ui-serif');
Reply to Stefan
]]>
Many of my projects include Node.js scripts to perform setup or teardown steps. And while I could run all of them with the node
binary (node create-thumbnails
), I prefer to remove the file extension and make the scripts executables (
). This approach saves characters, and makes me feel like a hacker!
My steps to create an executable are:
.js
file extension (mv create-thumbnails.js create-thumbnails
).chmod 744 create-thumbnails
).#!/usr/bin/env node
) to signal that the executing shell should use the node
binary.Et voilร , you just created a Node.js executable!
This approach has served me well for CommonJS-based scripts using the require
function. But it's 2022, and I planned to adopt ECMAScript modules in Node.js executables. Unfortunately, it's not that easy.
This article summarizes the most valuable parts of Axel Rauschmayer's extensive guide "Node.js: creating ESM-based shell scripts for Unix and Windows". Head on over if you want to dive into cross-platform executables.
But what's the problem?
There are three ways to enable ECMAScript modules in Node.js:
.mjs
file extensionpackage.json
with a "type": "module"
fieldnode
with the --input-type=module
flagMy executables should work without a file extension, which rules out the first option. I also don't want to declare all files as module files or add a package
, so the type
field is out, too.
The --input-type
flag looks promising at first, but it only works for strings you pipe into the Node binary yourself.
# pipe JavaScript code into the node binary
echo "import { mkdir } from 'node:fs/promises';" | node --input-type=module
Why's there no flag to enable modules when running a file?
I spent the last 15 minutes reading Node.js issues and discussions about the topic, and I still can't answer this question. If you want to read more, here's a very long GitHub discussion on why such a flag isn't available yet.
Knock yourself out, the discussion takes many turns, and folks have strong opinions!
There are multiple ways to make ECMAScript module-based executables work. They all come with slightly different spins but use the same trick.
Axel recommends the following for UNIX environments.
#!/bin/sh
':' // ; cat "$0" | node --input-type=module - $@ ; exit $?
import * as os from 'node:os';
const {username} = os.userInfo();
console.log(`Hello ${username}!`);
This snippet is wild! In short, it does the following:
cat
command reads the current file (the executable itself)node
with the --input-type
flagnode
, too ($@
)node
call's exit code is caught and propagated to the executableThis instruction makes the file read and pipe itself into the node
binary because there's no flag to enable modules. Wow! ๐คฏ
As said, this post is only for my reference. Here are more resources if you want to learn more about other approaches or make it work on Windows.
Reply to Stefan ]]>Just parking this CSS snippet on my blog for my future self. ๐
If you want to select elements only when there's a given number of them, you can use a :nth-last-child()
selector combination.
The following selector combination is called a "quantity query".
li:nth-last-child(n+3),
li:nth-last-child(3) ~ li {
color: red;
}
And it selects all list elements when there are at least three of them. See it in action below. ๐
[Interactive component: visit the article to see it...]
The CSS selector is hard to read but works. ๐ซฃ
The quantity query above is based on some selector trickery and definitely hard to remember. Luckily, the new :has()
pseudo-class is on its way to cross-browser support to simplify such monstrosities!
With :has()
the quantity query becomes a resonable one-liner that reads as "Select all li
elements in a list that includes a third last child counting from the end."
[Interactive component: visit the article to see it...]
Boom! And that's all it takes to select elements from a list with at least three items. ๐
And even exact matches are possible by selecting "all list elements in a list including a list element which is the third and last child."
[Interactive component: visit the article to see it...]
:has()
is a gift that keeps giving!
Jim Nielsen blogged about a mind-boggling feature of the new :has()
pseudo-class. The pseudo-class isn't cross-browser supported yet, but this CSS addition unlocks countless use cases that Frontend engineers have been dreaming of for years.
Here's the current browser support according to MDN.
When I heard first about :has()
, I thought it's only the long-awaited "parent selector", but Jim shared that it's the "previous sibling selector", too! ๐คฏ
/*
Select every <a> element that's a child of
a <p> element that directly precedes an <hr> element.
*/
p:has(+ hr) a { /* ... */ }
With :has()
entering the web platform, we can select elements in all directions โ we wanted the "parent selector" but will soon have a selector to match the entire family.
Styling the next and previous siblings becomes a nifty one liner. Let's look at a few examples!
Before getting into the new and fancy things, let's recap how to select next (or following) DOM elements.
To select an element's next sibling, use the adjacent sibling selector (+
). The selector will match the element that immediately follows another element.
[Interactive component: visit the article to see it...]
Similarly, the general sibling selector (~
) allows you to select all following elements.
[Interactive component: visit the article to see it...]
Are you ready for the :has()
selector beauty?
We can now invert these selectors with :has()
and match elements in the other direction to select previous siblings. ๐คฏ
Use :has()
with the adjacent sibling selector (+
) to select the immediately previous element...
[Interactive component: visit the article to see it...]
... or the general sibling selector (~
) to match all previous siblings.
[Interactive component: visit the article to see it...]
:has()
is a pretty big deal in CSS land and I can't wait until it finally lands in all browsers, so we can match elements in all directions. Up and down! And left and right โ the CSS future's bright!
I just found a super smart gist from David Wells that's worth bookmarking.
David uses a JavaScript Proxy
to map object properties to fetch
calls.
This approach works quite nice for querying RESTful APIs. apiObject
maps to /cars/
and apiObject
maps to /cars/123/
. ๐
// Found at https://gist.github.com/DavidWells/53518b3c12344952641dc81cc7599939
const createApi = (url) => {
return new Proxy({}, {
get(target, key) {
return async function(id = "") {
const response = await fetch(`${url}/${key}/${id}`)
if (response.ok) {
return response.json();
}
return Promise.resolve({ error: "Malformed Request" })
}
}
})
}
let api = createApi("https://swapi.co/api")
// 'get' request to https://swapi.co/api/people
let people = await api.people()
// 'get' request to https://swapi.co/api/people/1
let person = await api.people(1)
What a great little trick! ๐ฏ
Reply to Stefan ]]>Today Bez Hermoso's git tip passed my Twitter timeline, and it's worth bookmarking it here on my blog.
Whenever you find yourself cd'ing around in a large project just to reach the root of a git repository, the following alias might come in handy.
alias gr='cd $(git rev-parse --show-toplevel)'
It automatically navigates you to the git repo root! ๐ช
Reply to Stefan ]]>I'm a sucker for beautiful interactions. When you're browsing Codepen, you'll find countless examples of people going wild adding fancy effects to elements:
And as much as I like that stuff, I'm hesitant to throw JavaScript onto something tiny like a hover link animation. And additionally, the options to transition an element purely with CSS are limited. Let's take :hover
as an example, there are not many possibilities with two available states (hovered or not hovered). You define a hover state and revert it when the interaction is over. That's it.
Today I found a super smart trick explained by Geoff Graham. Geoff explains how to transition an element's background using pseudo-elements. The catch: the colored background "transitions in" from one side and moves out at another. I haven't seen this effect with pure CSS before. ๐ฒ
[Interactive component: visit the article to see it...]
How does that work? The trick is to define a different transform-origin
. This is a super smart solution, and if you want to learn more, head over to CSS-Tricks to read Geoff's article.
As a quick side note: if you're planning to apply this effect to links in paragraphs, be aware that the used ::before
pseudo-element won't span over line breaks.
This behavior is not a dealbreaker, but yeah... it's why I didn't apply this fancy effect to all my links. ๐คทโโ๏ธ
Reply to Stefan ]]>I haven't used Array
yet, but after reading Dmitri Pavlutin's explainer post, I got super excited about the method because it can make code shorter.
Let's look at a quick numbers example: if you want to filter and map array values you'd probably approach it like the following.
// Filter out negative numbers
// Double the positive numbers
[-3, -1, 1, 3, 5]
.filter((num) => num >= 0)
.map((num) => num * 2);
// -> [2, 6, 10]
There's nothing particularly wrong with chaining filter
and map
calls, but what if I'd tell you that you can do this with a single iteration?
// Filter out negative numbers
// Double the positive numbers
[-3, -1, 1, 3, 5].flatMap((num) => {
if (num >= 0) {
return [num * 2];
}
return [];
});
// -> [2, 6, 10]
Similar to map()
, flatMap()
iterates over every element providing a callback function.
The flatMap
method is identical to a map
followed by a call to flat
of depth 1.
The difference: the mentioned flat
call enables adding and removing elements in a single array iteration. This is impossible using a "normal map
loop".
The callback returns an empty array if the number is negative, leading to element removal. And otherwise, if the element is positive, the callback returns an array with the doubled value. That's the mapping to another value.
If you want to add elements, return an array with multiple values.
And to save some more characters, you could make the snippet even shorter using a ternary operator and filter and map with a sweet one-liner.
[-3, -1, 1, 3, 5]
.flatMap((num) => (num >= 0 ? [num * 2] : []));
// -> [2, 6, 10]
The big question with this line is if that code is easy to read. A filter
/map
combination goes better with my eyes, but it's probably just a matter of time until I get used to seeing these short flatMap
calls.
So, what's the browser support of flatMap
, you may ask? It's pretty green and ready to use!
69 | 69 | 79 | 62 | 62 | 12 | 12 | 10.0 | 69 |
If you want to learn more about flatMap
, check Dmitri's post or the flatMap
MDN documentation.
I came across this shell one-liner command to create a bunch of new directories recursively today! ๐ช
mkdir -p new-dir/{foo,baz}/whatever-{1,2}/{a,b};
# new-dir
# โโโ baz
# โ โโโ whatever-1
# โ โ โโโ a
# โ โ โโโ b
# โ โโโ whatever-2
# โ โโโ a
# โ โโโ b
# โโโ foo
# โโโ whatever-1
# โ โโโ a
# โ โโโ b
# โโโ whatever-2
# โโโ a
# โโโ b
The one-liner's magic is based on two things: mkdir
's -p
flag and a shell feature called brace expansion.
-p
instructs mkdir
to create intermediate directories as required. It's recursive directory creation so to say.
And brace expansion allows magic like the following.
$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
Magic!
Reply to Stefan ]]>I read the new web
course on Forms today and learned a surprising fact about forms in Microsoft Edge.
Did you know that Edge provides a "password reveal" button in password fields? That's right; password fields include a little clickable "eye icon" to show and hide the entered password.
However, a great usability feature only available in one browser isn't great. ๐คทโโ๏ธ If you want to provide password reveal functionality to all browsers, you still have to implement it yourself. And that means you have to get rid of Edge's little gimmick.
To hide Edge's additional password functionality, use this cryptic pseudo-element. ๐
::-ms-reveal {
display: none
}
If you're using Edge, play with the pseudo-element below.
[Interactive component: visit the article to see it...]
And if you want to learn more about this functionaliy, check the Microsoft Edge Reference docs.
Reply to Stefan ]]>Suppose you're working on multiple projects: apart from npm start
, it's very common that projects include very specific commands. There might be make
, last week a looked at grunt
and the "best ones" rely on cryptic command variables or flags.
Nate Meyers' jog eases navigating all these projects.
jog
allows accessing commands from your history filtered by the current working directory. ๐ฒ What a beautiful idea!
The project doesn't come with an automated setup and boils down to two shell functions.
# write all commands and the directory they've been run in to `.zsh_history_ext`
function zshaddhistory() {
echo "${1%%$'\n'}โฎ${PWD} " >> ~/.zsh_history_ext
}
# filter commands by the current working directory
function jog() {
grep -v "jog" ~/.zsh_history_ext | grep -a --color=never "${PWD} " | cut -f1 -d"โฎ" | tail
}
The zshaddhistory
shell hook runs before anything is written to the default history file (~/
). You can leverage this hook to create another history file that includes the ran command but also the directory it was used in.
jog
then filters all entries and presents the ones run in the current working directory.
That's great and all, but I went on and tweaked things a little bit.
I adjusted the jog
functions and added/changed the following:
jog
to lc
("last commands" โ way better!)~/.lc_history
ls
, git
, cd
, etc.)fzf
for some nice interactivityBe warned; I'm not a shell expert, but my "try and error script" below works fine. (I'm up for improvements, though!)
# things for the `lc` command
LC_DELIMITER_START="โฎ";
LC_DELIMITER_END="โญ";
# write current command location to `.lc_history` whenever a command is ran
# `.lc_history` is used in `lc` command
function zshaddhistory() {
# ignore empty commands
if [[ $1 == $'\n' ]]; then return; fi
# ignore specific commands
local COMMANDS_TO_IGNORE=( ls ll cd j git gss gap lc ggpush ggpull);
for i in "${COMMANDS_TO_IGNORE[@]}"
do
if [[ $1 == "$i"* ]]; then
return;
fi
done
echo "${1%%$'\n'}${LC_DELIMITER_START}${PWD}${LC_DELIMITER_END}" >> ~/.lc_history
}
# `lc` โ last command
function lc() {
SELECTED_COMMAND=$(grep -a --color=never "${PWD}${LC_DELIMITER_END}" ~/.lc_history | cut -f1 -d "${LC_DELIMITER_START}" | tail -r | fzf);
# handle case of selecting no command via fzf
if [[ ${#SELECTED_COMMAND} -gt 0 ]]; then
echo "Running '$SELECTED_COMMAND'..."
echo "**************************"
eval " $SELECTED_COMMAND";
fi
}
Before you copy the snippet above, make sure to have a look at my
file to see the most recent implementation. I'll continue tweaking it.
And with that, have fun!
Reply to Stefan ]]>When dealing with user-generated content, there's a high chance that you have to deal with strings full of Emojis. Emoji rendering can come with challenges, so you may want to detect when strings include Emojis and replace them with images.
Let's find out how to spot all these cute symbols!
There are Emoji edge cases when using the described Unicode property escapes. Make sure to read to the end of the article!
Luckily, JavaScript regular expressions come with a Unicode mode these days.
MDN describes that Unicode mode treats a regular expression pattern as a sequence of Unicode code points instead of code units.
There's more to it, though. When you enable Unicode mode in a regular expression, you can use Unicode property escapes. Unicode property escapes (\p{}
or \P{}
) allow you to match Unicode characters based on their properties and characteristics.
That's right; you can match currency symbols, non-Latin characters, and, you guessed it, Emojis!
Here's an example snippet:
const emojiRegex = /\p{Emoji}/u;
emojiRegex.test('โญ'); // true
// The capital 'p' negates the match
const noEmojiRegex = /\P{Emoji}/u;
noEmojiRegex.test('โญ'); // false
If you want to replace and alter Emojis in JavaScript strings, you can do that with String
, too.
// Note the 'g' flag to replace allEmojis
'๐โ๐โโญ'.replaceAll(/\p{Emoji}/ug, '_'); // '_โ_โ_'
The browser support for for Unicode property escapes looks pretty good, too! ๐
64 | 64 | 79 | 78 | 78 | 11.1 | 11.1 | 9.0 | 64 |
Unfortunately, as always, it's more complicated than that. Before going all in with \p{Emoji}
, let's dig deeper!
After publishing this blog post someone reached out to point out that \p{Emoji}
is matching digets and other characters, too. ๐ฒ
const emojiRegex = /\p{Emoji}/u;
emojiRegex.test('1'); // true
emojiRegex.test('*'); // true
emojiRegex.test('#'); // true
You propably don't want to include these codepoints in your Emoji detection because they're usually displayed as a normal text-based characters.
What counts as Emoji and what doesn't, then?
I'd say every tiny rendered comic icon counts, but unfortunately Emoji rendering depends on the operating system and the installed fonts. Just because you see a cute Emoji in front of you, it doesn't mean that someone else sees it, too.
And to make it more complicated: just because you see one rendered Emoji image, it doesn't mean that it's a single codepoint. It can be a combination of multiple Emojis and special characters.
If you have comments on Emojis detection in JavaScript, please give me a shoutout on Twitter or write me a good old email. I'm keen on learning more about it!
Mathias Bynes pointed out that there are shortcomings with this approach of Emoji detection. A property escape such as \p{Emoji}
matches every single Emoji code point and this can be a problem.
Let's have a look at an example:
"๐จโ๐ฉโ๐งโ๐ฆ".replaceAll(/\p{Emoji}/gu, '-'); // '----'
Various Emojis, such as the "Family" one, are rendered as a single symbol but consist of more than one code point. Unicode property escapes match every one of them so that you might run into unexpected behavior.
If you wonder what could count as an Emoji have a look at this extensive list.
There's a reason why Mathias' emoji-regex
package has 49 million weekly downloads, so make sure to check it out!
Here's a quick snippet I found on CSS Tricks for later use.
SVG favicons are almost cross-browser supported. Unfortunately, Safari is not ready yet, and that's why we still need to define an ico
favicon. Use the sizes="any"
attibute to avoid Chromium browsers loading both favicon files (ico
and svg
).
<!-- Avoid that Chromiums download ico and svg ๐ -->
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
Reply to Stefan
]]>