Skip to content

Instantly share code, notes, and snippets.

View ryanmorr's full-sized avatar

Ryan Morr ryanmorr

View GitHub Profile
@ryanmorr
ryanmorr / port.js
Last active March 29, 2024 06:40
Port a method to be used standalone
function port(fn) {
return Function.prototype.bind.call(Function.call, fn);
}
// Usage:
// port the `forEach` method of the Array prototype
const each = port([].forEach);
// Use standalone by passing the contextual object as the first argument
each(document.querySelectorAll('div'), (el) => console.log(el));
@ryanmorr
ryanmorr / is-plain-object.js
Last active March 29, 2024 06:40
Check if an object is an object literal or bare object
function isPlainObject(value) {
if (!value || typeof value !== 'object') {
return false;
}
const prototype = Object.getPrototypeOf(value);
return prototype === null || prototype === Object.getPrototypeOf({});
}
// Usage:
@ryanmorr
ryanmorr / scope.js
Last active March 29, 2024 06:40
Emulate the with statement
function scope(obj, callback) {
const source = callback.toString();
const vars = Object.getOwnPropertyNames(obj).map((key) => `${key} = this['${key}']`);
const body = source.substring(source.indexOf('{') + 1, source.lastIndexOf('}'));
const fn = new Function(`var ${vars.join(',')}; ${body}`);
fn.call(obj);
}
// Usage:
@ryanmorr
ryanmorr / tiny-vdom.js
Last active March 29, 2024 06:38
A tiny virtual DOM implementation
function updateAttribute(element, name, newVal, oldVal = null) {
if (name[0] === 'o' && name[1] === 'n') {
name = name.slice(2).toLowerCase();
if (newVal) {
element.addEventListener(name, newVal);
}
if (oldVal) {
element.removeEventListener(name, oldVal);
}
} else if (newVal == null || newVal === false) {
@ryanmorr
ryanmorr / callback-middleware-proxy.js
Last active March 29, 2024 06:37
Add middleware to any callback function
function callbackMiddlewareProxy(callback) {
const middleware = [];
const push = middleware.push.bind(middleware);
return new Proxy(callback, {
get: (target, prop) => {
if (prop === 'use') {
return push;
}
return target[prop];
},
@ryanmorr
ryanmorr / templit.js
Created March 29, 2024 06:27
Make a static template literal string come alive
function templit(tpl, tag) {
let exec;
return (data) => {
if (!exec) {
const vars = Object.getOwnPropertyNames(data).map((key) => `${key} = this['${key}']`);
exec = tag == null
? new Function(`var ${vars.join(',')}; return \`${tpl}\``)
: new Function('__tag', `var ${vars.join(',')}; return __tag\`${tpl}\``);
}
return tag == null ? exec.call(data) : exec.call(data, tag);
@ryanmorr
ryanmorr / pathfinder.js
Last active March 29, 2024 03:11
Get and set deeply nested object properties via paths
const has = {}.hasOwnProperty;
function getPath(root, tester, path = []) {
for (const key in root) {
if (has.call(root, key)) {
const value = root[key];
if (tester(key, value) === true) {
path.push(key);
return path;
}
@ryanmorr
ryanmorr / is-constructable.js
Last active March 29, 2024 00:30
Detect if a function is a constructor
// https://stackoverflow.com/a/46759625
function isConstructable(fn) {
try {
Reflect.construct(function () {}, [], fn);
return true;
} catch (e) {
return false;
}
}
@ryanmorr
ryanmorr / unload.js
Last active March 29, 2024 00:30
The MDN recommended way of executing unload code
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon#sending_analytics_at_the_end_of_a_session
const callbacks = [];
function unload(callback) {
callbacks.push(callback);
};
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
@ryanmorr
ryanmorr / state-generator.js
Last active March 29, 2024 00:30
Simple redux-style state machine via a generator function
function *stateGenerator(state, reducer) {
let action;
while (true) {
if (action) {
state = reducer(state, action);
}
action = yield state;
}
}