Skip to content

Instantly share code, notes, and snippets.

@gordonbrander
gordonbrander / h.js
Last active October 5, 2023 23:04
h.js - hyperscript micro implementation
// Set key on object, but only if value has changed.
// This is useful when setting keys on DOM elements, where setting the same
// value may trigger a style recalc.
//
// Note that the typical layout-triggering DOM properties are read-only,
// so this is safe to use to write to DOM element properties.
// See https://gist.github.com/paulirish/5d52fb081b3570c81e3a.
export const prop = (object, key, value) => {
if (object[key] !== value) {
object[key] = value
@gordonbrander
gordonbrander / prop.js
Created September 28, 2023 02:15
prop.js - make DOM fast with a write-through cache
// Set property of element.
// Uses a write-through cache to only set
// when value actually changes.
export const prop = (el, key, value) => {
let cacheKey = Symbol.from(`prop.${key}`)
if (el[cacheKey] != value) {
el[cacheKey] = value
el[key] = value
}
}
@gordonbrander
gordonbrander / async-mailbox.js
Created May 14, 2023 00:46
async-mailbox.js - async generator publisher
export const sleep = ms => new Promise(resolve => {
setTimeout(resolve, ms)
})
export const hz = x => (1/x * 1000)
const complete = Symbol('complete')
export class Mailbox {
#buffer = []
@gordonbrander
gordonbrander / cdom.js
Created March 26, 2023 01:41
cdom.js - cached dom writers that only write if property value changed
// CDOM - Cached DOM
// CDOM minimizes DOM writes by caching last written value and only touching the
// DOM when a new value does not equal the cached value.
// The goal of CDOM is to make it safe to "bash the DOM" as often as you like
// without affecting performance, even bashing it every frame.
// Create a cached setter
export const CachedSetter = (ns, set) => (el, value) => {
let cacheKey = Symbol.for(`CachedSetter::${ns}`)
if (el[cacheKey] !== value) {
@gordonbrander
gordonbrander / modest-example.js
Last active March 25, 2023 19:39
Modest - a modest framework for unambitious web apps. One file. No dependencies. No build steps.
import {StoreElement, change} from './modest.js'
class ModestApp extends StoreElement {
static template() {
return `
<style>
:host {
display: block;
}
.time {
@gordonbrander
gordonbrander / singledispatch.js
Last active October 19, 2023 13:57
singledispatch.js - single dispatch generic function like Python's singledispatch
// Create a Python singledispatch / Clojure multimethod style function that
// dispatches on the first argument.
//
// Returns a wrapped function that calls appropriate underlying function
// based on prototype of first argument.
//
// Custom implementations for specific types can be registered through calling
// `.define(constructor, fun)` on the wrapped function.
export const singledispatch = fallback => {
let _key = Symbol('singledispatch method')
@gordonbrander
gordonbrander / freeze.js
Created March 21, 2023 19:17
freeze.js - immutable JS object helpers
// Shorthand for Object.freeze
export const freeze = Object.freeze
// Define an immutable model intializer.
// Objects are frozen on the way out.
export const Model = init => flags => freeze(init(flags))
// Put a value to a key, returning a new frozen object
export const put = (state, key, val) => freeze({...state, [key]: val})
@gordonbrander
gordonbrander / read-only-proxy.js
Created March 8, 2023 13:55
Ready-only Proxy - a read-only view over JS objects
export const isObject = thing => thing != null && typeof thing === "object"
export class ReadOnlyProxyWriteError extends Error {}
const ReadOnlyProxyDescriptor = {
get: (target, key) => {
let value = target[key]
if (!isObject(value)) {
return value
}
@gordonbrander
gordonbrander / versioned-proxy.js
Created March 6, 2023 14:30
Versioned Proxy - using proxy to increment a version on object mutation
export const $version = Symbol('version')
const VersionedProxy = {
set(target, prop, value) {
target[$version] = target[$version] + 1
target[prop] = value
},
deleteProperty(target, prop) {
target[$version] = target[$version] + 1
@gordonbrander
gordonbrander / rendering-element.js
Created March 6, 2023 13:39
RenderingElement - A custom element that batches updates on animation frame
// RenderingElement
// A custom element that batches state updates on animation frame.
export class RenderingElement extends HTMLElement {
// Element state to render
#state = this.defaults();
// Has an animation frame been scheduled?
#isAnimationFrameScheduled = false
// Perform a render and flag that animationFrame has completed.