├── .gitignore ├── src ├── banner.js ├── ComputedStyleObserverEntry.js ├── index.js ├── ComputedStyleObserver.js └── elementStateMonitor.js ├── package.json ├── LICENSE ├── dist ├── computedStyleObserver.min.js ├── computedStyleObserver.js └── computedStyleObserver.js.map └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/banner.js: -------------------------------------------------------------------------------- 1 | /* ComputedStyleObserver | (C) Keith Clark | MIT | https://github.com/keithclark/ComputedStyleObserver */ 2 | -------------------------------------------------------------------------------- /src/ComputedStyleObserverEntry.js: -------------------------------------------------------------------------------- 1 | export default class { 2 | constructor(target, property, value, prevValue) { 3 | this.target = target; 4 | this.property = property; 5 | this.value = value; 6 | this.previousValue = prevValue; 7 | } 8 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ComputedStyleObserver from './ComputedStyleObserver'; 2 | import ComputedStyleObserverEntry from './ComputedStyleObserverEntry'; 3 | 4 | window.ComputedStyleObserver = ComputedStyleObserver; 5 | window.ComputedStyleObserverEntry = ComputedStyleObserverEntry; 6 | -------------------------------------------------------------------------------- /src/ComputedStyleObserver.js: -------------------------------------------------------------------------------- 1 | import {registerElement, unregisterElement, unregisterObserver} from './elementStateMonitor'; 2 | 3 | let observers = new WeakMap(); 4 | 5 | export default class { 6 | 7 | constructor(callback, properties = null) { 8 | 9 | if (Array.isArray(properties)) { 10 | properties = [...properties]; 11 | } 12 | 13 | observers.set(this, {callback, properties}); 14 | } 15 | 16 | disconnect() { 17 | unregisterObserver(observers.get(this)); 18 | } 19 | 20 | observe(targetElement) { 21 | return registerElement(targetElement, observers.get(this)); 22 | } 23 | 24 | unobserve(targetElement) { 25 | return unregisterElement(targetElement, observers.get(this)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "computed-style-observer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "dev": "rollup -f iife -m -i src/index.js -o dist/computedStyleObserver.js -w", 8 | "build": "rollup -f iife -m -i src/index.js -o dist/computedStyleObserver.js", 9 | "minify": "minify dist/computedStyleObserver.js | cat src/banner.js - > dist/computedStyleObserver.min.js", 10 | "dist": "npm run build && npm run minify" 11 | }, 12 | "devDependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-minify": "^0.5.0", 15 | "rollup": "^0.57.1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/keithclark/ComputedStyleObserver.git" 20 | }, 21 | "author": "Keith Clark", 22 | "bugs": { 23 | "url": "https://github.com/keithclark/ComputedStyleObserver/issues" 24 | }, 25 | "homepage": "https://github.com/keithclark/ComputedStyleObserver#readme", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Keith Clark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/computedStyleObserver.min.js: -------------------------------------------------------------------------------- 1 | /* ComputedStyleObserver | (C) Keith Clark | MIT | https://github.com/keithclark/ComputedStyleObserver */ 2 | (function(){"use strict";const a=1,b=0;let c=b,d=new Map;const e=(b,e)=>{let f=d.get(b);return(f||(f={styles:{},observers:[]},d.set(b,f)),!f.observers.includes(e))&&(f.observers.push(e),c!==a&&(c=a,j()),!0)},f=(a,e)=>{let f=d.get(a);if(!f)return!1;let g=f.observers.indexOf(e);return-1!==g&&(f.observers.splice(g,1),0===f.observers.length&&d.delete(a),0===d.size&&(c=b),!0)},g=a=>{d.forEach((b,c)=>{b.observers.includes(a)&&f(c,a)})},h=()=>{d.forEach((a,b)=>{i(b,a)})},i=(a,b)=>{let c=getComputedStyle(a),d={};b.observers.forEach(e=>{let f=[];e.properties.forEach(e=>{let g=c[e],h=b.styles[e];g!==h&&h&&f.push(new ComputedStyleObserverEntry(a,e,g,h)),d[e]=g}),f.length&&e.callback(f)}),b.styles=d},j=()=>{c===a&&(requestAnimationFrame(j),h())};let k=new WeakMap;window.ComputedStyleObserver=class{constructor(a,b=null){Array.isArray(b)&&(b=[...b]),k.set(this,{callback:a,properties:b})}disconnect(){g(k.get(this))}observe(a){return e(a,k.get(this))}unobserve(a){return f(a,k.get(this))}},window.ComputedStyleObserverEntry=class{constructor(a,b,c,d){this.target=a,this.property=b,this.value=c,this.previousValue=d}}})(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Computed Style Observer 2 | 3 | This prototype `ComputedStyleObserver` provides the ability to watch for changes being made to the style properties of DOM elements. 4 | 5 | ## Example 6 | 7 | ```html 8 | 17 | 18 |
19 | Hover over me and watch the console 20 |
21 | 22 | 23 | 24 | 25 | 39 | ``` 40 | 41 | ## :warning: A note on performance 42 | 43 | Internally, `ComputedStyleObserver` calls `getComputedStyle` for each observed element, every animation frame. Since `getComputedStyle` will trigger a style recalc (and sometimes reflows), performance will suffer as the number of observed elements increases. 44 | 45 | --- 46 | 47 | ## ComputedStyleObserver 48 | 49 | ### Constructor 50 | 51 | #### `ComputedStyleObserver(callback, properties)` 52 | 53 | Creates and returns a new `ComputedStyleObserver` which will invoke a specified callback function when style changes for the given properties occur. 54 | 55 | The `callback` function will receive an array of `ComputedStyleObserverEntry` objects, one for each property change that occured. 56 | 57 | `properties` is an array of strings specifying which CSS property names should be 58 | observed. (i.e. "background-color", "border-radius" etc.) 59 | 60 | ### Methods 61 | 62 | #### `disconnect()` 63 | 64 | Prevents the `ComputedStyleObserver` instance from receiving further notifications until `observe()` is called again. 65 | 66 | #### `observe(targetElement)` 67 | 68 | Configures the `ComputedStyleObserver` instance to begin receiving notifications for the specificed `targetElement` through its callback function when style changes occur. 69 | 70 | #### `unobserve(targetElement)` 71 | 72 | Prevents the `ComputedStyleObserver` instance from receiving further notifications for the specificed `targetElement`. 73 | 74 | 75 | ## ComputedStyleObserverEntry 76 | 77 | A `ComputedStyleObserverEntry` represents an individual style mutation. It is the object that is passed to `ComputedStyleObserver`'s callback. 78 | 79 | ### Properties 80 | 81 | #### `target` 82 | The DOM node the mutation affected 83 | 84 | #### `property` 85 | The style property that mutated 86 | 87 | #### `value` 88 | The current value of the property 89 | 90 | #### `previousValue` 91 | The previous value of the property 92 | 93 | 94 | --- 95 | 96 | # Contributing 97 | 98 | ## Requirements 99 | 100 | * Node / NPM 101 | 102 | ## Setup 103 | 104 | 1) Clone this repo. 105 | 2) Install dependencies: `npm install` 106 | 3) Build the project with the watch task: `npm run dev` 107 | 4) Start editing... 108 | 109 | ## Other build options 110 | 111 | * `npm run dist` - builds the both the unminified and minified distribution files to the `/dist/` folder. 112 | -------------------------------------------------------------------------------- /src/elementStateMonitor.js: -------------------------------------------------------------------------------- 1 | import './ComputedStyleObserverEntry'; 2 | 3 | const POLLSTATE_RUNNING = 1; 4 | const POLLSTATE_STOPPED = 0; 5 | 6 | 7 | let pollState = POLLSTATE_STOPPED; 8 | let elements = new Map(); 9 | 10 | 11 | /** 12 | * Registers an element and an observer. 13 | * 14 | * @param {DOMElement} 15 | * The element to watch 16 | * @param {ComputedStyleObserver} 17 | * Observer instance responsible for handling changes 18 | */ 19 | const registerElement = (elem, observer) => { 20 | let state = elements.get(elem); 21 | 22 | if (!state) { 23 | state = { 24 | styles: {}, 25 | observers: [] 26 | } 27 | elements.set(elem, state); 28 | } 29 | 30 | // only allow an observer to watch an element once 31 | if (state.observers.includes(observer)) { 32 | return false; 33 | } 34 | 35 | state.observers.push(observer); 36 | 37 | // if we're not already polling the DOM, start now. 38 | if (pollState !== POLLSTATE_RUNNING) { 39 | pollState = POLLSTATE_RUNNING; 40 | poll(); 41 | } 42 | 43 | return true; 44 | } 45 | 46 | 47 | /** 48 | * 49 | * @param {DOMElement} 50 | * The element to stop watching 51 | * @param {ComputedStyleObserver} 52 | * Observer instance responsible for handling changes 53 | */ 54 | const unregisterElement = (elem, observer) => { 55 | let state = elements.get(elem); 56 | if (!state) { 57 | return false; 58 | } 59 | 60 | let index = state.observers.indexOf(observer); 61 | 62 | // if the observer doesn't exist, exit now 63 | if (index === -1) { 64 | return false; 65 | } 66 | 67 | state.observers.splice(index, 1); 68 | 69 | // remove the element from the map if it has no observers 70 | if (state.observers.length === 0) { 71 | elements.delete(elem); 72 | } 73 | 74 | // if the map is empty, stop polling the DOM 75 | if (elements.size === 0) { 76 | pollState = POLLSTATE_STOPPED; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | 83 | /** 84 | * Stop watching all elements for a specific observer 85 | */ 86 | const unregisterObserver = observer => { 87 | elements.forEach((state, element) => { 88 | if (state.observers.includes(observer)) { 89 | unregisterElement(element, observer); 90 | } 91 | }) 92 | } 93 | 94 | 95 | /** 96 | * Updates the state of every currently observed element 97 | */ 98 | const update = () => { 99 | elements.forEach((state, element) => { 100 | updateElementState(element, state); 101 | }); 102 | } 103 | 104 | 105 | /** 106 | * Determines if the `computedStyle` of the passed element has changed since 107 | * this function was last called. Any changes in `computedStyle` are filtered 108 | * against the property list of each ComputedStyleObserver and, if any 109 | * relevant changes exist, the observer callback is invoked with a list of 110 | * `ComputedStyleObserverEntry` objects. 111 | * 112 | * @param {DOMElement} elem 113 | * The element to update 114 | * @param {Object} state 115 | * The element's state object 116 | */ 117 | const updateElementState = (elem, state) => { 118 | let styles = getComputedStyle(elem); 119 | let newValues = {}; 120 | 121 | state.observers.forEach(observer => { 122 | let changes = []; 123 | 124 | observer.properties.forEach(property => { 125 | let value = styles[property]; 126 | let previousValue = state.styles[property]; 127 | if (value !== previousValue) { 128 | if (previousValue) { 129 | changes.push(new ComputedStyleObserverEntry( 130 | elem, 131 | property, 132 | value, 133 | previousValue 134 | )); 135 | } 136 | } 137 | newValues[property] = value; 138 | }); 139 | 140 | if (changes.length) { 141 | observer.callback(changes); 142 | } 143 | }) 144 | 145 | state.styles = newValues; 146 | } 147 | 148 | 149 | /** 150 | * Start watching elements for changes. 151 | */ 152 | const poll = () => { 153 | if (pollState === POLLSTATE_RUNNING) { 154 | requestAnimationFrame(poll); 155 | update(); 156 | } 157 | } 158 | 159 | 160 | export { 161 | registerElement, 162 | unregisterElement, 163 | unregisterObserver 164 | } 165 | -------------------------------------------------------------------------------- /dist/computedStyleObserver.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | class ComputedStyleObserverEntry$1 { 5 | constructor(target, property, value, prevValue) { 6 | this.target = target; 7 | this.property = property; 8 | this.value = value; 9 | this.previousValue = prevValue; 10 | } 11 | } 12 | 13 | const POLLSTATE_RUNNING = 1; 14 | const POLLSTATE_STOPPED = 0; 15 | 16 | 17 | let pollState = POLLSTATE_STOPPED; 18 | let elements = new Map(); 19 | 20 | 21 | /** 22 | * Registers an element and an observer. 23 | * 24 | * @param {DOMElement} 25 | * The element to watch 26 | * @param {ComputedStyleObserver} 27 | * Observer instance responsible for handling changes 28 | */ 29 | const registerElement = (elem, observer) => { 30 | let state = elements.get(elem); 31 | 32 | if (!state) { 33 | state = { 34 | styles: {}, 35 | observers: [] 36 | }; 37 | elements.set(elem, state); 38 | } 39 | 40 | // only allow an observer to watch an element once 41 | if (state.observers.includes(observer)) { 42 | return false; 43 | } 44 | 45 | state.observers.push(observer); 46 | 47 | // if we're not already polling the DOM, start now. 48 | if (pollState !== POLLSTATE_RUNNING) { 49 | pollState = POLLSTATE_RUNNING; 50 | poll(); 51 | } 52 | 53 | return true; 54 | }; 55 | 56 | 57 | /** 58 | * 59 | * @param {DOMElement} 60 | * The element to stop watching 61 | * @param {ComputedStyleObserver} 62 | * Observer instance responsible for handling changes 63 | */ 64 | const unregisterElement = (elem, observer) => { 65 | let state = elements.get(elem); 66 | if (!state) { 67 | return false; 68 | } 69 | 70 | let index = state.observers.indexOf(observer); 71 | 72 | // if the observer doesn't exist, exit now 73 | if (index === -1) { 74 | return false; 75 | } 76 | 77 | state.observers.splice(index, 1); 78 | 79 | // remove the element from the map if it has no observers 80 | if (state.observers.length === 0) { 81 | elements.delete(elem); 82 | } 83 | 84 | // if the map is empty, stop polling the DOM 85 | if (elements.size === 0) { 86 | pollState = POLLSTATE_STOPPED; 87 | } 88 | 89 | return true; 90 | }; 91 | 92 | 93 | /** 94 | * Stop watching all elements for a specific observer 95 | */ 96 | const unregisterObserver = observer => { 97 | elements.forEach((state, element) => { 98 | if (state.observers.includes(observer)) { 99 | unregisterElement(element, observer); 100 | } 101 | }); 102 | }; 103 | 104 | 105 | /** 106 | * Updates the state of every currently observed element 107 | */ 108 | const update = () => { 109 | elements.forEach((state, element) => { 110 | updateElementState(element, state); 111 | }); 112 | }; 113 | 114 | 115 | /** 116 | * Determines if the `computedStyle` of the passed element has changed since 117 | * this function was last called. Any changes in `computedStyle` are filtered 118 | * against the property list of each ComputedStyleObserver and, if any 119 | * relevant changes exist, the observer callback is invoked with a list of 120 | * `ComputedStyleObserverEntry` objects. 121 | * 122 | * @param {DOMElement} elem 123 | * The element to update 124 | * @param {Object} state 125 | * The element's state object 126 | */ 127 | const updateElementState = (elem, state) => { 128 | let styles = getComputedStyle(elem); 129 | let newValues = {}; 130 | 131 | state.observers.forEach(observer => { 132 | let changes = []; 133 | 134 | observer.properties.forEach(property => { 135 | let value = styles[property]; 136 | let previousValue = state.styles[property]; 137 | if (value !== previousValue) { 138 | if (previousValue) { 139 | changes.push(new ComputedStyleObserverEntry( 140 | elem, 141 | property, 142 | value, 143 | previousValue 144 | )); 145 | } 146 | } 147 | newValues[property] = value; 148 | }); 149 | 150 | if (changes.length) { 151 | observer.callback(changes); 152 | } 153 | }); 154 | 155 | state.styles = newValues; 156 | }; 157 | 158 | 159 | /** 160 | * Start watching elements for changes. 161 | */ 162 | const poll = () => { 163 | if (pollState === POLLSTATE_RUNNING) { 164 | requestAnimationFrame(poll); 165 | update(); 166 | } 167 | }; 168 | 169 | let observers = new WeakMap(); 170 | 171 | class ComputedStyleObserver { 172 | 173 | constructor(callback, properties = null) { 174 | 175 | if (Array.isArray(properties)) { 176 | properties = [...properties]; 177 | } 178 | 179 | observers.set(this, {callback, properties}); 180 | } 181 | 182 | disconnect() { 183 | unregisterObserver(observers.get(this)); 184 | } 185 | 186 | observe(targetElement) { 187 | return registerElement(targetElement, observers.get(this)); 188 | } 189 | 190 | unobserve(targetElement) { 191 | return unregisterElement(targetElement, observers.get(this)); 192 | } 193 | 194 | } 195 | 196 | window.ComputedStyleObserver = ComputedStyleObserver; 197 | window.ComputedStyleObserverEntry = ComputedStyleObserverEntry$1; 198 | 199 | }()); 200 | //# sourceMappingURL=computedStyleObserver.js.map 201 | -------------------------------------------------------------------------------- /dist/computedStyleObserver.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"computedStyleObserver.js","sources":["../src/ComputedStyleObserverEntry.js","../src/elementStateMonitor.js","../src/ComputedStyleObserver.js","../src/index.js"],"sourcesContent":["export default class {\n constructor(target, property, value, prevValue) {\n this.target = target;\n this.property = property;\n this.value = value;\n this.previousValue = prevValue;\n }\n}","import './ComputedStyleObserverEntry';\n\nconst POLLSTATE_RUNNING = 1;\nconst POLLSTATE_STOPPED = 0;\n\n\nlet pollState = POLLSTATE_STOPPED;\nlet elements = new Map();\n\n\n/**\n * Registers an element and an observer.\n * \n * @param {DOMElement}\n * The element to watch\n * @param {ComputedStyleObserver} \n * Observer instance responsible for handling changes\n */\nconst registerElement = (elem, observer) => {\n let state = elements.get(elem);\n\n if (!state) {\n state = {\n styles: {},\n observers: []\n }\n elements.set(elem, state);\n }\n\n // only allow an observer to watch an element once\n if (state.observers.includes(observer)) {\n return false;\n }\n\n state.observers.push(observer);\n\n // if we're not already polling the DOM, start now.\n if (pollState !== POLLSTATE_RUNNING) {\n pollState = POLLSTATE_RUNNING;\n poll();\n }\n\n return true;\n}\n\n\n/**\n * \n * @param {DOMElement}\n * The element to stop watching\n * @param {ComputedStyleObserver}\n * Observer instance responsible for handling changes\n */\nconst unregisterElement = (elem, observer) => {\n let state = elements.get(elem);\n if (!state) {\n return false;\n }\n\n let index = state.observers.indexOf(observer);\n\n // if the observer doesn't exist, exit now\n if (index === -1) {\n return false;\n }\n\n state.observers.splice(index, 1);\n\n // remove the element from the map if it has no observers\n if (state.observers.length === 0) {\n elements.delete(elem);\n }\n\n // if the map is empty, stop polling the DOM\n if (elements.size === 0) {\n pollState = POLLSTATE_STOPPED;\n }\n\n return true;\n}\n\n\n/**\n * Stop watching all elements for a specific observer\n */\nconst unregisterObserver = observer => {\n elements.forEach((state, element) => {\n if (state.observers.includes(observer)) {\n unregisterElement(element, observer);\n }\n })\n}\n\n\n/**\n * Updates the state of every currently observed element\n */\nconst update = () => {\n elements.forEach((state, element) => {\n updateElementState(element, state);\n });\n}\n\n\n/**\n * Determines if the `computedStyle` of the passed element has changed since\n * this function was last called. Any changes in `computedStyle` are filtered\n * against the property list of each ComputedStyleObserver and, if any\n * relevant changes exist, the observer callback is invoked with a list of\n * `ComputedStyleObserverEntry` objects.\n * \n * @param {DOMElement} elem\n * The element to update\n * @param {Object} state\n * The element's state object\n */\nconst updateElementState = (elem, state) => {\n let styles = getComputedStyle(elem);\n let newValues = {};\n\n state.observers.forEach(observer => {\n let changes = [];\n \n observer.properties.forEach(property => {\n let value = styles[property];\n let previousValue = state.styles[property];\n if (value !== previousValue) {\n if (previousValue) {\n changes.push(new ComputedStyleObserverEntry(\n elem,\n property,\n value,\n previousValue\n ));\n }\n }\n newValues[property] = value;\n });\n\n if (changes.length) {\n observer.callback(changes);\n }\n })\n\n state.styles = newValues;\n}\n\n\n/**\n * Start watching elements for changes.\n */\nconst poll = () => {\n if (pollState === POLLSTATE_RUNNING) {\n requestAnimationFrame(poll);\n update();\n }\n}\n\n\nexport {\n registerElement,\n unregisterElement,\n unregisterObserver\n}\n","import {registerElement, unregisterElement, unregisterObserver} from './elementStateMonitor';\n\nlet observers = new WeakMap();\n\nexport default class {\n\n constructor(callback, properties = null) {\n\n if (Array.isArray(properties)) {\n properties = [...properties];\n }\n\n observers.set(this, {callback, properties});\n }\n\n disconnect() {\n unregisterObserver(observers.get(this));\n }\n\n observe(targetElement) {\n return registerElement(targetElement, observers.get(this));\n }\n\n unobserve(targetElement) {\n return unregisterElement(targetElement, observers.get(this));\n }\n\n}\n","import ComputedStyleObserver from './ComputedStyleObserver';\nimport ComputedStyleObserverEntry from './ComputedStyleObserverEntry';\n\nwindow.ComputedStyleObserver = ComputedStyleObserver;\nwindow.ComputedStyleObserverEntry = ComputedStyleObserverEntry;\n"],"names":["ComputedStyleObserverEntry"],"mappings":"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"} --------------------------------------------------------------------------------