This ain't jQuery or React or any of that... This is a JavaScript library for front-end developers who want the full power that modern JavaScript has to offer with minimal bundle sizes, polyfills where needed, and a familiar but terse syntax. It's friendly enough for beginners, but powerful enough for even the most experienced developer.
It supports your
AbortSignal
. It supportstrustedTypes
andSanitizer
andcookieStore
and yournavigator.locks
. And if you call in the next five minutes, I'll even throw in support forarray.groupBy()
&set.difference()
(TC39 proposals) for free!
Were you expecting this to be where I tell you to npm install
something?
Maybe someday it'll be published on NPM, but until then you can either download
it or add it as a submodule. There's just no reason for this to be restricted to
NPM or to rely on it, and we're all using git
anyways, right?
git submodule add https://github.com/shgysk8zer0/std-js.git path/to/use
To Update git submodule update --remote /path/to/use
(assuming you added as a submodule).
Tip 1 Since this is all native JavaScript modules, you can easily plop everything
on some CDN and import { stuff } from 'https://cdn.example.com/js/std-js/stuff.js';
.
Tip 2 Use Dependabot to automatically get Pull Requests when a submodule (like std-js) is updated.
// exports.js
// Export class/function/constant
export function myFunc() {
// Function body
}
function notExported() {
// Function body
}
export function unused() {
// Function body
}
export class MyClass {
// Class body
}
export const FOO = 'bar';
// Spiffy.js
// Default export (can only specify one default and should be only export)
export default class Spiffy {
// Class body
}
// dialog.js
// Does not export anything.
if (! ('HTMLDialogElement' in window)) {
Object.defineProperty(HTMLUnknownElement.prototype, 'open', {
get: function() {
return this.hasAttribute('open');
},
set: function(open) {
if (open) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
this.dispatchEvent(new Event('close'));
}
}
});
}
// main.js
// Import and run dialog.js
import './dialog.js';
// Import specific functions/classes/objects/etc.
// Must be valid relative or absolute path, so relative paths
// must begin with "./" or "../" and must contain extension.
// importing `as` aliases an import, allowing renaming from the name exported
import {myFunc, MyClass as CustomClass} from './exports.js';
// Or import everything into an object / namespace
import * as exports from './exports.js';
/**
* const exports = { myFunc, unused, MyClass, FOO };
*/
// Import default (matching `export default`)
import Spiffy from './Spiffy.js';
// Import everything from a remote script
import 'https://cdn.polyfill.io/v2/polyfill.min.js';
import './shims/sanitizer.js';
import './shims/trust.js';
import './shims/cookieStore.js';
import { html, on, attr, data, supportsElement, toggleClass } from './dom.js';
import { loadStylesheet, loadImage, preconnect } from './loader.js';
import { prefersColorScheme } from './match-media.js';
import { createPolicy } from './trust.js';
import { getJSON } from './http.js';
import { pwned } from './pwned.js';
import { YEARS } from './date-consts.js';
import { md5 } from './hash.js';
import { getBeforeUnloadSignal } from './abort.js';
// std-js does not provide these... They're just here for example
import { title, allowedOrigins } from './consts.js';
import { getCookiesConsent, createResultCard } from './funcs.js';
toggleClass(document.documentElement, {
'no-dialog': ! supportsElement('dialog'),
'no-details': ! supportsElement('details'),
'js': true,
'no-js': false,
});
const policy = createPolicy('default', {
createHTML: input => new Sanitizer().sanitizeFor('div', input).innerHTML,
createScript: () => trustedTypes.emptyScript,
createScriptURL: input => {
if (allowedorigins.includes(new URL(input, document.baseURI).origin)) {
return input;
} else {
throw new Error(`Untrusted script URL: '${input}'`);
}
},
});
const signal = getBeforeUnloadSignal();
data(':root', {
theme: prefersColorScheme(), // 'light' or 'dark'
layout: 'default',
});
loadStylesheet('/style.css');
cookieStore.get({ name: 'cookie-consent' }).then(async cookie => {
if (cookie == null) {
if (await geCookieConsent()) {
cookieStore.set({ name: 'cookie-consent', value: 'granted', expires: 2 * YEARS });
}
}
});
ready({ signal }).then(() => {
html('heading', policy.createHTML(title));
preconnect('https://api.pwnedpasswords.com');
preconnect('https://secure.gravatar.com');
on('input[type="password"]', {
change: async ({ target }) => {
attr('.pwned-notice', { hidden: ! await pwned(target.value) });
}
}, { signal });
on('input[type="email"]', {
change: async ({ target }) => {
if (target.validity.valid) {
const hash = await md5(input.value);
const url = new URL(hash, 'https://secure.gravatar.com/avatar/');
url.searchParams.set('s', 96);
const img = await loadImage(url, { height: 96, width: 96 });
document.getElementById('gravatar-container').replaceChildren(img);
}
}
}, { signal });
on(document.forms.search, {
submit: async event => {
event.preventDefault();
const body = new FormData(event.target);
const results = await getJSON(event.target.action, { body, signal });
document.getElementById('search-results').replaceChildren(...await Array.fromAsync(results));
},
reset: () => document.getElementById('search-results').replaceChildren(),
}, { signal });
});