Skip to content

Instantly share code, notes, and snippets.

Last active September 22, 2021 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save derlin/50b02e293f8dda69eb46f25ad97c63d7 to your computer and use it in GitHub Desktop.
Save derlin/50b02e293f8dda69eb46f25ad97c63d7 to your computer and use it in GitHub Desktop.
Get notified when something (pattern, text) appears on a page. Great for pages showing logs !
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
function toArrayOfPatterns(searchPatterns) {
// transform strings, patterns, or arrays of strings/patterns into arrays of patterns
return (Array.isArray(searchPatterns) ? searchPatterns : [searchPatterns])
.map((pattern) => typeof pattern == "string" ? new RegExp(pattern) : pattern);
async function isNotificationPermissionGranted() {
// ask for permission to show notification
return new Promise((resolve, _reject) => {
const permissionDenied = () => console.log(
"%cYou didn't grant notification permission for this page. Aborting.",
"font-weight: bold; color: red");
if (Notification.permission === 'granted') {
} else if (Notification.permission === 'default') {
console.log("Requesting notification permission.");
"%cFROM FIREFOX 72 onwards%c\nIt is not possible to trigger the permission prompt without user interaction. " +
"A little icon should have appeared on the left of the URL bar though. Click on it to grant permission !\n" +
"font-weight: bold; color: red", "");
Notification.requestPermission().then(() => {
Notification.permission === 'granted' ? resolve(true) : resolve(false);
} else {
* Wait for some text/pattern to appear on the page, and show a notification.
* The `searchPattern` can be a string, a pattern (`RegExp`), or an array of string/pattern.
* If an array is passed, the notification is shown as soon as any string/pattern is found (logical OR).
* @example
* waitForText([/hello.*worlds?/, "exact match"], document.getElementById("out"), {title: "My Title"})
* @param {(string | RegExp | [(string|RegExp)])} searchPattern the pattern(s) to wait for for (logical OR)
* @param {HTMLElement} element in which element to search for the string (default: the whole body)
* @param {object} notificationParams notification param overrides (see
async function waitForText(searchPattern, element, notificationParams) {
if (!await isNotificationPermissionGranted()) return;
const elt = element ?? document.body;
const patterns = toArrayOfPatterns(searchPattern);
console.log(`Waiting for any of ${patterns}.`,
"Type 'window.stopWaiting = true' on the developer console to stop the wait, or reload the page.");
while (!window.stopWaiting) {
console.log("..."); // show that something is working in the background
let matchingPattern = patterns.find(pattern => pattern.test(elt.innerHTML));
if (matchingPattern) {
const match = matchingPattern.exec(elt.innerHTML)[0];
console.log(`${match} found. Showing notification.`);
// compute actual notification parameters
let params = Object.assign({
title: "Found!",
body: `found "${match}" in ${document.title}`,
requireInteraction: true // only dismiss on click (if supported)
}, notificationParams);
// show the notification
let notification = new Notification(params.title, params);
// switch automatically to the right window on notification click
notification.onclick = function () {
await sleep(5_000);
console.log("WaitForText aborted.");
window.stopWaiting = false; // reset
// this can be used anytime to stop the current waiting loop
window.stopWaiting = false;
// attach functions to window so they are available from bookmarklets
window.waitForText = waitForText;
Copy link

derlin commented Sep 22, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment