Asontu

Herman Scheele
.NET, C#, Sitecore developer and Data Science engineer @ theFactor.e.

profile for asontu at Stack Overflow, Q&A for professional and enthusiast programmers

Tech-blog about C#, Sitecore, T-SQL, Front-end, Python, Data Science and everything in between run by Herman Scheele

RSS Archive
30 December 2020

MutationObserver Promise made easy

When adding a new feature to my Nifty Sitecore Userscript I noticed a lot of similar code to initiate one-time MutationObservers and disbanding them again after the Mutation was Observed. Clearly, a refactor was in order.

I found this Gist that was already half the way to what I needed, including someone asking for a Timeout feature in the comments. I just needed more flexibility on what to Observe and on when to resolve immediately.

This resulted in mop(), a MutationObserver Promise implementation. With mop() it becomes very easy to make a function that triggers some Mutation and returns a Promise that resolves when the Mutation is Observed or rejects if after a set Timeout the Mutation still hasn’t happened.

This mop() function accepts the following arguments:

  • trigger
    Logic to perform the action that triggers the Mutation
    (If this function returns true, the Promise immediately resolves without waiting for a Mutation)
  • watch
    Element to Observe Mutation on
  • query
    Query selector to search for in the added nodes
    (optional, defaults to '*' for everything)
  • options
    MutationObserver options about how deep to observe
    (optional, defaults to only childList)
  • timeout
    Milliseconds after which to fail
    (optional, defaults to never timing out)

An example use of this function would be the following function to click a node in the Sitecore Content Editor. Utilizing mop() the function clickTreeNode() contains simply the logic for clicking the element and what to watch for. All logic of building a MutationObserver, Timeout and removing those again is taken care of.

As well this function is an example of using return true; to skip clicking and Observing a Mutation entirely if the node to click was never found in the first place.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function clickTreeNode(itemId) {
	return mop(function() {
		let nodeToClick = document.querySelector(`a#Tree_Node_${itemId}.scContentTreeNodeNormal`);
		if (!nodeToClick) {
			document.getElementById('TreeSearch').value = itemId;
			return true;
		}
		nodeToClick.click();
	},
	document.getElementById('ContentEditor'),
	'#EditorTabs .scEditorHeaderVersionsLanguage',
	{attributes:false, childList: true, subtree: true},
	2000);
}

clickTreeNode() and similar functions using mop() is then used in the code like so:

1
2
3
4
5
6
7
8
9
10
11
search.expandTo
	.split('!')
	.map(id => `#Tree_Glyph_${id}[src*=treemenu_collapsed]`)
	.map(itemId => () => expandTreeNode(itemId))
	.reduce((prom, fn) => prom.then(fn), Promise.resolve())
	.then(() => clickTreeNode(search.clickTo))
	.then((nodes) => openLangMenu(search.langTo, !!nodes.length))
	.then((nodes) => clickLang(search.langTo, !!nodes.length))
	.then(() => scrollTree(search.scrollTreeTo))
	.then(() => scrollPanel(search.scrollPanelTo))
	.then(() => hideSpinner());
tags: Front-end - Javascript