8

I'm trying to find a way to detect changes to the element style but I haven't had much luck. The code below works on a new property I define like tempBgColor but I cannot override/shadow an existing property like color. I know jquery has a watch function, but it only detects changes from the jquery api but not directly changing the value of a style something like elem.style.color.

var e = document.getElementById('element');
e.style.__defineGetter__("color", function() {
   return "A property";
});
e.style.__defineSetter__("color", function(val) {
    alert("Setting " + val + "!");
});

Any pointers?

webber
  • 1,834
  • 5
  • 24
  • 56

3 Answers3

18

You should be able to do this with a MutationObserver - see demo (Webkit only), which is the new, shiny way of getting notified about changes in the DOM. The older, now deprecated, way was Mutation events.

Demo simply logs in the console the old and new values when the paragraph is clicked. Note that the old value will not be available if it was set via a non-inline CSS rule, but the change will still be detected.

HTML

<p id="observable" style="color: red">Lorem ipsum</p>​

JavaScript

var MutationObserver = window.WebKitMutationObserver;

var target = document.querySelector('#observable');

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log('old', mutation.oldValue);
    console.log('new', mutation.target.style.cssText);
  });    
});

var config = { attributes: true, attributeOldValue: true }

observer.observe(target, config);

// click event to change colour of the thing we are observing
target.addEventListener('click', function(ev) {
    observable.style.color = 'green';
    return false;
}, false);

Credit to this blog post, for some of the code above.

andyb
  • 43,435
  • 12
  • 121
  • 150
  • amazing! Any idea hows the performance if you're observing the whole doc? – webber Nov 26 '12 at 14:55
  • I am only really aware that the old method performed [very badly](https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.platform/L0Lx11u5Bvs) and the [new pattern](http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/0779.html) was designed as an efficient replacement. I also had a [presentation](http://www.youtube.com/watch?v=eRZ4pO0gVWw) bookmarked which might be useful. Sorry but I don't have any comparative tests or idea on the performance impact of observing a document. Suspect it will vary between browsers though :-) – andyb Nov 26 '12 at 15:07
  • Thanks! i love how clean it is – webber Nov 26 '12 at 15:26
  • Andyb: is there an observer to detected attached events? – webber Nov 27 '12 at 21:52
  • It looks like it was considered for DOM3 but dropped according to http://stackoverflow.com/questions/7810534/have-any-browsers-implemented-the-dom3-eventlistenerlist/7814692#7814692. Sorry, I don't know of a native way to detect an event (de|at)tach. – andyb Nov 28 '12 at 09:09
  • You could write a custom wrapper function that handles the (de|at)tach but also maintains a store of which events are bound - this is how jQuery does it - see https://coderwall.com/p/b1o2gw. jQuery can list _some_ events http://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node. Chrome can show events in the _Console_ so it's certainly possible within the browser and maybe in the Chrome API if you are writing an extension. – andyb Nov 28 '12 at 09:10
  • This only detects changes to style and reports them as a change to the style attribute, not the sub-property such as color. :-( – Michael Sep 15 '18 at 17:50
  • @Michael I'm not exactly sure what you mean. The demo logs the before and after colour values. Do you want to know the exact CSS property that changed? If so, it might be worth asking a new question – andyb Sep 17 '18 at 20:04
17

With Chrome's Developer Tools open, you can find the element whose style's change you're interested in, right click it, select "Break on..." and "Attributes modifications".

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Stubbs
  • 323
  • 1
  • 4
  • 14
1

here is a naive implementation using setTimeout with undescorejs.

The only way to find out which change was made is to iterate through the style object properties.

Here is the live example

$( function () {
  var ele = document.getElementById('ele'), 
      oldStyle = {};

function checkEquality() {
  style = _.clone(ele.style);
  if (!_.isEqual(style, oldStyle)) {
    console.log('Not equal');
    oldStyle = _.clone(style);
  } else {
    console.log('Equal');
  }
  _.delay(checkEquality, 2000);
}

checkEquality();

$('a#add_prop').on('click', function () {
  var props = $('#prop').val().replace(/ /g, '').split(':');
  console.log(props);
  $(ele).css(props[0], props[1]);
});

$('#prop').on('keydown', function (e) {
  if (e.keyCode == 13) {
    $('a#add_prop').trigger('click');    
  }
});

});
AlexandruSerban
  • 312
  • 2
  • 9