Skip to content

Commit b8b31a0

Browse files
committed
Imrpove v-style
- auto detect and cache prefixes - allow both dash-case and camel-case
1 parent 6bc60e9 commit b8b31a0

File tree

2 files changed

+91
-54
lines changed

2 files changed

+91
-54
lines changed

src/directives/style.js

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,79 @@
1+
var _ = require('../util')
12
var prefixes = ['-webkit-', '-moz-', '-ms-']
3+
var camelPrefixes = ['Webkit', 'Moz', 'ms']
24
var importantRE = /!important;?$/
5+
var camelRE = /([a-z])([A-Z])/g
6+
var testEl = null
7+
var propCache = {}
38

49
module.exports = {
510

611
deep: true,
712

8-
bind: function () {
9-
var prop = this.arg
10-
if (!prop) return
11-
this.prop = prop
12-
},
13-
1413
update: function (value) {
15-
if (this.prop) {
16-
this.setCssProperty(this.prop, value)
14+
if (this.arg) {
15+
this.setProp(this.arg, value)
1716
} else {
1817
if (typeof value === 'object') {
18+
if (!this.cache) this.cache = {}
1919
for (var prop in value) {
20-
this.setCssProperty(prop, value[prop])
20+
this.setProp(prop, value[prop])
21+
/* jshint eqeqeq: false */
22+
if (value[prop] != this.cache[prop]) {
23+
this.cache[prop] = value[prop]
24+
this.setProp(prop, value[prop])
25+
}
2126
}
2227
} else {
2328
this.el.style.cssText = value
2429
}
2530
}
2631
},
2732

28-
setCssProperty: function (prop, value) {
29-
var prefixed = false
30-
if (prop.charAt(0) === '$') {
31-
// properties that start with $ will be auto-prefixed
32-
prop = prop.slice(1)
33-
prefixed = true
34-
}
33+
setProp: function (prop, value) {
34+
prop = normalize(prop)
3535
// cast possible numbers/booleans into strings
36-
if (value != null) {
37-
value += ''
38-
}
39-
var isImportant = importantRE.test(value)
40-
? 'important'
41-
: ''
42-
if (isImportant) {
43-
value = value.replace(importantRE, '').trim()
44-
}
45-
this.el.style.setProperty(prop, value, isImportant)
46-
if (prefixed) {
47-
var i = prefixes.length
48-
while (i--) {
49-
this.el.style.setProperty(
50-
prefixes[i] + prop,
51-
value,
52-
isImportant
53-
)
36+
if (value != null) value += ''
37+
if (value) {
38+
var isImportant = importantRE.test(value)
39+
? 'important'
40+
: ''
41+
if (isImportant) {
42+
value = value.replace(importantRE, '').trim()
5443
}
44+
this.el.style.setProperty(prop, value, isImportant)
45+
} else {
46+
this.el.style.removeProperty(prop)
5547
}
5648
}
5749

50+
}
51+
52+
function normalize (prop) {
53+
if (propCache[prop]) {
54+
return propCache[prop]
55+
}
56+
var res = prefix(prop)
57+
propCache[prop] = propCache[res] = res
58+
return res
59+
}
60+
61+
function prefix (prop) {
62+
prop = prop.replace(camelRE, '$1-$2').toLowerCase()
63+
var camel = _.camelize(prop)
64+
var upper = camel.charAt(0).toUpperCase() + camel.slice(1)
65+
if (!testEl) {
66+
testEl = document.createElement('div')
67+
}
68+
if (camel in testEl.style) {
69+
return prop
70+
}
71+
var i = prefixes.length
72+
var prefixed
73+
while (i--) {
74+
prefixed = camelPrefixes[i] + upper
75+
if (prefixed in testEl.style) {
76+
return prefixes[i] + prop
77+
}
78+
}
5879
}

test/unit/specs/directives/style_spec.js

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ var _ = require('../../../../src/util')
22
var def = require('../../../../src/directives/style')
33
var Vue = require('../../../../src/vue')
44

5+
function checkPrefixedProp (prop) {
6+
var el = document.createElement('div')
7+
var upper = prop.charAt(0).toUpperCase() + prop.slice(1)
8+
if (!(prop in el.style)) {
9+
var prefixes = ['Webkit', 'Moz', 'ms']
10+
var i = prefixes.length
11+
while (i--) {
12+
if ((prefixes[i] + upper) in el.style) {
13+
prop = prefixes[i] + upper
14+
}
15+
}
16+
}
17+
return prop
18+
}
19+
520
if (_.inBrowser) {
621
describe('v-style', function () {
722

@@ -14,52 +29,53 @@ if (_.inBrowser) {
1429

1530
it('normal with arg', function () {
1631
dir.arg = 'color'
17-
dir.bind()
1832
dir.update('red')
1933
expect(el.style.color).toBe('red')
2034
})
2135

2236
it('normal no arg', function () {
23-
dir.bind()
2437
dir.update('color:red;')
2538
expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
2639
})
2740

2841
it('!important', function () {
2942
dir.arg = 'color'
30-
dir.bind()
3143
dir.update('red !important;')
3244
expect(el.style.getPropertyPriority('color')).toBe('important')
3345
})
3446

47+
it('camel case', function () {
48+
dir.arg = 'marginLeft'
49+
dir.update('30px')
50+
expect(el.style.marginLeft).toBe('30px')
51+
})
52+
53+
it('remove on falsy value', function () {
54+
el.style.color = 'red'
55+
dir.arg = 'color'
56+
dir.update(null)
57+
expect(el.style.color).toBe('')
58+
})
59+
3560
it('auto prefixing', function () {
36-
var spy = el.style.setProperty = jasmine.createSpy()
37-
dir.arg = '$transform'
38-
dir.bind()
61+
var prop = checkPrefixedProp('transform')
62+
dir.arg = 'transform'
3963
var val = 'scale(0.5)'
4064
dir.update(val)
41-
expect(spy).toHaveBeenCalledWith('transform', val, '')
42-
expect(spy).toHaveBeenCalledWith('-ms-transform', val, '')
43-
expect(spy).toHaveBeenCalledWith('-moz-transform', val, '')
44-
expect(spy).toHaveBeenCalledWith('-webkit-transform', val, '')
65+
expect(el.style[prop]).toBe(val)
4566
})
4667

4768
it('update with object', function () {
48-
dir.bind()
49-
dir.update({color: 'red', 'margin-right': '30px'})
69+
dir.update({color: 'red', marginRight: '30px'})
5070
expect(el.style.getPropertyValue('color')).toBe('red')
5171
expect(el.style.getPropertyValue('margin-right')).toBe('30px')
5272
})
5373

5474
it('update with object and auto prefix', function () {
55-
var spy = el.style.setProperty = jasmine.createSpy()
56-
dir.bind()
57-
var scale = 'scale(0.5)';
58-
dir.update({'$transform': scale})
59-
expect(spy).toHaveBeenCalledWith('transform', scale, '')
60-
expect(spy).toHaveBeenCalledWith('-ms-transform', scale, '')
61-
expect(spy).toHaveBeenCalledWith('-moz-transform', scale, '')
62-
expect(spy).toHaveBeenCalledWith('-webkit-transform', scale, '')
75+
var prop = checkPrefixedProp('transform')
76+
var val = 'scale(0.5)';
77+
dir.update({transform: val})
78+
expect(el.style[prop]).toBe(val)
6379
})
6480

6581
it('updates object deep', function (done) {

0 commit comments

Comments
 (0)