NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @exclude * // // ==UserLibrary== // @name DOM Waiter // @description DOM helper to wait HTML elements // @copyright 2020, Sergey Zaborovsky (seryiza.xyz) // @license MIT // @version 0.1.5 // ==/UserLibrary== // // ==OpenUserJs== // @author Seryiza // ==/OpenUserJs== // ==/UserScript== /* jshint esversion: 6 */ /** * Print any errors thrown by a function to log, and rethrow them. Useful for * asynchronous callbacks. */ function logCallback(cb) { return function (...args) { try { return cb(...args); } catch (e) { console.error(e); throw e; } }; } /** * Default finder of HTML elements. * * @param {string} selector * @returns {HTMLElement[]} */ function findElementsFromDocument(selector, source = undefined) { if (source) return [...source].filter(el => el.matches(selector)); return Array.from(document.querySelectorAll(selector)); } /** * Check element by comparation of text content. * * @param {string} expectingText * @param {HTMLElement} element */ function checkElementByTextContent(expectingText, element) { return element.textContent === expectingText; } /** * Get checker function from multiple types. * * @param {function|string|undefined} checker */ function getCheckerFunction(checker) { if (checker instanceof Function) { return checker; } if (typeof checker === 'string') { return checkElementByTextContent.bind(null, checker); } return function () { return true; } } /** * Create wait function. * * @param {function} elementsFinder */ function Waiter(findElementsFn = findElementsFromDocument) { return function (selector, checker) { const checkerFn = getCheckerFunction(checker); const select = (source = undefined) => { const elements = findElementsFn(selector); if (elements.length === 0) { return; } const checkedElements = elements.filter(checkerFn); if (checkedElements.length === 0) { return; } return (checkedElements.length > 1) ? checkedElements : checkedElements[0]; }; const selected = select(); if (selected) { return Promise.resolve(selected); } return new Promise(logCallback((resolve) => { const observer = new MutationObserver(logCallback((mods) => { for (const mod of mods) { const selected = select(mod.addedNodes); if (selected) { observer.disconnect(); resolve(selected); return; } } })); // Document because document.body may not yet be instantiated observer.observe(document, { childList: true, subtree: true, }); })); } } window.Waiter = Waiter;