I am working on fetching some data from a website whose data is in a mutation state. I want to observe every change that appears on a table row. I have attached my mutation observer with the table body whose rows are changing. As every change appears on a table column therefore my code only gives me the column that is changing whereas I need a table row whose column is changing. I am unable to fetch that mutated row. Please tell me what change makes me read a mutated row from a table body.
$( window ).ready(function() {
// Select the node that will be observed for mutations
let tableBody = document.getElementById('wtbl8bb9e9b5-1b29-4f8d-909f-2837b994bfc7').children[1];
// Options for the observer (which mutations to observe)
let options = {
childList: true,
attributes: false,
characterData: false,
subtree: true,
attributeOldValue: false,
characterDataOldValue: false
};
//Callback function to execute when mutations are observed
let callback = function(mutationsList, observer) {
for(const mutation of mutationsList) {
console.log("MutationRecord: "+MutationRecord);
if (mutation.type === 'childList') {
console.log(mutation.target);
console.log(mutation)
console.log('A child node has been added or removed.');
console.log(mutation.addedNodes);
mutation.addedNodes.forEach(function(added_node) {
//if(added_node.id == 'child') {
console.log('#child has been added');
console.log(added_node);
});
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// Create an observer instance linked to the callback function
let observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(tableBody, options);
});
the mutation partent is .target, the .target parent is .parentNode.
mutation.target.parentNode;
Related
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 1 year ago.
I need to detect when an element appears and bind an eventListener to it.
There are 2 forms on the webpage, when submitted the first one is deleted and the second one is created dynamically depending on the first form values.
I can only run code onDomReady, I use $(function(){...});
I have tried to check if input[data-name="check3"] exists using setInterval() and when found I bind the eventListener to it.
I also tried to bind the eventListener after the first firm submit using setTimeout().
Both of these solutions "worked", but I wonder if there is a cleaner way to do it.
You can use MutationObserver to watch for changes being made to the DOM tree and detect what nodes are added to dom and add event listener to them. there is an example in the documentation that you can use.
// Select the node that will be observed for mutations
const targetNode = document.getElementById('some-id');
// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: true, subtree: true };
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(const mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
observer.disconnect();
There is an example using MutationObserver.
// element will appear at a random time
setTimeout(() => {
document.body.insertAdjacentHTML('afterbegin', '<div id="el">#el</div>')
}, Math.floor(Math.random() * 4000) + 1000)
const parentOfELement = document.body
const onMutation = () => {
if (parentOfELement.querySelector('#el')) {
observer.disconnect()
console.log('#el appeared!')
}
}
const observer = new MutationObserver(onMutation)
observer.observe(parentOfELement, {childList: true})
The MutationObserver is a simple API that allows me to monitor DOM changes. I'm using it in a chrome-extension I want to know when certain elements on a webpage are added. and using the childList option which is supposed to tell me when the specified target adds new nodes.
From Mozilla Docs: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/childList
By setting childList to true, your callback will be invoked any time nodes are added to or removed from the DOM node or nodes being watched.
but on the callback the change.addedNodes and change.removedNodes is false. even though printing the targets' childNodes.length shows that the number of children is actuall changing
let content_box = document.querySelector('div#content-box'); // target to monitor
// create observer with callback
let observer = new MutationObserver( (mutations, observer) => {
mutations.forEach( change => {
console.log(`Added: ${change.addedNodes==true}`) // no added nodes (there should be)
console.log(`Removed ${change.removedNodes==true}`) // no removed nodes (which is fine)
console.log(change.target) // I've confirmed the target is the correct one
console.log(change.target.childNodes.length) // after multiple callbacks the length of children is actually increasing
})
})
observer.observe(content_box, {
childList: true, // monitoring for childList changes
attributes: false,
subtree: false,
})
You wanna check whether the property addedNodes is in the object of change. so console.log("addedNodes" in change) to get a true result.
let content_box = document.querySelector('div#content-box'); // target to monitor
// create observer with callback
let observer = new MutationObserver( (mutations, observer) => {
mutations.forEach( change => {
console.log(`Added: ${"addedNodes" in change}`) // no added nodes (there should be)
console.log(`Removed ${"removedNodes" in change}`) // no removed nodes (which is fine)
console.log(change.target) // I've confirmed the target is the correct one
console.log(change.target.childNodes.length) // after multiple callbacks the length of children is actually increasing
})
})
observer.observe(content_box, {
childList: true, // monitoring for childList changes
attributes: false,
subtree: false,
})
I am trying to run a simple function each time there is a change in the value of a custom data attribute of a DOM element.
Here is an example below
<div id="myDiv" data-type="type-1">
<!-- Some Content -->
</div>
In the HTML code above, i have a div with a custom data attribute of data-type with a value which i change using javascript. I would like to fire up a another function when ever the value of the attribute is changed depending on the value it holds.
For instance Using an if-statement(which doesn't work! ๐)
var myDiv = document.getElementById("myDiv");
var myDivAttr = myDiv.getAttribute("data-type");
if(myDivAttr == "type-1"){
typeOneFunction();
}
else if(myDivAttr == "type-2"){
typeTwoFunction();
}
// and so on...
I hope my question is clear enough๐๐
You can achieve this using Mutation Observers
// Select the node that will be observed for mutations
const targetNode = document.getElementById('myDiv');
// Options for the observer (which mutations to observe)
const config = { attributes: true };
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(let mutation of mutationsList) {
if (mutation.type === 'attributes') {
if(myDivAttr == "type-1"){
typeOneFunction();
}
else if(myDivAttr == "type-2"){
typeTwoFunction();
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
observer.disconnect();
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
I didn't test that code*
I am learning to use the Mutation observer class, mostly here: https://javascript.info/mutation-observer
I know how to listen to changes of attributes, and how to get old attribute values using attributeOldValue. But can I get the new attribute value from the same event? If so, how? Thanks
You can simply query current value directly from the changed object.
// is element is an input
const newValue = document.getElementByID('myElement').value;
// or, if it is a any text element
const newValue = document.getElementByID('myElement').innerHTML;
Also, each MutationRecord have a property named target which contains all detailed information about changed object. So depending on MutationRecord.type (which tells us what type of the change have happened) you can get new value from corresponding property inside MutationRecord.target
We can listen for mutations and take content from the target where it has already happened. (the example is not on your topic, but the principle is similar)
document.addEventListener('DOMContentLoaded', function () {
let targetEl = document.querySelector('body');
function checkChange(mutations, observer) {
//****************************
console.log(mutations[0].target.innerHTML);
//****************************
}
let observer = new MutationObserver(checkChange);
observer.observe(targetEl, {
subtree: true,
childList: true,
characterData: true,
characterDataOldValue: true,
})
})
I intend to use MutationObserver on observing the appearance and changing of element's value, but to be honest I'm not sure how this should be implemented.
The target of MO would be div.player-bar and what I'm trying to accomplish is to detect when el-badge__content appears in page and when el-badge__content element value is changed (for example instead 1 would change to 2).
Please note that el-badge__content appears at the same time with the creation of div.new-bar and many times div.new-bar would not be present in the page, that's why I need to listen to div.player-bar.
Is this possible? So far I was thinking of something like this:
var target = document.getElementsByClassName('player-bar')[0];
var config = { attributes: true, childList: true, subtree: true };
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.forEach(function(addedNode) {
var e = addedNode.document.getElementsByClassName('el-badge__content')[0];
if (e) {
console.log("Element appearance/changed")
};
});
});
});
observer.observe(target, config);
Thank you in advance.
mutation is a MutationRecord object that contains the array-like addedNodes NodeList collection that you missed in your code, but it's not an array so it doesn't have forEach. You can use ES6 for-of enumeration in modern browsers or a plain for loop or invoke forEach.call.
A much easier solution for this particular case is to use the dynamically updated live collection returned by getElementsByClassName since it's superfast, usually much faster than enumeration of all the mutation records and all their added nodes within.
const target = document.querySelector('.player-bar');
// this is a live collection - when the node is added the [0] element will be defined
const badges = target.getElementsByClassName('el-badge__content');
let prevBadge, prevBadgeText;
const mo = new MutationObserver(() => {
const badge = badges[0];
if (badge && (
// the element was added/replaced entirely
badge !== prevBadge ||
// or just its internal text node
badge.textContent !== prevBadgeText
)) {
prevBadge = badge;
prevBadgeText = badge.textContent;
doSomething();
}
});
mo.observe(target, {subtree: true, childList: true});
function doSomething() {
const badge = badges[0];
console.log(badge, badge.textContent);
}
As you can see the second observer is added on the badge element itself. When the badge element is removed, the observer will be automatically removed by the garbage collector.