Skip to content

Instantly share code, notes, and snippets.

@FOBshippingpoint
Last active February 22, 2026 04:47
Show Gist options
  • Select an option

  • Save FOBshippingpoint/94ecfdd661a82548e94aab57e7c3418e to your computer and use it in GitHub Desktop.

Select an option

Save FOBshippingpoint/94ecfdd661a82548e94aab57e7c3418e to your computer and use it in GitHub Desktop.
Handy userscript toolkit
/**
* Selects a single element from the DOM.
*
* @param selectors - Either a CSS selector string or an existing DOM element.
* @returns The selected element or null if not found.
*/
var $ = document.querySelector.bind(document);
/**
* Selects multiple elements from the DOM and returns them as an array.
*
* @param selectors - Either a CSS selector string or an existing DOM element.
* @returns An array of the selected elements with additional methods.
*/
var $$ = (selector) => [...document.querySelectorAll(selector)];
/**
* Creates a new HTML element with the specified tag name and optional attributes.
*
* This is an alias for `document.createElement`.
*
* @param tagName - The name of the HTML element to create.
* @param options - Optional attributes for the element.
* @returns The created HTML element.
*/
var $$$ = document.createElement.bind(document);
/**
* Converts an HTML string into either a single Element or DocumentFragment
*
* @param html - The HTML string to be converted
* @returns A single Element if the HTML contains one root element, otherwise returns a DocumentFragment containing all elements
*
* @example
* // Returns a strongly-typed HTMLDivElement
* const div = h<HTMLDivElement>('<div>Hello</div>');
*
* @example
* // Returns a DocumentFragment containing multiple elements
* const fragment = h('<div>First</div><div>Second</div>');
*/
var h = (html) => {
const template = $$$("template");
template.innerHTML = html;
if (template.content.childElementCount === 1) {
return template.content.firstElementChild;
} else {
return template.content;
}
};
/**
* Add CSS stylesheet to current document.
*
* @param {string} css - CSS stylesheet to adopt.
* @returns {CSSStyleSheet} stylesheet - injected CSS stylesheet.
*/
var addCss = (css) => {
const extraSheet = new CSSStyleSheet();
extraSheet.replaceSync(css);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, extraSheet];
return extraSheet;
};
/**
* Mock browser's $x helper function in devtools.
*
* @param {string} xpath - the XPath to be evaluated.
* @see {@link https://devhints.io/xpath}
*/
var $x = (xpath) => {
const xpathResult = document.evaluate(
xpath,
document,
null,
XPathResult.ANY_TYPE,
null,
);
const elements = [];
let el;
while ((el = xpathResult.iterateNext())) {
elements.push(el);
}
return elements;
};
/**
* Write text to clipboard.
* Use Clipboard API if possible, fallback to document.exec when failed.
*
* @param {string} text - string to copy.
*/
var copyText = async (text) => {
if ("clipboard" in navigator) {
/* Won't work under HTTP */
navigator.clipboard
.writeText(text)
.then(() => {
console.log("Text copied");
})
.catch((err) => console.error(err.name, err.message));
} else {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.opacity = 0;
document.body.appendChild(textArea);
textArea.focus({ preventScroll: true });
textArea.select();
try {
const success = document.execCommand("copy");
console.log(`Text copy was ${success ? "successful" : "unsuccessful"}.`);
} catch (err) {
console.error(err.name, err.message);
}
document.body.removeChild(textArea);
}
};
/**
* Forked from https://github.com/violentmonkey/vm-dom/blob/master/src/index.ts
*
* Observe an existing `node` until `callback` returns `true`.
* The returned function can be called explicitly to disconnect the observer.
*
* ```js
* observe(document.body, () => {
* const node = document.querySelector('.profile');
* if (node) {
* console.log('It\'s there!');
* return true;
* }
* });
* ```
*/
var observe = (node, callback, options) => {
const observer = new MutationObserver((mutations, ob) => {
const result = callback(mutations, ob);
if (result) disconnect();
});
observer.observe(node, {
childList: true,
subtree: true,
...options,
});
const disconnect = () => observer.disconnect();
return disconnect;
};
/**
* Wait until querySelector found.
* ```js
* until("#username", (input) => {
* input.value = "admin";
* });
* ```
*/
var until = (querySelector, callback) => {
const target = $(querySelector);
if (target) {
callback(target);
} else {
observe(document, (mutations) => {
const target = $(querySelector);
if (target) {
callback(target);
return true;
}
});
}
};
/**
* Creates a debounced function that delays invoking callback until after waitMs
* milliseconds have elapsed since the last time the debounced function was invoked.
* The callback is invoked with the last arguments provided to the debounced function.
*
* @typeParam T - The type of function to debounce
* @param callback - The function to debounce
* @param options - The options object
* @param options.isLeadingEdge - Whether to invoke on the leading edge of the timeout
* @param options.waitMs - The number of milliseconds to delay
* @returns A new debounced function
*
* @example
* ```javascript
* // Avoid costly calls while typing
* const debouncedSave = debounce((text: string) => saveToServer(text));
* input.addEventListener('input', (e) => debouncedSave(e.target.value));
* ```
*/
var debounce = (
callback,
{ isLeadingEdge, waitMs } = {
isLeadingEdge: false,
waitMs: 3000, // 3 sec delay before saving
},
) => {
let timeoutId;
return (...args) => {
const isCallNow = isLeadingEdge && !timeoutId;
clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
timeoutId = undefined;
if (!isLeadingEdge) {
callback(...args);
}
}, waitMs);
if (isCallNow) {
callback(...args);
}
};
};
///////////////////////////////////////////////////////////////////////////
///////////////////////////////INITIALIZATION//////////////////////////////
///////////////////////////////////////////////////////////////////////////
if (EventTarget.prototype.on) {
console.warn(`EventTarget.prototype.on already in use.`);
} else {
EventTarget.prototype.on = Element.prototype.addEventListener;
}
if (EventTarget.prototype.off) {
console.warn(`EventTarget.prototype.off already in use.`);
} else {
EventTarget.prototype.off = EventTarget.prototype.removeEventListener;
}
if (Element.prototype.$) {
console.warn(`Element.prototype.$ already in use.`);
} else {
Element.prototype.$ = Element.prototype.querySelector;
}
if (Element.prototype.$$) {
console.warn(`Element.prototype.$$ already in use.`);
} else {
Element.prototype.$$ = function (...args) {
return [...Element.prototype.querySelectorAll.apply(this, args)];
};
}
if (DocumentFragment.prototype.$) {
console.warn(`DocumentFragment.prototype.$ already in use.`);
} else {
DocumentFragment.prototype.$ = DocumentFragment.prototype.querySelector;
}
if (DocumentFragment.prototype.$$) {
console.warn(`DocumentFragment.prototype.$$ already in use.`);
} else {
DocumentFragment.prototype.$$ = function (selector) {
return [...this.querySelectorAll(selector)];
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment