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);
Related
hey would there be anyway to connect an MutationObserver and then disconnect it with the same button i know how to disconnect and connect it but i would only like to do it with one button how can i do this? sorry my bad english
var target = document.getElementsByClassName('message')[0];
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.info("EVENT TRIGGERT ");
});
});
var config = {
attributes: true,
childList: true,
characterData: true
};
function dis() {
observer.disconnect();
}
function obs() {
observer.observe(target, config);
}
// simulate change
refreshIntervalId = setInterval(function() {
document.getElementsByClassName('message')[0].innerHTML = Math.random().toString(36).substring(2, 15);
}, 1000);
//
<button onclick="obs();">observe</button>
<button onclick="dis();">disconnect</button>
<div class="message"></div>
Keep track of you observer state in another variable (e.g. isObserving). Set this variable to true in your obs() function, set it to false in dis() and call a toggleObs() with your button.
function toggleObs() {
if(isObserving) {
dis();
} else {
obs();
}
}
there is no property on observable which will tell you if you observing or not. For that reason you need to keep track by your self. See example https://stackblitz.com/edit/js-dw5jmr
let isObserving = false;
window.toggleObserve = function() {
isObserving = !isObserving;
isObserving ? window.obs() : window.dis();
}
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>
I have a simple MutationObserver setup as a test. The HTML has a span whose text content gets updated once per second (and a div for messages):
<span class="tester"></span>
<div id="msg"></div>
The MutationObserver is set to watch .tester and writes text to the #msg div when it observes a change. Meanwhile, a setInterval() runs once/second to change the text in .tester:
var target = document.querySelector('.tester');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
$('#msg').append(mutation.type+"<br/>")
setTimeout(function() { $('#msg').text(''); }, 500);
});
});
var config = { attributes: true, childList: true, characterData: true };
observer.observe(target, config);
setInterval(function() {
$('#msg').text('');
$('.tester').text("update: "+Math.random())
}, 1000);
I would expect this code to print once per second that the characterData has changed. According to Mozilla's docs for MutationObserver, it says about characterData: "Set to true if mutations to target's data are to be observed." Instead, I see no characterData mutations but do see two childList mutations every second.
Why am I not seeing any characterData mutations, and why am I seeing two childList mutations?
Here's a working example with CodePen.
The reason is as Jeremy Banks said: When you use jQuery's text(), it removes all the text nodes and then adds in new ones. That's not a change to character data, it's a change to the childList: Removing the node that's there and replacing it with a new one.
To see a change to character data, you have to modify the existing text node's nodeValue, and observe subtree modifications:
var target = document.querySelector('.tester');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
$('#msg').append(mutation.type+"<br/>")
setTimeout(function() { $('#msg').text(''); }, 500);
});
});
var config = {
attributes: true,
childList: true,
characterData: true,
subtree: true // <=== Change, added subtree
};
observer.observe(target, config);
var timer = setInterval(function() {
$('#msg').text('');
// Change --VVVVV modifying the existing child node
$('.tester')[0].firstChild.nodeValue = "updated" + Math.random();
}, 1000);
// Stop after 10 seconds
setTimeout(function() {
clearInterval(timer);
}, 10000);
<span class="tester">x</span><!-- Change, added a starting child node -->
<div id="msg"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Re your question about why there are two childList mutations, yes I think you're right: They're removing the child, then adding a new one. If we use the replaceChild method, we see only a single mutation:
var target = document.querySelector('.tester');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
$('#msg').append(mutation.type+"<br/>")
setTimeout(function() { $('#msg').text(''); }, 500);
});
});
var config = {
attributes: true,
childList: true,
characterData: true,
subtree: true // <=== Change, added subtree
};
observer.observe(target, config);
var timer = setInterval(function() {
$('#msg').text('');
// Change --VVVVV modifying the existing child node
var text = document.createTextNode("updated" + Math.random());
var parent = $('.tester')[0];
parent.replaceChild(text, parent.firstChild);
}, 1000);
// Stop after 10 seconds
setTimeout(function() {
clearInterval(timer);
}, 10000);
<span class="tester">x</span><!-- Change, added a starting child node -->
<div id="msg"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
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
Is there a way to have a listener for when a div element is empty?
$('#myDiv').emptyEvent(function(){
)};
You should run David's code inside an event handler, such as DOMNodeInserted, DOMCharacterDataModified, or DOMSubtreeModified. The latter being the most recommended. For example:
$('#myDiv').bind("DOMSubtreeModified", function(){
if ( $('#myDiv').html() == "" ) {
}
)};
Edit: Such implementation is however deprecated, as stated in the comments. An alternative implementation, as suggested by david, is the following:
// select the target node
var target = $("#myDiv")[0];
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if($("#myDiv").html() == ""){
// Do something.
}
});
});
// 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);