'Event trigger on a class change
I'd like my event to be triggered when a div tag containing a trigger class is changed.
I have no idea how to make it listen to the class' adding event.
<div id="test">test</div>
<script type="text/javascript">
document.getElementById.setAttribute("class", "trigger");
function workOnClassAdd() {
alert("I'm triggered");
}
</script>
Solution 1:[1]
Well there were mutation events, but they were deprecated and the future there will be Mutation Observers, but they will not be fully supported for a long time. So what can you do in the mean time?
You can use a timer to check the element.
function addClassNameListener(elemId, callback) {
var elem = document.getElementById(elemId);
var lastClassName = elem.className;
window.setInterval( function() {
var className = elem.className;
if (className !== lastClassName) {
callback();
lastClassName = className;
}
},10);
}
Running example: jsFiddle
Solution 2:[2]
The future is here, and you can use the MutationObserver interface to watch for a specific class change.
let targetNode = document.getElementById('test')
function workOnClassAdd() {
alert("I'm triggered when the class is added")
}
function workOnClassRemoval() {
alert("I'm triggered when the class is removed")
}
// watch for a specific class change
let classWatcher = new ClassWatcher(targetNode, 'trigger', workOnClassAdd, workOnClassRemoval)
// tests:
targetNode.classList.add('trigger') // triggers workOnClassAdd callback
targetNode.classList.add('trigger') // won't trigger (class is already exist)
targetNode.classList.add('another-class') // won't trigger (class is not watched)
targetNode.classList.remove('trigger') // triggers workOnClassRemoval callback
targetNode.classList.remove('trigger') // won't trigger (class was already removed)
targetNode.setAttribute('disabled', true) // won't trigger (the class is unchanged)
I wrapped MutationObserver with a simple class:
class ClassWatcher {
constructor(targetNode, classToWatch, classAddedCallback, classRemovedCallback) {
this.targetNode = targetNode
this.classToWatch = classToWatch
this.classAddedCallback = classAddedCallback
this.classRemovedCallback = classRemovedCallback
this.observer = null
this.lastClassState = targetNode.classList.contains(this.classToWatch)
this.init()
}
init() {
this.observer = new MutationObserver(this.mutationCallback)
this.observe()
}
observe() {
this.observer.observe(this.targetNode, { attributes: true })
}
disconnect() {
this.observer.disconnect()
}
mutationCallback = mutationsList => {
for(let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
let currentClassState = mutation.target.classList.contains(this.classToWatch)
if(this.lastClassState !== currentClassState) {
this.lastClassState = currentClassState
if(currentClassState) {
this.classAddedCallback()
}
else {
this.classRemovedCallback()
}
}
}
}
}
}
Solution 3:[3]
Here's a simple, basic example on how to trigger a callback on Class attribute change
MutationObserver API
const attrObserver = new MutationObserver((mutations) => {
mutations.forEach(mu => {
if (mu.type !== "attributes" && mu.attributeName !== "class") return;
console.log("class was modified!");
});
});
const ELS_test = document.querySelectorAll(".test");
ELS_test.forEach(el => attrObserver.observe(el, {attributes: true}));
// Example of Buttons toggling several .test classNames
document.querySelectorAll(".btn").forEach(btn => {
btn.addEventListener("click", () => ELS_test.forEach(el => el.classList.toggle(btn.dataset.class)));
});
.blue {background: blue;}
.gold {color: gold;}
<div class="test">TEST DIV</div>
<button class="btn" data-class="blue">BACKGROUND</button>
<button class="btn" data-class="gold">COLOR</button>
Solution 4:[4]
Can use this onClassChange function to watch whenever classList of an element changes
function onClassChange(element, callback) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'class'
) {
callback(mutation.target);
}
});
});
observer.observe(element, { attributes: true });
return observer.disconnect;
}
var itemToWatch = document.querySelector('#item-to-watch');
onClassChange(itemToWatch, (node) => {
node.classList.contains('active')
? alert('class added')
: alert('class removed');
node.textContent = 'Item to watch. classList: ' + node.className;
});
function addClass() {
itemToWatch.classList.add('active');
}
function removeClass() {
itemToWatch.classList.remove('active');
}
<div id="item-to-watch">Item to watch</div>
<button onclick="addClass();">Add Class</button>
<button onclick="removeClass();">Remove Class</button>
Solution 5:[5]
I needed a class update listener for a project, so I whipped this up. I didn’t end up using it, so it’s not fully tested, but should be fine on browsers supporting Element.classList DOMTokenList.
Bonus: allows “chaining” of the 4 supported methods, for example el.classList.remove(“inactive”).remove(“disabled”).add(“active”)
function ClassListListener( el ) {
const ecl = el.classList;
['add','remove','toggle','replace'].forEach(prop=>{
el.classList['_'+prop] = ecl[prop]
el.classList[prop] = function() {
const args = Array.from(arguments)
this['_'+prop].apply(this, args)
el.dispatchEvent(new CustomEvent(
'classlistupdate',
{ detail: { method: prop, args } }
))
return this
}
})
return el
}
Useage:
const el = document.body
ClassListListener(el).addEventListener('classlistupdate', e => {
const args = e.detail.args.join(', ')
console.log('el.classList.'+e.detail.method+'('+args+')')
}, false)
el.classList
.add('test')
.replace('test', 'tested')
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | epascarello |
| Solution 2 | |
| Solution 3 | |
| Solution 4 | Loi Nguyen Huynh |
| Solution 5 |
