whoami
=== andrzejfriczeDisclaimer
No more "undefined is not a function"
let number = 0 || 1;
// number === 1
number = 0 ?? 1;
// number === 0
let string = '' || 'string';
// string === 'string'
string = '' ?? 'string';
// string === ''
const ArticleView = (props) => {
const value = props.article.value;
// Uncaught TypeError: Cannot read properties of undefined (reading 'value')
const maybeValue = props.article?.value;
// maybeValue === undefined
…
};
Native async possibilities ⌛️
Collect the results of all given promises. Even those that threw an error
Promise.allSettled
1// OLD
Promise.all([Promise.resolve(10), Promise.resolve(20)])
=== [10, 20]
// OLD
Promise.all([Promise.resolve(10), Promise.reject(20)])
=== Promise {<rejected>: 20}
// NEW 🔥
Promise.allSettled([Promise.resolve(10), Promise.reject(20)])
=== [
{status: 'fulfilled', value: 10},
{status: 'rejected', reason: 20},
]
From event-based to Promise-based APIs
Promise.withResolvers
1const getRequestPromise = () => {
const {
promise, resolve, reject
} = Promise.withResolvers();
asyncRequest(config, response => {
const buffer = [];
response.on('data', data => buffer.push(data));
response.on('end', () => resolve(buffer));
response.on('error', reason => reject(reason));
});
return promise;
}
Array.fromAsync
for consuming asynchronous iterables 1const asyncIterable = (async function* () {
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(
resolve, 10 * i
));
yield i;
}
})();
await Array.fromAsync(asyncIterable) === [0, 1, 2, 3, 4]
The end of lodash? 🤯
arr.at(), arr.findLast()
1const arr = [5, 12, 50, 130, 44];
arr.at(3) === arr[3] === 130;
arr.at(-2) === arr[4] === 130;
arr.find((element) => element > 45)
=== 50
arr.findLast((element) => element > 45)
=== 130
Safe state changes in React with…
Array.prototype.toSorted()
Array.prototype.toReversed()
Array.prototype.toSpliced()
Array.prototype.with()
const [posts, setPosts] = useState([]);
setPosts(posts => posts.with(
3,
{...posts[3],
"title": "Updated post title"})
);
Object.groupBy()
Map.groupBy()
.addEventListener()
on your own objects
class Counter extends EventTarget {
constructor(initialValue = 0) {
super();
this.value = initialValue;
}
#emitChangeEvent() {
this.dispatchEvent(new CustomEvent("valuechange", { detail: this.value }));
}
increment() {
this.value++;
this.#emitChangeEvent();
}
decrement() {
this.value--;
this.#emitChangeEvent();
}
}
const counter = new Counter(0);
counter.addEventListener("valuechange", (event) => {
document.querySelector("#currentValue").innerText = event.detail;
});
height: 45dvh
No more margin problems with
.flex-gap {
display: inline-flex;
flex-wrap: wrap;
gap: 12px;
}
.grid-gap {
display: grid;
// gap: 12px 20px;
column-gap: 12px;
row-gap: 20px;
}
Interop 2023
also…
Interop 2023
Want to support older browsers?
display: contents
to imitate subgridSupported since 2018
Part of new containment spec. Partial implementation in Firefox and Safari
content-visibility
All browsers since 2022
contain: strict
Interop 2023
Interop 2023
Interop 2023
:is(), :where()
:is()
/* Level 0 */
h1 {
font-size: 30px;
}
/* Level 1 */
section h1,
article h1,
aside h1,
nav h1 {
font-size: 25px;
}
/* Level 2 */
section section h1,
section article h1,
section aside h1,
section nav h1,
article section h1,
article article h1,
article aside h1,
article nav h1…
:is()
🔥/* Level 0 */
h1 {
font-size: 30px;
}
/* Level 1 */
:is(section, article, aside, nav) h1 {
font-size: 25px;
}
/* Level 2 */
:is(section, article, aside, nav) :is(section, article, aside, nav) h1 {
font-size: 20px;
}
@import url(headings.css) layer(default);
@import url(links.css) layer(default);
@import url(links-dimmed.css) layer(dimmed);
@layer default {
audio[controls] {
display: block;
}
}
Interop 2024
// that's native CSS! 🥳
input {
/* styles for input not in a label */
border: tomato 2px solid;
}
label {
/* styles for label */
font-family: system-ui;
font-size: 1.25rem;
& input {
/* styles for input in a label */
border: blue 2px dashed;
}
}
Easier declarations, easier animations
// old transform property
.logo {
transform: rotate(45deg) scale(2) translate(40px, 0);
}
// new, individual properties
.logo {
rotate: 45deg;
scale: 2;
translate: 40px, 0;
}
Interop 2024
text-wrap: balance
unbalanced: https://codepen.io/wuuuuuuuuut/pen/RwOxVQe
vs balanced: https://codepen.io/wuuuuuuuuut/pen/QWPaMWQ
No more :before
and weird borders
text-decoration
magic (since 2020)lh
unit for vertical rhythm (2023)Custom
Highlight APIAvailable since 2021
accent-color
Widely available since 2019…
<datalist>
- predefined autocomplete for forms 1This one is also immoral 😅
<details>
TC39 Stage 2
console.log(
chalk.dim(
`$ ${Object.keys(envars)
.map(envar =>
`${envar}=${envars[envar]}`)
.join(' ')
}`,
'node',
args.join(' ')));
Object.keys(envars)
.map(envar =>
`${envar}=${envars[envar]}`)
.join(' ')
|> `$ ${%}`
|> chalk.dim(
%, 'node', args.join(' '))
|> console.log(%);
TC39 Stage 2
const proposal = #{
id: 1234,
title: "Record & Tuple proposal",
contents: `...`,
// tuples are primitive types so you can put them in records:
keywords: #["ecma", "tc39", "proposal", "record", "tuple"],
};
// Accessing keys like you would with objects!
proposal.title === "Record & Tuple proposal"
proposal.keywords[1] === "tc39"
// Spread like objects!
const proposal2 = #{
...proposal,
title: "Stage 2: Record & Tuple",
};
proposal2.title === "Stage 2: Record & Tuple"
proposal2.keywords[1] === "tc39"
// Object functions work on Records:
Object.keys(proposal) === ["contents", "id", "keywords", "title"]
Interop 2024 – almost there!
.getAnimations()
APIPromise.all(
elem
.getAnimations({ subtree: true })
.map((animation) => animation.finished),
).then(() => elem.remove());
Firefox - behind flag
Safari - no implementation
Polyfill available
Firefox, Safari - no implementation. Can't polyfill 🤷🏼♂️
<template>
<section>
<h1 id="name">{{}}</h1>
Email: <a id="link" href="{{}}">{{}}</a>
</section>
</template>
{{}}
is a reference to a specific place in DOM tree<template>
<section>
<h1 id="name">{{}}</h1>
Email: <a id="link" href="{{}}">{{}}</a>
</section>
</template>
https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Template-Instantiation.md
Chat with me about: