CSS Expression是自IE5开始提供的特性,虽然因安全性、性能问题臭名昭著,到IE8也终于
寿终正寝。回过头看,与XMLHttpRequest一样,CSS Expression的理念确实也有先驱之功,从CSS Expression也可看出由jQuery发扬光大的用CSS selector绑定行为的编程方式的雏形。不过雏形只能是雏形。由于设计上的缺陷,CSS Expression不堪大用,通常只局限为patch一些CSS特性,例如min-width/max-width。
不过CSS Expression在patch IE方面其实还可以发挥更大的功用。Dean Edwards首创了
一次性执行experssion的模式,巧妙的利用了IE的内建Selector机制,同时又避免了experssion被反复计算的性能问题。这种模式被许多patch所使用。例如Peter Nederlof的
hover/active/focus伪类补丁。
但是这个模式仍然有不足。Dean使用的是behavior属性。而单个CSS属性只能被用一次,即在一个元素上,最后根据cascade规则只会有一个behavior声明会胜出,因此你无法为一个元素同时启用多个特性(即调用多个行为)。
Peter Nederlof因为要绑定三个行为(分别对应于三个伪类),因此征用了3个不常用的css property(text-kashida, text-kashida-space, text-justify)。
显然,可以征用的css属性是很有限的(比方说你不能把background这样常用的属性给搞坏了,所以西方人也就是会欺负一下日本人专用的CSS属性)。如果能使用任意自定义的css属性的话,就好了。实际上IE对于不认识的property也是可以通过currentStyle返回其cascade之后的值的,而且expression对于自定义property也是有效的。
但是custom property存在一些问题:
1. 无法用runtimeStyle来override自定义属性
2. 即使删除包含expression的stylesheet,expression仍然有效
3. 在expression调用的方法里也不能调用removeExpression来强行删除表达式(会扔异常)
下面我讲一下我对这个问题的研究和解决方法。
根据我的研究,expression的机制与普通CSS属性不同,是设置在每个元素的style上的。
span {
behavior: expression(test1(this));
pie-test: expression(test2(this));
}
通常我们可以从currentStyle.propName来得到属性值,但是这样只是得到计算后的结果,且currentStyle上并无getExpresssion方法。但是调用span.style.getExpression('behavior')或('pie-test')会得到'test1(this)'和'test2(this)'
因此可以认为,对于这个rule,相当于对每个span都调用了一次 .style.setExpression('behavior', 'test1(this)') 以及 .style.setExpression('pie-test', 'test2(this)')
且通过对应的 .style.removeExpression 可以清除expression。
但是方法调用有一个限制,就是如果当前在expression所调用的函数(这里就是test2()函数)中,是不能调用 .style.set/removeExpression 方法的,会扔出异常。
设置runtimeStyle.behavior时,也会清除expression,相当于调用了style.removeExpression('behavior'),但是没有上面描述的限制。然而对于自定义属性无效。甚至任何时候runtimeStyle.setExpression('pie-test', null),也不会覆盖style上已经设定的expression。
以上。
显然一个解决方案是在expression之外调用 .style.removeExpression 。
示例代码:
function test2(e) {
setTimeout(function () {
e.style.removeExpression('pie-test')
}, 1)
...
}
这是有效的。问题是定时器只能保证最终自定义属性上的expression会被移除,但是不能确保expression只被执行一次(实测下来会执行多少次是不确定的,一次到数次都有可能)。
当然,我们可以给每个元素加上标志位来判断是否已经执行过,不过这还是挺麻烦的。
另一个解决方案是先把元素存起来,然后统一删除expression。
示例代码:
var spans = []
function test(e) {
spans.push(e)
...
}
<head>
...
<script defer>
for (var i = 0; i < spans.length; i++)
spans[i].style.removeExpression('pie-test')
spans = []
</script>
经过实测defer的script能确保正好执行一次。不过有趣的是如果打印spans.length会发现是在不断增加的,也就是并非所有元素上的test2执行完成后才执行defer的脚本,而是恰好一次test2,一次循环。这个也太巧合了,我对这个行为吃不太准。另外这个方式不能应对后续动态加入的匹配元素,丧失了使用css expression的最大好处之一。
最终的方案是利用CSS的规则。
实际上不是所有expression都会被反复执行。IE其实有基本的优化,比如对于expression(0)就只会计算一次,不会反复求值,因为很容易判断该值是不会变化的。
虽然如前所述不能在test2里直接调用 .style.setExpression ,但是可以通过改变match条件来override expression,如示例代码:
<style>
span {
pie-test: expression(test2(this));
}
span.pie-test {
pie-test: expression(true);
}
</style>
<script>
function test2(e) {
e.className += ' pie-test'
...
}
</script>
经过测试该方式可以完美达到只执行一次的目标!
该方法还有个好处,如果要再次执行test2,只需要从元素的class中删除pie-test即可。
后续的blog中我会展示一下使用这个模式来给IE打一些重要的特性补丁。
分享到:
相关推荐
iecss3.htc css3圆角支持文件
区别IE和非IE浏览器的兼容,css兼容
关于CSS中expression的使用简介,从此可以在Css中使用javascript了
IE6, IE7, IE8 CSS 兼容速查表
让IE6、IE7、IE8支持CSS3的圆角、阴影样式.
ie-css3(让ie678支持css3).rar
IE6.0、IE7.0 与FireFox CSS兼容的解决方法
让IE7 IE8 完全支持CSS2.1和部分CSS3标准。
但凡是前端工程师,都知道IE6,IE7,IE8不支持、或者不完全支持CSS3的属性。 CSS3 有很多很强大、绚丽的效果,比如,圆角,阴影,渐变透明,渐变背景,等等。 因为IE6时代,没有什么标准,而因为各种原因,IE6用户...
IE6、IE7、IE8对css和js支持方面差异的研究
CSS Expression在其它浏览器中不起作用,因此在跨浏览器的编码中单独针对IE设置时会比较有用。从IE5开始支持CSS Expression。我们看下面的代码:background-color: [removed] (new Date()()).getHours()%2 ? "#F00" ...
这是一个对IE8及以下做前端兼容的文件工具...--对于ie6到ie8做兼容,兼容h5,css3等新特性--> <!--[if (gte IE 6)&(lte IE 8)]> [removed][removed] [removed][removed] [removed][removed] <![endif]-->
介绍了10个很实但IE却不支持的CSS属性,列出这些属性并不是为了数落IE(数落也没用), 而是你了解了哪些CSS属性是IE不支持的,就更有针对性的去编写CSS和Hack了
以其可以在Css中定义表达式(公式)来达到建立元素间属性之间的联系等作用,从IE5开始得到支持,后因标准、性能、安全性等问题,微软从IE8 beta2标准模式开始,取消对css expression的支持。 使用 微软提供了4个css ...
搜集整理的CSS HACK,也即是在多种浏览器(主要是ie6 ie7 ie8 ff)下样式统一的解决办法。 内容还是较全的,前台开发中常见的兼容性问题都可以找到解决方法,值得研究。 包括各浏览器CSS hack,技巧。有实例。
兼容IE的最小最大高度CSS写法
CSS插件 ie.rarCSS插件 ieCSS插件 ieCSS插件 ieCSS插件 ie
CSS布局资料:IE5、IE6、IE7、IE8 的CSS HACK兼容列表
针对firefox ie6 ie7 ie8的css样式hack
引入PIE.htc使ie7,ie8 支持css3 border-radius圆角效果