Code is…
- …written by humans
- …read by humans
- …called (as an API) by humans
So its User Experience (UX) matters. Its usability matters. Its UI matters.
So its User Experience (UX) matters. Its usability matters. Its UI matters.
“User experience (UX) refers to a person's total experience using a particular product, system or service.”
“Usability is the ease of use and learnability of a human-made object.”
“The User Interface (UI) is everything designed into an information device with which a human being may interact”
let isBinA = !!(a.compareDocumentPosition(b) &
Node.DOCUMENT_POSITION_CONTAINS);
let isBinA = a.contains(b);
<circle cx="50" cy="50" r="40" />
// Complex/rare case
let evt = new KeyboardEvent("keyup", {
key: "Enter", code: "Enter",
shiftKey: true
});
textarea.dispatchEvent(evt);
// Simple/common case
button.click();
// Common case: standard, minified JSON
JSON.stringify(data);
// Complex case: custom serialization, formatting
JSON.stringify(data, (key, value) => {
if (value instanceof HTMLElement) {
return value.outerHTML;
}
return value;
}, "\t");
// Simplest case: replace string with string
text.replaceAll(" ", "\t");
// Common case: replace regex with string
text.replaceAll(/^\s+/mg, "\t");
// Complex case: replace with function
text.replaceAll(/-[a-z]/g, match => match.toUpperCase());
// Common case: get safe HTML from untrusted input
const sanitizer = new Sanitizer();
container.replaceChildren( sanitizer.sanitize(untrusted_input)) );
// Complex case: configure what’s allowed
const sanitizer = new Sanitizer({
allowElements: ['span', 'em', 'strong', 'b', 'i'],
allowAttributes: {'class': ['*']}
});
container.replaceChildren( sanitizer.sanitize(untrusted_input)) );
“The best error message is the one that never shows up”
> document.body.removeChild(div)
Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
element.remove();
> document.documentElement.insertBefore(ref, div)
Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
element.before(ref);
> let ctx = canvas.getContext("2d", {alpha: false})
> let ctx2 = canvas.getContext("2d", {alpha: true});
> ctx === ctx2
< true
> ctx2.getContextAttributes().alpha
< false
let ctx = canvas.getContext("2d");
ctx.font = "bold 48px serif"; // Just like CSS’s font property
ctx.fillStyle = "hsl(330 50% 50%)"; // Just like CSS <color> values
ctx.fillText("Consistency", 0, 0);
filter: progid:DXImageTransform.Microsoft.gradient(
startColorstr='#9acd32',
endColorstr='#80000000'
);
color: lab(50% 30 40);
<!-- SVG Gradient: -->
<linearGradient id="g" gradientTransform="rotate(-50 0 .7)">
<stop position="0" stop-color="yellowgreen">
<stop position=".3333" stop-color="gold">
<stop position=".6666" stop-color="red">
<stop position="1" stop-color="black" stop-opacity=".5">
</linearGradient>
let x1 = canvas.width, y0 = canvas.height;
let gradient = ctx.createLinearGradient(0, y0, x1, 0);
gradient.addColorStop(0, 'yellowgreen');
gradient.addColorStop(1 / 3, 'gold');
gradient.addColorStop(2 / 3, 'red');
gradient.addColorStop(1, 'rgb(0 0 0 / .5)');
/* CSS linear gradient: */
background: linear-gradient(to top right,
yellowgreen,
gold,
red,
rgb(0 0 0 / .5));
> new URL("foo.txt")
Uncaught TypeError: Failed to construct 'URL': Invalid URL
> new URL("foo.txt", location)
< URL {href: "https://projects.verou.me/jsux/foo.txt", …}
> CSS.registerProperty({name: "--color", syntax: "<color>"});
Uncaught TypeError: Failed to execute 'registerProperty' on 'CSS': required member inherits is undefined.
> CSS.registerProperty({name: "--color", syntax: "<color>", inherits: true});
Uncaught DOMException: Failed to execute 'registerProperty' on 'CSS': An initial value must be provided if the syntax is not '*'
👎 history.pushState(null, "", "https://lea.verou.me"); // (actual)
👍 history.pushState("https://lea.verou.me"); // (fictional)
👎 JSON.stringify(data, null, "\t"); // (actual)
👍 JSON.stringify(data, "\t"); // (fictional)
element.cloneNode(true);
element.clone({deep: true})
element.addEventListener("focus", callback, true);
element.addEventListener("focus", callback, {
capture: true
});
element.addEventListener("focus", callback, {
capture: true,
once: true
});
let event = document.createEvent('KeyboardEvent');
event.initKeyEvent("keypress", true, true, null, null,
true, false, true, false, 9, 0);
let event = new KeyboardEvent("keypress", {
ctrlKey: true,
shiftKey: true,
keyCode: 9
});
oldChild.parentNode.replaceChild(newChild, oldChild)
oldChild.parentNode.replaceChild(oldChild, newChild)
oldChild.replaceWith(newChild)
// Change these numbers to see what each one is for...
ctx.ellipse(100, 100, 50, 75, .785398);
ctx.ellipse({
center: [100, 100],
radius: [50, 75],
rotation: .785398
});
ctx.ellipse({
cx: 100,
cy: 100,
rx: 50,
ry: 75,
rotation: .785398
});
“Code is written once but read many times”
Unknown
(50000).toLocaleString("en", {
style: "currency",
currency: "EUR",
currencyDisplay: "name"
}); // "50,000.00 euros"
> new URL("https://lea.verou.me")
< URL {href: "https://lea.verou.me", …}
> new URL(new URL("https://lea.verou.me"))
< URL {href: "https://lea.verou.me", …}
> new URL(location)
< URL {href: "https://projects.verou.me/jsux/index.html", …}
for (let img of document.querySelectorAll("img:nth-of-type(n+4)")) {
img.setAttribute("loading", "lazy");
img.setAttribute("importance", "low");
img.setAttribute("decoding", "async");
}
let images = document.querySelectorAll("img:nth-of-type(n+4)");
images.setAttribute({
loading: "lazy",
importance: "low",
decoding: "async"
});
$("img:nth-of-type(n+4)").attr({
loading: "lazy",
importance: "low",
decoding: "async"
});
parent.append(element1, "Some text", element2)
parent.append(...[element1, "Some text", element2])
> primes = new Set([2, 3, 5])
< Set {2, 3, 5}
> primes.add(7, 11, 13)
< Set {2, 3, 5, 7}
> new Set(42)
Uncaught TypeError: number 42 is not iterable (cannot read property Symbol(Symbol.iterator))
> new Set([42])
< Set {42}
Object.defineProperty(obj, "property1", descriptor1);
Object.defineProperties(obj, {
property1: descriptor1,
property2: descriptor2,
...
});
element.querySelectorAll(".foo")
element.find(".foo")
Describe what it does, not how it works
element.addEventListnener("click", callback)
element.bind("click", callback)
element.on("click", callback)
element.addEvent("click", callback)
element.getBoundingClientRect().bottom
element.rect().bottom
element.rect.bottom