Consider the following situation: You have an external Save button in your application that saves the current document in the Document Editor. This button should only be enabled to allow users to save content when the document is in Edit mode, not Preview mode. Preview mode is a mode that populates inserted merge fields with data to preview the document as you design a template.

Enabled button

When the document is in Preview mode, the Save button should be disabled, as shown in the following screenshot:

Disabled button

MutationObserver

One way to achieve this is by using the MutationObserver API. The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. When a change is detected, the observer will call a specified callback function. This is useful for detecting changes to the DOM that are made by JavaScript, such as when the document mode changes from Preview to Edit.

In the ribbonTabsLoaded event handler, you can create a new MutationObserver instance and observe changes to the ribbonGroupMailMergePreview element. When the ribbonGroupMailMergePreview element is visible (i.e., the document is in Preview mode), you can disable the Save button. When the ribbonGroupMailMergePreview element is not visible (i.e., the document is in Edit mode), you can enable the Save button.

// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
view raw test.js hosted with ❤ by GitHub
TXTextControl.addEventListener("ribbonTabsLoaded", function () {
targetNode = document.getElementById('ribbonGroupMailMergePreview');
// Start observing the target node for configured mutations
if (targetNode) {
observer.observe(targetNode, config);
// Initial check
console.log(`Initial visibility of #ribbonGroupMailMergePreview: ${checkVisibility(targetNode)}`);
} else {
console.error('Element #ribbonGroupMailMergePreview not found');
}
});
view raw test.js hosted with ❤ by GitHub

The callback function will be called whenever a change is detected in the ribbonGroupMailMergePreview element. The function will check if the ribbonGroupMailMergePreview element is visible and enable or disable the Save button accordingly.

// Function to check visibility
function checkVisibility(element) {
const style = window.getComputedStyle(element);
return style && style.display !== 'none' && style.visibility !== 'hidden';
}
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' || mutation.type === 'childList') {
// Check visibility whenever an attribute or child list changes
const isVisible = checkVisibility(targetNode);
if (isVisible) {
document.getElementById('saveBtn').disabled = true;
} else {
document.getElementById('saveBtn').disabled = false;
}
}
}
};
view raw test.js hosted with ❤ by GitHub

The full kbd for this example is here:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TX Text Control Document Editor from JS</title>
<script
src="https://backend.textcontrol.com/api/TXWebSocket/GetResource?name=tx-document-editor.min.js">
</script>
<style>
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
button {
margin-top: 20px;
}
#txDocumentEditor {
height: 600px;
width: 800px;
}
</style>
</head>
<body>
<div id="txDocumentEditor"></div>
<button id="saveBtn" onclick="save()">Save Document</button>
<script>
var targetNode = null;
TXTextControl.init({
containerID: "txDocumentEditor",
webSocketURL: "wss://backend.textcontrol.com/api/TXWebSocket?access-token="
});
var customerJson = [{
"name": "John Doe",
"address": "1234 Elm Street",
"city": "Austin",
"state": "TX",
"zip": "78701"
}];
TXTextControl.addEventListener("ribbonTabsLoaded", function () {
targetNode = document.getElementById('ribbonGroupMailMergePreview');
// Start observing the target node for configured mutations
if (targetNode) {
observer.observe(targetNode, config);
// Initial check
console.log(`Initial visibility of #ribbonGroupMailMergePreview: ${checkVisibility(targetNode)}`);
} else {
console.error('Element #ribbonGroupMailMergePreview not found');
}
});
TXTextControl.addEventListener("textControlLoaded", function () {
TXTextControl.loadJsonData(JSON.stringify(customerJson));
});
function save() {
TXTextControl.saveDocument(TXTextControl.StreamType.HTMLFormat, function (data) {
console.log(data);
});
}
// Function to check visibility
function checkVisibility(element) {
const style = window.getComputedStyle(element);
return style && style.display !== 'none' && style.visibility !== 'hidden';
}
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' || mutation.type === 'childList') {
// Check visibility whenever an attribute or child list changes
const isVisible = checkVisibility(targetNode);
if (isVisible) {
document.getElementById('saveBtn').disabled = true;
} else {
document.getElementById('saveBtn').disabled = false;
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: true, subtree: true };
</script>
</body>
</html>
view raw test.html hosted with ❤ by GitHub

Conclusion

The MutationObserver API is a powerful tool for detecting changes to the DOM tree. In this example, we used a MutationObserver to enable or disable a Save button based on the visibility of a specific element. This technique can be applied to a wide range of scenarios where you need to respond to changes in the DOM.