Skip to content

Instantly share code, notes, and snippets.

@BrockA
Created May 7, 2012 04:21
Show Gist options
  • Save BrockA/2625891 to your computer and use it in GitHub Desktop.
Save BrockA/2625891 to your computer and use it in GitHub Desktop.
A utility function, for Greasemonkey scripts, that detects and handles AJAXed content.
/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
that detects and handles AJAXed content.
Usage example:
waitForKeyElements (
"div.comments"
, commentCallbackFunction
);
//--- Page-specific function to do what we want when the node is found.
function commentCallbackFunction (jNode) {
jNode.text ("This comment changed by waitForKeyElements().");
}
IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents ()
.find (selectorTxt);
if (targetNodes && targetNodes.length > 0) {
btargetsFound = true;
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each ( function () {
var jThis = $(this);
var alreadyFound = jThis.data ('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
var cancelFound = actionFunction (jThis);
if (cancelFound)
btargetsFound = false;
else
jThis.data ('alreadyFound', true);
}
} );
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace (/[^\w]/g, "_");
var timeControl = controlObj [controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval (timeControl);
delete controlObj [controlKey]
}
else {
//--- Set a timer, if needed.
if ( ! timeControl) {
timeControl = setInterval ( function () {
waitForKeyElements ( selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
300
);
controlObj [controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}
@Cerberus-tm
Copy link

No one's bothered to link the mystical rumored fork that uses MutationObserver so here's the link: https://gist.github.com/double-beep/c4d4ec3866e5e54ae514c0aab60af242

Thanks, it's interesting to compare the two approaches. Maybe this is out of the scope of this page, but...what would be the advantage of using this script over Mutation Observer (and vice versa)?

@CoeJoder
Copy link

CoeJoder commented Mar 9, 2025

For a modern dynamic webpage, you normally wouldn't want to naively attach a MutationObserver to the root of the document body (callback thrashing!). It's often a good idea to first poll for a particular parent element e.g. comment section container, and then attach a MO to that element. I like using ViolentMonkey's utility instead of MutationObserver directly. Here is an example userscript: Turn rumble comment timestamps into clickable links (here I'm using GM_wrench.wait() for more general purpose polling, not WFKE)

@BrockA
Copy link
Author

BrockA commented Mar 9, 2025

No one's bothered to link the mystical rumored fork that uses MutationObserver so here's the link: https://gist.github.com/double-beep/c4d4ec3866e5e54ae514c0aab60af242

Thanks, it's interesting to compare the two approaches. Maybe this is out of the scope of this page, but...what would be the advantage of using this script over Mutation Observer (and vice versa)?

Properly implemented, MutationObserver is now faster, more efficient, and more responsive. I do not think that linked script is the best example and it is not as flexible or robust as this WFKE. Nor is it a drop-in replacement. But if it, or any of the thousands of now available alternatives, works for you, then awesome. ;)

To be fair to me, this WFKE was written before MutationObserver was widely available and while it was particularly buggy and resource intensive. It performs well enough that I almost never need anything else and haven't felt a pressing need to refactor it yet.

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