Question

I need to leverage this DOM event. IE has onpropertychange, which does what I need it to do also. Webkit doesn't seem to support this event, however. Is there an alternative I could use?

Was it helpful?

Solution

Although Chrome does not dispatch DOMAttrModified events, the more lightweighted mutation observers are supported since 2011 and these work for attribute changes, too.

Here is an example for the document body:

var element = document.body, bubbles = false;

var observer = new WebKitMutationObserver(function (mutations) {
  mutations.forEach(attrModified);
});
observer.observe(element, { attributes: true, subtree: bubbles });

function attrModified(mutation) {
  var name = mutation.attributeName,
    newValue = mutation.target.getAttribute(name),
    oldValue = mutation.oldValue;

  console.log(name, newValue, oldValue);
}

For a simple attribute change, the console.log statement would print:

<body color="black">
<script type="text/html">
document.body.setAttribute("color", "red");
</script>
</body>

Console:

> color red black

OTHER TIPS

If you are happy with merely detecting calls to setAttribute() (as opposed to monitoring all attribute modifications) then you could over-ride that method on all elements with:

Element.prototype._setAttribute = Element.prototype.setAttribute
Element.prototype.setAttribute = function(name, val) { 
 var e = document.createEvent("MutationEvents"); 
 var prev = this.getAttribute(name); 
 this._setAttribute(name, val);
 e.initMutationEvent("DOMAttrModified", true, true, null, prev, val, name, 2);
 this.dispatchEvent(e);
}

I had the same question and was thinking of modifying setAttribute, so seeing what Sean did, I copied that. Worked great, except that it was firing when an attribute was repeatedly set to the same value, so I added a check to my copy to skip firing the event if the value is not being changed. I also added val = String(val), based on the rationale that setAttribute will coerce numbers to strings, so the comparison should anticipate that.

My modified version is:

var emulateDOMAttrModified = {

  isSupportedNatively: function () {
    var supported = false;
    function handler() {
      supported = true;
    }
    document.addEventListener('DOMAttrModified', handler);
    var attr = 'emulateDOMAttrModifiedTEST';
    document.body.setAttribute(attr, 'foo'); // aka $('body').attr(attr, 'foo');
    document.removeEventListener('DOMAttrModified', handler);
    document.body.removeAttribute(attr);
    return supported;
  },

  install: function () {
    if (!this.isSupportedNatively() &&
      !Element.prototype._setAttribute_before_emulateDOMAttrModified) {
      Element.prototype._setAttribute_before_emulateDOMAttrModified = Element.prototype.setAttribute
      Element.prototype.setAttribute = function(name, val) {
        var prev = this.getAttribute(name);
        val = String(val); /* since attributes do type coercion to strings,
           do type coercion here too; in particular, D3 animations set x and y to a number. */
        if (prev !== val) {
          this._setAttribute_before_emulateDOMAttrModified(name, val);
          var e = document.createEvent('MutationEvents');
          e.initMutationEvent('DOMAttrModified', true, true, null, prev, val, name, 2);
          this.dispatchEvent(e);
        }
      };
    }
  }

};

// Install this when loaded.  No other file needs to reference this; it will just make Chrome and Safari
// support the standard same as Firefox does.
emulateDOMAttrModified.install();

Please refer code: https://github.com/meetselva/attrchange/blob/master/attrchange.js 'DOMAttrModified' + ('propertychange' for IE) are used there like in your case. If it's not suitable for you, the "ugly" solution that can satisfy this demand should be setInterval(function(){}, delay) Otherwise see Sean Hogan post above.

The solution provided by @Filip is close (and may have worked at the time) but now you need to request delivery of the old attribute value.

Thus, you'll want to change :

observer.observe(element, { attributes: true, subtree: bubbles });

to this:

observer.observe(element, { attributes: true, attributeOldvalue:true, subtree: bubbles });

Otherwise, you won't see the oldValues (you'll get null instead.) This was tested in Chrome 34.0.1847.131 (Official Build 265687) m.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top