Skip to content

Instantly share code, notes, and snippets.

@BehindTheMath
Last active August 27, 2017 19:06
Show Gist options
  • Select an option

  • Save BehindTheMath/538f898838987b6d458b3cad7d443298 to your computer and use it in GitHub Desktop.

Select an option

Save BehindTheMath/538f898838987b6d458b3cad7d443298 to your computer and use it in GitHub Desktop.
QuerySelectorASAP
/**
* Wraps <code>document.querySelector()</code> and <code>document.querySelectorAll()</code> to either:
*
* a) return the matching {@link Element} or {@link NodeListOf<Element>} as soon as possible: immediately if it's already
* available in the DOM, or as soon as it's ready, by using <code>setInterval()</code>.
*
* or
*
* b) resolve when the specified element(s) are no longer present in the DOM.
*
* @param querySelector The querySelector to match
* @param options an object with the following optional properties:
* <pre> querySelectorAll</pre> Set true to use <code>document.querySelectorAll()</code>
* <pre> negative</pre> Set to true to resolve when the specified query selector is no longer present in the DOM
* <pre> timeout</pre> The interval in ms to recheck
* <pre> maxIterations</pre> the maximum number of iterations to check
* @returns A {@link Promise} which resolves with an {@link Element} or {@link NodeListOf<Element>} matching the specified query selector.
*/
function querySelectorASAP(querySelector: string, options: IQuerySelectorASAPOptions = {}): Promise<Element | NodeListOf<Element>> {
return new Promise<Element | NodeListOf<Element>>((resolve, reject) => {
const {querySelectorAll = false, negative = false, timeout = 100, maxIterations = (5000 / timeout)} = options;
// First, check the current state
let elementOrNodeListOfElements: Element | NodeListOf<Element> = querySelectorAll ?
document.querySelectorAll(querySelector) :
document.querySelector(querySelector);
let found: boolean = elementOrNodeListOfElements !== null && (!querySelectorAll || (elementOrNodeListOfElements as NodeListOf<Element>).length >= 1);
// If it's the desired state, resolve
if (found === !negative) {
resolve(!negative ? elementOrNodeListOfElements : null);
} else {
// If it's not found immediately, start looping to check for it
let iterationCounter: number = 1;
const interval: number = setInterval(() => {
// On every loop, check for it
elementOrNodeListOfElements = querySelectorAll ?
document.querySelectorAll(querySelector) :
document.querySelector(querySelector);
found = (elementOrNodeListOfElements !== null) && (!querySelectorAll || (elementOrNodeListOfElements as NodeListOf<Element>).length >= 1);
// If it's the desired state
if (found === !negative) {
// Stop looping
clearInterval(interval);
// Resolve
resolve(!negative ? elementOrNodeListOfElements : null);
} else {
// If it's still not found, increment the counter to get ready for the next iteration
iterationCounter++;
// If it's past the maximum iterations
if (iterationCounter > maxIterations) {
// Stop checking
clearInterval(interval);
// Reject the Promise with an appropriate error message
reject({
querySelector: querySelector,
message: "Query selector was not found within the specified number of iterations."
});
}
}
}, timeout);
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment