is there an alternative to DOMAttrModified that will work in webkit
Asked Answered
G

5

21

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?

Greene answered 10/12, 2009 at 16:28 Comment(2)
I was able to come up with something based on this http://www.west-wind.com/Weblog/posts/453942.aspxGreene
Based on the solution by David Walsh I have created a small library to catch DOM insertions. If you can write a CSS selector that matches to the element after the change you are interested in - this is a valid solution. It covers more browsers than DOM Mutation Observers. See: github.com/naugtur/insertionQueryJovi
K
24

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

Koala answered 5/5, 2012 at 21:46 Comment(4)
It seems that this will currently not work in combination with the CSS3 "resize: both" property. The observer will not pick up changes to the element by the user dragging the resize handle.Afterworld
Is there any way I can see where the change originated from? I've got an element whose class is being changed somewhere through JS, and I'm trying to figure out where from, but Chrome's stack trace of the event doesn't show the code that initiated the event.Handhold
it's worth noting that DOMAttrModified triggers immediately when the attribute changes, but mutation observers queue an event. So there's a not-so-subtle difference between the two.Medlar
I believe the Webkit prefix is no longer necessary. caniuse.com/#search=MutationObserverHogback
A
14

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);
}
Analysis answered 3/3, 2010 at 11:31 Comment(0)
R
3

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();
Reptile answered 3/10, 2014 at 5:19 Comment(0)
R
0

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.

Rawdon answered 5/3, 2014 at 8:18 Comment(0)
A
0

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.

Almeta answered 5/5, 2014 at 1:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.