Mutation Observers: detecting visibility changes inherited from a parent element - javascript

I am trying to detect when a target element becomes visible (visible or display:block) regardless of where it is placed in the HTML.
I tried using MutationObservers, however, the visibility change can only be detected if the observed element has a direct change in CSS properties. Thus, it ignores changes to its parent element.
var targetNode = document.getElementById('target');
var observer = new MutationObserver(function(){
alert(targetNode.offsetWidth);
}
);
observer.observe(targetNode, { attributes: true, childList: true });
The PROBLEM occurs when a parent element CSS properties are changed and it is not detected/observed by the MuatationObserver attached to the child/target element as seen in in this jsfiddle. Is there a way (aside from using Intervals) for the observed DOM element to detect changes in CSS that is inherited from parents?

You can observe the parent and the child:
observer.observe(targetNode, options);
observer.observe(parentNode, options);
var targetNode = document.getElementById('target');
var parentNode = document.getElementById('parent');
var observer = new MutationObserver(function (changes) { // or replace changes by just: [ { target } ]
const target = changes[0].target
if (target === parentNode)
alert('parent')
if (target === targetNode)
alert('target')
console.log(target)
});
var options = {
attributes: true,
childList: true,
attributeFilter: ['style', 'class']
}
observer.observe(targetNode, options);
observer.observe(parentNode, options);
<script>
function showParent() {
document.getElementById("parent").style.display = "block";
}
function showChild() {
document.getElementById("target").style.display = "block";
}
</script>
<button onclick="showParent()">showParent</button>
<button onclick="showChild()">showChild</button>
<div id="parent" style="display:none;">
parent
<div id="target" style="display:none;">target</div>
</div>

Related

Display Modal on DOM Element Update with MutationObserver

I want to display a modal popup only after one of any DOM elements with the same class are updated. I am guessing MutationObserver is the best tool for this? Here is what I've got so far but it is not working...
// Get the modal
var modal = document.getElementById("save-search-modal");
// select the target nodes for mutation observing
var target = document.getElementsByClassName("sidx-pill-value")[0];
// When .sidx-pill-value is updated, open the modal
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
modal.style.display = "block";
});
});
// configuration of the observer:
var config = { subtree: true, characterData: true, attributes: true, childList: true }
// pass in the target node, as well as the observer options
observer.observe(target, config);

multiple listener event on javascript [duplicate]

I want to detect changes in div-elements. I tried already the "addEventListener" with some types (e.g.: change, load).
Here is my example, but the event won't trigger:
<!doctype html>
<html>
<head>
</head>
<body>
<div id='DivElement'>Test Div Text...</div>
<button type="button" onclick="EditDivElement()">click me!</button>
<script>
function EditDivElement() {
ClickCounter++;
SelectedDiv.textContent="new text content: " + ClickCounter;
}
function OnDivEdit() {
alert("success!");
}
var ClickCounter = 0;
var SelectedDiv = document.querySelector("#DivElement");
SelectedDiv.addEventListener("change", OnDivEdit);
</script>
</body>
</html>
(Edit: It's for a browser-addon and it should be used on other websites.)
You can use a MutationObserver.
From MDN:
// select the target node
var target = document.querySelector('#some-id');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
// later, you can stop observing
observer.disconnect();
Note: Deprecated
What I have done in the extensions I wrote was to listen on DOMNodeInserted.
div.addEventListener("DOMNodeInserted", function (e) {
e.target //
}, false);

Observing html text changes with mutationObserver

I try to observe if the text in the HTML element is changing, so if certain text has appeared, it will display another element.
var travelarea = document.querySelector("#travelInformationStep");
var MutationObserver = window.MutationObserver;
var observer = new MutationObserver(function(mutations){
mutations.forEach(function(mutation){
if(mutation.target.text === "Europe"){
document.getElementById("terjer2").style.display = "block";
}
});
});
var config = {attributes: true, childList: true, characterData: true}
observer.observe(travelarea, config);
But I'm not sure is how should I check those changes in the if statement.
Try to check target's textContent. If the use case requires to avoid checking hidden or other text content that is not rendered in the browser, but is present in the mutation.target you can use innerText instead of textContent
var travelarea = document.querySelector("#travelInformationStep");
var MutationObserver = window.MutationObserver;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.target.textContent === "Europe") {
alert('Text changed to Europe');
}
});
});
var config = {attributes: true, childList: true, characterData: true};
observer.observe(travelarea, config);
document.getElementById('changeText').addEventListener('click', function(e) {
travelarea.textContent = travelarea.textContent === 'Europe' ? 'random text' : 'Europe';
});
<p id="travelInformationStep">Some text</p>
<button id="changeText">Change</button>

Chrome Extension: Waiting For Element to Load (async js)

