Reddit Watcher
Consolidated MutationObserver handler for use by other Reddit userscripts. By itself doesn't do much, but other scripts can use hooks to avoid redundant observers watching the same thing. The main benefit being that overloading MutationObservers could slow down the browser. As such, if using multiple userscripts that use this function, a centralized handler will avoid unnecessary duplication.
Can use with:
Ensure in userscript dashboard that this is loaded before any dependent scripts by moving it above in the sorted list of installed userscripts.
Change log
v1.2.0 : Mar 9, 2022
- Additional functions added for potential development use.
v1.1.0 : Mar 9, 2022
- Use
unsafeWindow
instead of no-sandboxing with @grant none
Development
redditWatcher
Lives as object named unsafeWindow.redditWatcher
as simplest way of making itself available to other userscripts. As such, the @grant
parameter of the userscript header will need access to unsafeWindow
.
Within are three watcher objects for redditWatcher.body
, redditWatcher.listing
, and redditWatcher.feed
. Each handles changes to the document body, the listing outer container, and the post feed respectively. The latter two are written to handle deletions and recreations of the watched element, should it be removed and readded.
Hooks
Each watch for updates and changes on the element it is observing, for which hooks can be applied. Updates are defined as anything that would trigger the MutationObserver to detect an update. For body and listing, these include anything DOM changes in subtree. For feed, this only includes direct children, which translates to any added/removed post item in the feed.
Changes are defined as the element being removed and replaced, which will subsequently also fire an update event. This should never happen on the body, and generally, Reddit keeps an empty version of the other watched elements, so they should never be removed/replace either. E.g. swapping from the front page to r/popular doesn't replace the feed container, it simply empties and repopulates it. As such, a special case is implement the feed watcher, which triggers a change event when the first post is removed and/or changes. Thus, swapping 'pages' are detected as change in the feed.
In the case a change does occur, the old MutationObserver is disconnected and a new MutationObserver is created to watch the replacement element. All the existing hooks are carried over to the new observer. Ideally this helps prevent memory leaks from old MutationObservers hanging on to references of detached elements.
Usage
To apply a hook, identify the applicable watcher and pass a function to onUpdate()
or onChange()
. E.g., to add events to the feed watcher:
unsafeWindow.redditWatcher.feed.onUpdate((feed, mutated) => {
mutated.addedNodes.forEach(post => {
console.log("New post detected");
});
mutated.removedNodes.forEach(post => {
console.log("Post removed");
});
});
unsafeWindow.redditWatcher.feed.onChange(feed => {
console.log("Feed changed");
});
The function onUpdate()
is provided with the watched element and the array of changes as MutationRecords. The function onChange()
is provided with only the watched element. Both append hooks (instead of replacing a singular hook).
Hooks can be removed with un()
, passing the original callback/hook to be removed from both the update and change hooks.
More usage
update()
may be called to manually fire an update event.
change()
may be called to manually fire a change event (which will subsequently fire an update event).
check()
may be called to start a manual check for if an element change has occurred. If a change is detected, will also fire a change event (which will subsequently fire an update event).
get()
will return the element, if found, as searched for in the document. It should be analogous to watching
, but instead of returning the stored reference, will dynamically search the document for it when it is called.
watching
is the current element being watched.
watcher
stores the current MutationObserver watching the element.
observe(elem, options)
is a proxy to MutationObserver.observe() on the current MutationObserver.