admin管理员组

文章数量:1277271

Is there a non-polling method to listen for changes to an element's puted style?

This fantasy code snippet should explain what I mean succinctly:

var el = document.getElementById('doodad');

el.addComputedStyleChangeListener('width', function (prev, new) {
  alert('Previous width: ' + prev + '; New width: ' + new);
});

I'm aware of the DOMAttrModified mutation event and the uping MutationObserver, but neither is sufficient -- they can only be used to watch the style DOM attribute of an element, which doesn't wholly determine an element's puted style.


The use case for this was originally part of this question, which really just lead me down a course of curiosity.

Is there a non-polling method to listen for changes to an element's puted style?

This fantasy code snippet should explain what I mean succinctly:

var el = document.getElementById('doodad');

el.addComputedStyleChangeListener('width', function (prev, new) {
  alert('Previous width: ' + prev + '; New width: ' + new);
});

I'm aware of the DOMAttrModified mutation event and the uping MutationObserver, but neither is sufficient -- they can only be used to watch the style DOM attribute of an element, which doesn't wholly determine an element's puted style.


The use case for this was originally part of this question, which really just lead me down a course of curiosity.

Share Improve this question edited May 23, 2017 at 12:00 CommunityBot 11 silver badge asked Nov 1, 2012 at 22:23 namuolnamuol 9,9966 gold badges43 silver badges54 bronze badges 9
  • 6 As far as I know: no. – Matt Ball Commented Nov 1, 2012 at 22:25
  • The DOMAttrModified event will be deprecated in DOM 3 Events so not useful anyway (and I think support is pretty patchy to start with). All mutation events will be deprecated, there will be a DOM 4 MutationObserver instead. – RobG Commented Nov 1, 2012 at 23:26
  • Agreed with @MattBall, you either trigger an event handler explicitly or use polling – mccannf Commented Nov 2, 2012 at 0:08
  • Maybe if you described the goal of doing this people would be able to suggest alternate approaches. – Stephen P Commented Nov 2, 2012 at 0:46
  • 3 @namuol - jsfiddle/e5QW5 shows the basic idea. It adds an iframe to the element with a size proportional to the element. When the element size changes, so does the iframe, and the resize event for the iframe's window fires, which can be listened for. – Alohci Commented Nov 2, 2012 at 9:18
 |  Show 4 more ments

3 Answers 3

Reset to default 3

There is no such method. CSS OM is not there yet.

And it is not clear what "puted style change" means.

In principle you can detect change of used (for e.g. rendering) style. But this will need some event like "paint" or "layout" to happen.

A way to achieve this without relying on requestAnimationFrame is to add a very tight transition duration to the property you want to track and then listen to the transitionstart event to get the value.

#target {
  transition: width 0.001ms step-start;
}
document.getElementById('target').addEventListener('transitionstart', () => {
  const styles = getComputedStyle(document.getElementById('target'));
  const width = styles.getPropertyValue('width');
  console.log(`Width: ${width}`);
});

For discrete properties, don’t forget to include transition-behavior: allow-discrete in your CSS.

To make things easy, I have abstracted away this approach into an easy to use library named @bramus/style-observer

import CSSStyleObserver from "@bramus/style-observer";

const cssStyleObserver = new CSSStyleObserver(
    ['width'],
    (values) => {
        showNotification(values['width']);
    }
);

cssStyleObserver.attach(document.getElementById('target'));

The best you can do at the moment is request animation frames.

getComputedStyle apparently returns a live-updating object of puted properties.

You can do a basic animation loop like this:

var putedStyle = getComputedStyle(element);
var animate = function () {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = putedStyle.color;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    requestAnimationFrame(animate);
};
animate();

You could potentially optimize by only updating if any of the used properties have changed:

var putedStyle = getComputedStyle(element);
var lastFrameBackground;
var lastFrameColor;
var animate = function () {
    if (
        putedStyle.background !== lastFrameBackground ||
        putedStyle.color !== lastFrameColor
    ) {
       lastFrameBackground = putedStyle.background;
       lastFrameColor = putedStyle.color;

       // assuming expensive drawing code here
       // not like this!
       ctx.clearRect(0, 0, canvas.width, canvas.height);
       ctx.fillStyle = putedStyle.color;
       ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
    requestAnimationFrame(animate);
};
animate();

If it's for a specific CSS animation, you could maybe manage the requestAnimationFrame loop by listening for animationstart and animationend, or if those events don't have good enough browser support, you could kick it off when you know the animation will start (e.g. mouseenter for :hover) and stop when an animated property's puted value stops changing (i.e. don't call requestAnimationFrame if it's equal to its previous value).

If you don't need to animate smoothly, you could use setInterval for possibly-better performance (checking if the document is hidden which requestAnimationFrame does implicitly):

var putedStyle = getComputedStyle(element);
setInterval(function () {
    if (!document.hidden) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = putedStyle.color;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
}, 200);

本文标签: javascriptFire an event when DOM element39s computed style changesStack Overflow