I have a Chrome extension, and I want to wait until an element is loaded before injecting content into the page.
I'm trying to inject a button:
myButton = document.createElement('button');
myButton.class = 'mybutton';
document.querySelector('.element_id').appendChild(myButton)
I have this at the top of my content script. It used to work just fine, but then it stopped working. The error that was displayed was:
Uncaught TypeError: Cannot read property 'appendChild' of null
In order to wait for the element with class id .element_id to load, I tried to use a MutationObserver
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (!mutation.addedNodes) return
for (var i = 0; i < mutation.addedNodes.length; i++) {
if (mutation.addedNodes[i].parentNode == document.querySelector('#outer-container')) {
myButton = document.createElement('button');
myButton.class = 'mybutton';
document.querySelector('.element_id').appendChild(myButton)
}
var node = mutation.addedNodes[i]
}
})
})
observer.observe(document.body, {
childList: true
, subtree: true
, attributes: false
, characterData: false
})
When I used the mutation observer, the page would load an outer div element called outer-container, and there was no way for me to directly compare the class .element_id. The class .element_id is nested a number of layers into the outer div.
HOWEVER, the above did not work, and I still received the null property error.
Is there a better way to wait for some element to be loaded (which is loaded async), before injecting?
Don't forget to add childList and subtree property when observing changes.
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (!mutation.addedNodes) {
return;
}
for (var i = 0; i < mutation.addedNodes.length; i++) {
if (mutation.addedNodes[i].classList.contains("element_id")) {
// Your logic here
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
An insertion into DOM may have the element in question deeper in the added node.
For example, this can be inserted into the DOM:
<div class="container">
<div class="element_id">...</div>
...
</div>
In that case, the added node list will only contain the .container node.
The mutation will not list everything added, it's your responsibility to recursively dig into the added fragment looking through added nodes.
Using mutation-summary library may help you avoid such headaches.
var observer = new MutationSummary({
rootNode: document.body,
callback: function(summaries) {
summaries.forEach(function(summary) {
summary.added.forEach(function(idElement) {
/* ... */
idElement.appendChild(myButton);
});
});
},
queries: [{element: ".element_id"}]
});
If you don't want to use a library, you can try calling querySelector or querySelectorAll on addedNodes[i].

How to use MutationObserver?

I recently came across this awesome MutationObserver feature which sort of keep tracks of the changes on any dom element. I used the code that was shown on the mozilla developer network, but can't seem to make it run. This is the code I used (link):
// create an observer instance
var target = document.querySelector('#something');
console.log(target);
var observer = new WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log("Success");
//$('#log').text('input text changed: "' + target.text() + '"');
//console.log(mutation, mutation.type);
});
});
observer.observe(target, { attributes: true, childList: true, characterData: true });
//observer.disconnect(); - to stop observing
// test case
setInterval(function(){
document.querySelector('#something').innerHTML = Math.random();
},1000);
The above code doesn't seems to work. However if I modify the same code with a bit of jQuery, everything seems to work just fine (Demo here). Is there something I'm missing from the docs or I'm just misinterpreting the observer feature.
You need subtree: true
http://jsfiddle.net/6Jajs/1/
The inner text would normally be a child text() element in the DOM. Without the subtree it will only watch the element itself.
There is possible confusion surrounding "characterData" (https://developer.mozilla.org/en-US/docs/Web/API/CharacterData), but it seems that that applies only to nodes that directly contain text. The DOM is structured so that most markup elements contain mixed type which optionally include a child text node (which in turn would implement characterData, but would be a child of the targeted node).
Simple Example:
<div contentEditable id="myID">MUST EDIT NOW</div>
<script>
let x = new MutationObserver( function(){ alert('DETECTED'); } );
x.observe( myID , {subtree:true,characterData:true} );
</script>
See Example Live: https://jsfiddle.net/mbo9eLt5/
To Watch Text or Input changes
use
characterData: true
Example:
var target = document.querySelector('#text');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
var config = {
characterData: true,
subtree: true,
};
observer.observe(target, config);
// otherwise
observer.disconnect();
observer.observe(target, config);
<div id="text" contenteditable="true">characterData:true</div>
To Watch Child or Append Text or Inserting Dom
childList:true
Example:
var target = document.querySelector('#text');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
var config = {
childList: true,
subtree: true,
};
observer.observe(target, config);
// otherwise
observer.disconnect();
observer.observe(target, config);
<div id="text" contenteditable="true">characterData:true</div>
<button onclick="testappend();
function testappend(){
document.getElementById('text').append('tesxt')
}">append</button>
To Watch dom Attributes
attributes: true
Example
var target = document.querySelector('#text');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
var config = {
characterData: true,
attributes: true,
};
observer.observe(target, config);
// otherwise
observer.disconnect();
observer.observe(target, config);
<div id="text" contenteditable="true">characterData:true</div>
<button onclick="testappend();
function testappend(){
document.getElementById('text').classList.add('tesxt')
}">add class</button>
<button onclick="setat();
function setat(){
document.getElementById('text').setAttribute('data-prop','text')
}">set attribute</button>
attribute old value
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/attributeOldValue

Categories

Resources