├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dom-css 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | Small module for fast and reliable DOM styling. 6 | 7 | - normalizes for camel and dash case (see [to-camel-case](https://www.npmjs.com/package/to-camel-case)) 8 | - detects vendor prefixes as necessary, cached for performance (see [prefix-style](https://github.com/mattdesl/prefix-style)) 9 | - converts numbers to `px` strings for common properties (see [add-px-to-style](https://www.npmjs.com/package/add-px-to-style)) 10 | 11 | ```js 12 | var css = require('dom-css') 13 | 14 | //set a style 15 | css(element, 'position', 'absolute') 16 | 17 | //will be set as "WebkitFontSmoothing" on Chrome 18 | css(element, 'font-smoothing', 'none') 19 | 20 | //set multiple styles 21 | css(element, { 22 | // can be camel or dash case 23 | 'background-color': 'blue', 24 | 25 | // you can use numbers to auto-"px" 26 | left: 25, 27 | top: 0, 28 | marginTop: 0, 29 | position: 'absolute', 30 | 31 | // certain props will not have "px" added 32 | opacity: 0.5 33 | }) 34 | 35 | //get the current style 36 | css.get(element, 'position') 37 | // -> 'absolute' 38 | 39 | css.get(element, ['left', 'marginTop']) 40 | // -> { left: '25px', marginTop: '0px' } 41 | ``` 42 | 43 | **Note:** The `get()` method does not *compute* an element's style, it only fetches the currently set inline style. 44 | 45 | ## Usage 46 | 47 | [![NPM](https://nodei.co/npm/dom-css.png)](https://nodei.co/npm/dom-css/) 48 | 49 | #### `css(element, property, value)` 50 | #### `css.set(element, property, value)` 51 | 52 | Styles an element with the css `property` (dash or camel case) and a given value. `value` is a string, or a number to be suffixed with `'px'` (special cases, see below). 53 | 54 | #### `css(element, styles)` 55 | #### `css.set(element, styles)` 56 | 57 | A shorthand for setting multiple styles, where `styles` is an object containing `property:value` pairs. 58 | 59 | #### `css.get(element, prop)` 60 | 61 | Gets the inline style of element, where `prop` is a string (like `"borderRadius"`) or an array of strings. If an array of strings is given, an object is returned with key-value pairs representing the specified properties. 62 | 63 | ```js 64 | css.get(div, ['width', 'height']) 65 | //=> { width: '20px', height: '40px' } 66 | ``` 67 | 68 | This does not provide the *computed* style, only the current inline style. 69 | 70 | #### auto px 71 | 72 | If a number is specified, the value will have `"px"` added to it, *unless* it is a special unitless property like `'opacity'` and `'zIndex'`. See the full list in [add-px-to-style](https://www.npmjs.com/package/add-px-to-style) (sourced from React). 73 | 74 | ## Changelog 75 | 76 | - `2.x` 77 | - formatted to [standard](https://npmjs.com/package/standard) code style 78 | - updates to latest `prefix-style`, since `'Khtml'` prefix has long been obsolete 79 | - now all properties are suffixed with "px" except a few like `opacity`, `zIndex`, etc. The list is sourced from React and maintained in another module. 80 | - `1.x` - initial version which had a list of properties to be suffixed with "px" 81 | 82 | ## License 83 | 84 | Special thanks to Paul Irish's gist for the prefix detection (now part of Modernizr). 85 | 86 | MIT, see [LICENSE.md](http://github.com/mattdesl/dom-css/blob/master/LICENSE.md) for details. 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var prefix = require('prefix-style') 2 | var toCamelCase = require('to-camel-case') 3 | var cache = { 'float': 'cssFloat' } 4 | var addPxToStyle = require('add-px-to-style') 5 | 6 | function style (element, property, value) { 7 | var camel = cache[property] 8 | if (typeof camel === 'undefined') { 9 | camel = detect(property) 10 | } 11 | 12 | // may be false if CSS prop is unsupported 13 | if (camel) { 14 | if (value === undefined) { 15 | return element.style[camel] 16 | } 17 | 18 | element.style[camel] = addPxToStyle(camel, value) 19 | } 20 | } 21 | 22 | function each (element, properties) { 23 | for (var k in properties) { 24 | if (properties.hasOwnProperty(k)) { 25 | style(element, k, properties[k]) 26 | } 27 | } 28 | } 29 | 30 | function detect (cssProp) { 31 | var camel = toCamelCase(cssProp) 32 | var result = prefix(camel) 33 | cache[camel] = cache[cssProp] = cache[result] = result 34 | return result 35 | } 36 | 37 | function set () { 38 | if (arguments.length === 2) { 39 | if (typeof arguments[1] === 'string') { 40 | arguments[0].style.cssText = arguments[1] 41 | } else { 42 | each(arguments[0], arguments[1]) 43 | } 44 | } else { 45 | style(arguments[0], arguments[1], arguments[2]) 46 | } 47 | } 48 | 49 | module.exports = set 50 | module.exports.set = set 51 | 52 | module.exports.get = function (element, properties) { 53 | if (Array.isArray(properties)) { 54 | return properties.reduce(function (obj, prop) { 55 | obj[prop] = style(element, prop || '') 56 | return obj 57 | }, {}) 58 | } else { 59 | return style(element, properties || '') 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-css", 3 | "version": "2.1.0", 4 | "description": "fast dom CSS styling", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "dependencies": { 13 | "add-px-to-style": "1.0.0", 14 | "prefix-style": "2.0.1", 15 | "to-camel-case": "1.0.0" 16 | }, 17 | "devDependencies": { 18 | "browserify": "^8.1.1", 19 | "smokestack": "^3.2.0", 20 | "standard": "^5.4.1", 21 | "tap-closer": "^1.0.0", 22 | "tap-spec": "^2.1.2", 23 | "tape": "^3.2.0" 24 | }, 25 | "scripts": { 26 | "test": "standard && browserify test.js | tap-closer | smokestack | tap-spec" 27 | }, 28 | "keywords": [ 29 | "dom", 30 | "css", 31 | "style", 32 | "sheet", 33 | "animate", 34 | "element", 35 | "px", 36 | "pixels" 37 | ], 38 | "repository": { 39 | "type": "git", 40 | "url": "git://github.com/mattdesl/dom-css.git" 41 | }, 42 | "homepage": "https://github.com/mattdesl/dom-css", 43 | "bugs": { 44 | "url": "https://github.com/mattdesl/dom-css/issues" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var css = require('./') 2 | var test = require('tape') 3 | var prefix = require('prefix-style') 4 | 5 | test('handles multiple', function (t) { 6 | var div = document.createElement('div') 7 | css(div, { 8 | 'background-color': 'blue', 9 | marginTop: '10px', 10 | width: 20, 11 | height: 20, 12 | position: 'absolute', 13 | top: 20, 14 | left: 20, 15 | zIndex: 45, 16 | opacity: 0.5, 17 | 'font-smoothing': 'none' 18 | }) 19 | 20 | document.body.appendChild(div) 21 | 22 | var style = window.getComputedStyle(div, null) 23 | t.equal(style.backgroundColor, 'rgb(0, 0, 255)', 'converts dash to camel case') 24 | t.equal(style.width, '20px', 'injects px for standard properties') 25 | t.equal(style.opacity, '0.5', 'avoids px for unitless CSS properties') 26 | t.equal(style.zIndex, '45', 'avoids px for unitless CSS properties') 27 | t.equal(style.marginTop, '10px', 'accepts camel case props') 28 | 29 | var prefixed = prefix('fontSmoothing') 30 | if (prefixed) { // webkit only test 31 | t.equal(style[prefixed], 'none', 'hanldes prefixing') 32 | } 33 | 34 | css(div, 'marginTop', 20) 35 | css(div, 'width', '') // clears a style 36 | style = window.getComputedStyle(div, null) 37 | t.equal(style.marginTop, '20px', 'single property version works') 38 | t.equal(style.width, '0px', 'empty string clears style') 39 | 40 | document.body.removeChild(div) 41 | t.end() 42 | }) 43 | 44 | test('transforms', function (t) { 45 | var div = document.body.appendChild(document.createElement('div')) 46 | css(div, { 47 | position: 'absolute', 48 | top: 0, 49 | left: 0, 50 | width: 100, 51 | display: 'inline-block', 52 | height: 100, 53 | background: 'blue', 54 | padding: 0, 55 | margin: 0 56 | }) 57 | 58 | // if transforms are supported 59 | if (prefix('transform')) { 60 | var width = div.getBoundingClientRect().width 61 | t.equal(width, 100, 'starts with 100px') 62 | 63 | css(div, 'transform', 'translateZ(10px)') 64 | width = div.getBoundingClientRect().width 65 | t.equal(width, 100, 'still 100px after translateZ') 66 | 67 | // apply the 3D effect to parent 68 | css(document.body, { 69 | transformStyle: 'preserve-3d', 70 | perspective: 1000 71 | }) 72 | 73 | css(div, 'transform', 'rotateY(90deg) translateZ(10px)') 74 | width = div.getBoundingClientRect().width 75 | t.ok(width < 100, 'shrinks after 3d perspective') 76 | } 77 | 78 | css(div, 'left', '50%') 79 | 80 | document.body.removeChild(div) 81 | t.end() 82 | }) 83 | 84 | test('get / set exports', function (t) { 85 | var div = document.createElement('div') 86 | css.set(div, { 87 | marginTop: 10, 88 | width: 20 89 | }) 90 | 91 | document.body.appendChild(div) 92 | 93 | t.equal(css.get(div), div.style['foo-barcacha'], 'handles missing props') 94 | t.equal(css.get(div, undefined), div.style['foo-barcacha'], 'handles missing props') 95 | t.deepEqual(css.get(div, []), {}, 'handles missing props') 96 | 97 | t.equal(css.get(div, 'width'), '20px', 'single value') 98 | t.deepEqual( 99 | css.get(div, ['width', 'marginTop']), 100 | { width: '20px', marginTop: '10px' }, 101 | 'multiple values' 102 | ) 103 | 104 | document.body.removeChild(div) 105 | t.end() 106 | }) 107 | 108 | test('string argument', function (t) { 109 | var div = document.createElement('div') 110 | 111 | // in new versions of Node/Browser this might be 112 | // a template string :) 113 | css.set(div, [ 114 | 'margin-top: 10px;', 115 | 'position: absolute;' 116 | ].join('\n')) 117 | 118 | document.body.appendChild(div) 119 | 120 | var style = window.getComputedStyle(div) 121 | t.equal(style.marginTop, '10px') 122 | t.equal(style.position, 'absolute') 123 | 124 | // empty string clears style 125 | css(div, '') 126 | 127 | style = window.getComputedStyle(div, null) 128 | t.equal(style.marginTop, '0px', 'single property version works') 129 | t.equal(style.position, 'static', 'empty string clears style') 130 | 131 | document.body.removeChild(div) 132 | t.end() 133 | }) 134 | --------------------------------------------------------------------------------