JavaScript: Listen for attribute change?
Asked Answered
V

3

133

Is it possible in JavaScript to listen for a change of attribute value? For example:

var element=document.querySelector('…');
element.addEventListener( ? ,doit,false);

element.setAttribute('something','whatever');

function doit() {

}

I would like to respond to any change in the something attribute.

I have read up on the MutationObserver object, as well as alternatives to that (including the one which uses animation events). As far as I can tell, they are about changes to the actual DOM. I’m more interested in attribute changes to a particular DOM element, so I don’t think that’s it. Certainly in my experimenting it doesn’t seem to work.

I would like to do this without jQuery.

Thanks

Valleau answered 2/1, 2017 at 10:25 Comment(1)
MutationObserver works for that. Just configure it to listen to attribute changes. Set it to observe only the element you’re interested in.Fanni
P
217

You need MutationObserver, Here in snippet I have used setTimeout to simulate modifying attribute

var element = document.querySelector('#test');
setTimeout(function() {
  element.setAttribute('data-text', 'whatever');
}, 5000)

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (mutation.type === "attributes") {
      console.log("attributes changed");

      // Example of accessing the element for which 
      // event was triggered
      mutation.target.textContent = "Attribute of the element changed";
    }
    
    console.log(mutation.target);
  });
});

observer.observe(element, {
  attributes: true //configure it to listen to attribute changes
});
<div id="test">Dummy Text</div>

Additionally, mutation.target property gives the reference to mutated/changed node.

Poland answered 2/1, 2017 at 10:30 Comment(5)
Thanks for the answer and for the sample. Once I have streamlined this, I think it will be good technique for making more complex changes to elements by simply changing and attribute. It makes an easier API. Would you be aware of a reasonable polyfill for primitive browsers?Valleau
@Manngo, For primitive browsers you can use Mutation events and also see davidwalsh.name/dom-events-javascriptPoland
I see that it is currently supported back to IE9, and still supported in modern browsers. Thanks again.Valleau
kinda useless when we have no access to the element in the observerRebeckarebeka
mutation.target is the element within the observer mutationsImpeccable
S
12

This question is already answered, but I'd like to share my experiences, because the mutation observer did not bring me the insights in needed.

Note This is some kind of hacky solution, but for (at least) debugging purposes quite good.

You can override the setAttribute function of a particalar element. This way you can also print the callstack, and get an insight of "who" changed the attribute value:

// select the target element
const target = document.querySelector("#element");
// store the original setAttribute reference
const setAttribute = target.setAttribute;
// override setAttribte
target.setAttribute = (key: string, value: string) => {
  console.trace("--trace");
  // use call, to set the context and prevent illegal invocation errors
  setAttribute.call(target, key, value); 
};
Sprain answered 12/4, 2022 at 7:1 Comment(1)
A clever hack. Note that if setAttributeNS() is called this will fail.Merkle
C
0

Use pub/sub (took this from someone who, no doubt, rightfully stole it from someone else):

        PubSub: function () {

        //var topics = {};
        //var hOP = topics.hasOwnProperty;
        var hasProp = Me.Subscriptions.hasOwnProperty;

        return {
            Subscribe: function (topic, listener) {
                // Create the topic's object if not yet created
                if (!hasProp.call(Me.Subscriptions, topic)) Me.Subscriptions[topic] = [];

                // Add the listener to queue
                var index = Me.Subscriptions[topic].push(listener) - 1;

                // Provide handle back for removal of topic
                return {
                    remove: function () {
                        delete Me.Subscriptions[topic][index];
                    }
                };
            },
            Publish: function (topic, info) {
                // If the topic doesn't exist, or there's no listeners in queue, just leave
                if (!hasProp.call(Me.Subscriptions, topic)) return;

                // Cycle through topics queue, fire!
                Me.Subscriptions[topic].forEach(function (item) {
                    item(info != undefined ? info : {});
                });
            }
        };
    },

Usage:

Publish:

PubSub.Subscriber().Publish('AppLoaded', true);

Subscribe:

PubSub.Subscriber().Subscribe('AppLoaded', function (result) {

    //I can now do things I'll tell my friends I did anyway
            
});
Curvilinear answered 16/11, 2023 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.