批量修改style采取哪种方式好

原创 2008年02月29日 11:19:00
本文原发表于我在JavaEye的技术部落格

fins同志向我提了个问题。因这个问题其实可以展开讨论,所以提出来大家共同探讨。

fins 写道

在同类元素 例如 td 很多的情况下, "一次性改变元素的class对应的styleSheet"
和 "在循环里改变每一个元素style" 哪个更好

ext的代码不知道你看过没
在ext 1 里 改变表格列宽的方式 就是用的 改变那一列的 td对应的class里的 width
而ext 2里变成了 用循环 依次改变每一个 td的style.width
两种方法哪个好呢? 我一直喜欢第一种
不过实在不明白为什么 ext 2里换了方式


对于这一问题,我的意见是,就这个个例来说,两种方式都不好。因为table中一个column的width不应该在每个td上都做设置,而应该 设置在table的col或colgroup元素上。注意,col元素上有效的css prop是很少的,这关系到另一个问题,这里不做展开,不过,width恰好属于那少数几个有效的css prop之一。

当然,我对ext不熟,不知道在ext上是否能做这件事情。比方说如果ext产生的datagrid并不用col/colgroup,那你就干不 了这个事情。但是有个变通方式。对于table-layout:fixed的表格来说(注意:对于大表格来说,应尽可能使用fixed),其td宽度只取 决于第一行上的td的宽度。所以,可以把width设在第一行对应的td上。


本身,这个问题大体就是这样了。但是我想fins提出的这个问题,其实有更广泛的意义。那就是对于批量动态修改style,到底采取哪种方式好。

批量修改style的具体形式千变万化,但是万变不离其宗,归根到底就是两种:

A. 动态修改多个元素。
B. 动态修改stylesheet上的单个cssdeclaration。

差别就是,前者选择待修改元素的集合(符合条件的snapshot)的过程是由脚本完成的(比如用一个getElementsByTagName 选出一批元素,现在有了jQuery之类的,就更方便了,直接拿css selector来选择元素集合),并需要遍历所有元素一一进行修改;而后者选择待修改对象集合(这个集合是live的)的过程是由css selector自动完成的,然后style的变更也是自动分发到所有对象的。

这两种方式本身,在具体实践时还有一些可以注意的地方。

比如对于A方式来说,直接修改inline style实际上是把style混入了script中,味道通常不好,可考虑在stylesheet中创建若干个class来表示几组不同的style, 然后script中只要修改className就好。这适合那些样式切换的需求,因为样式切换往往隐含语义,所以确实也应该用class来显式的标记这些 语义。但是对于修改width这样的例子,就并不适合了。修改width,是一个纯粹UI上的操作,它并不带有语义价值(我指对应用的功能来说UI并不 significant——当然某些应用除外,例如UI编辑器)。

又如对于B方式来说,我们应该记得,selector很好很强大,因此不要老是给许多元素码上无数的class,完全可以用其他的 selector方式,例如父子关系的,不必每个li都li class=mylist,完全可以ul class=mylist,然后样式表写ul.mylist>li{...}。

如果这些地方都注意了,你会发现,真正需要批量修改的地方其实很少。大多数场合,只需要修改某个父级元素的className就可以了。

我们下面谈论的是除了这些情况之外,真正需要批量修改style的地方。

一种比较理论化的方案,是判断真正的需求,是要批量修改符合特定模式(实际是根据语义来匹配)的某些元素的样式(采用方式B),还是批量修改只是 临时碰巧符合某个结构的(实际上没有持久有效的语义,甚至可能是随机的,比如由用户临时指定的)一批元素的属性(采用方式A)。但是许多时候,这个界限并 不清楚,或者难以明确。就好象,在写样式表的时候,我是写
div#div1 {font-size:small}
div#div2 {font-size:small}
div#div3 {font-size:small}
还是抽象出一个div.class1,然后
div.class1 {font-size:small}

这个事情脱离环境,是无法判断的!因为有时候你知道这里要达到这个效果,但是你并没有花精力去判断,font-size为small这个事情是纯 粹偶然,还是这三个div真的具有某种一体的联系?况且,很多时候,追究这一点是不经济的(或者根本不可能,比如身为程序员的你无法找到真正懂需求的人, 或者做UI设计的人根本没有这个概念,无法回答你的问题)。

撇开对“需求的真正含义”的判断,我们假设,就个案来说已经存在确定的匹配模式(不管它有语义还是碰巧),也就是说不考虑你进行额外抽象的负担 (比如原来页面上并没有某个class,而你决定额外加入一个class,并给一些元素加上class——这不仅是一种额外负担,在匹配模式其实是碰巧的 情况下,长久来看其实可能是起了反作用),然后来关注纯技术层面的问题,那么:

通常来说,B方式看上去更好一些。fins同志也是这样认为的。因为它只修改了一处,而且这个修改是单点可控的,不可能受到外部因素的破坏。而方 式A则没有那么安全,因为样式与元素的匹配,只是一次性的,不存在始终有效的绑定。如果在完成批量改变style的操作之后,我们可能某个时候要再做一次 操作批量去除所附加的style。其潜台词是在这个期间,这些元素仍旧符合原先所设的条件。

如果元素由于某种外部因素的影响,不再符合原始的条件(例如一个元素被移动到了DOM树的其他地方),或者有新的符合条件的元素出现(例如插入了 一个新元素),既有的约定就被破坏了,结果自然是可能出现bug,而且几乎无法跟踪。即使你可以监听某些变化,成本也非常之高。使用css selector的jQuery之类的,尤其如此,因为css selector的模式匹配是如此强大,以至于完全无法track一个元素是否发生了一个变化就不再匹配既定模式了。

所以实际上,采用A方式,意味着DOM结构(包括所有影响你选择元素集合的因素)至少在一定范围内最好是静态的。幸好,这个需求在许多应用中还是 符合的,在受控的框架中通常也是可以保证的。还有一个例子是组件库,组件内部的DOM结构一般是确定不变的,然后可变的部分已经被封装为它的外部接口了。 对于组件来说,使用A方式还有一个副作用是,它能隔绝外部css对它内部元素的样式的影响,因为inline style优先级最高。当然组件可以正常工作的前提是,你的代码(或者你用的第三方代码)不会破坏它的封装,比如不破坏它内部的DOM结构。这一点其实存 在一点隐患,比如假设你搞来一个自动圆角的库,然后加诸于某个组件之上,因为这个圆角库会自动插入一堆b啦i啦的元素,结果你的脆弱的组件就完蛋了。

而B方式,因为它依赖的是声明性的selector,模式匹配是自动的,所以没有A方式的问题。DOM结构你随便变吧。但是B方式并不是没有自己 的问题。首先,stylesheet是全局的,对于一个rule修改所产生的影响也是全局的。在现有的CSS中缺乏将一部分style局域化的能力 (CSS 3有草案http://www.w3.org/TR/css-style-attr,html5 中有对style元素的局域化定义,但是得到浏览器普遍支持还需时日)。而组件需要局域化style。现在我们需要用id或者class来限定 stylesheet的scope。就ext的例子来说,它需要td上有一个class,而且每个table上的class都不能一样。维护局域化标识, 同时在全局stylesheet中维护局域化的style,这样的操作,其实是较为复杂的一件事情,目前似乎还没有一个库提供这方面的支持。

其次,A方式的stylesheet是静态的,而B方式的stylesheet是动态的。既然stylesheet是一种声明性的东西,那么通常 stylesheet本身就倾向于保持静态。这允许一些针对stylesheet的前处理和后处理。比如dean edwards的IE7,它会重新解析stylesheets。而我之前也写过预编译stylesheet来产生兼容ie的css的思路。 然而,如果stylesheet是动态可变的,对这些方案就存在很大的挑战。因为监听stylesheet的变化如果不是不可能,那至少是非常非常困难 的。而且无论是跟踪你修改的rules对应哪些实际的rules,还是整个重新编译样式表,成本可能都很巨大。要解决这个问题,需要投入很大的努力。 (dean edwards的IE7还有更严重的问题,因为它实际上内部是使用A方式的,所以是对AJAX不友好的,不过这与这里的讨论关系不大。)

以上,就是我对fins所提出的问题所做的一些考虑。结论其实是,首先看看是否你真的需要批量修改style。也许有90%的情况,你应该改为修 改单一元素上的一个className。又有90%的情况,你可能只需修改单一元素上的inline style。虽然jquery、mootools、prototype等框架先后提供了css selector的功能,未来浏览器甚至会提供原生的querySelector功能,但是没有必要滥用这个能力。

剩下1%的情形,那就要根据你的情况进行权衡。理想上,方式B更好一些,但是也存在一些现实问题。更多时候,我们见到的是大量方式A的写法,因为带有selector query功能的工具使得这样做更容易,而且在绝大多数时候,方式A也是可行的。

下一篇,我们将探讨由本文所引发的对于我们到底希望一种怎样的编程方式的思考。

Java - Spring支持的事务管理类型有哪些?你在项目中使用哪种方式?

Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理,因为这种方式和应用程序的关联较少,因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在...
  • chimomo
  • chimomo
  • 2017年11月09日 09:53
  • 148

java解析xml的几种方式哪种最好?

java解析xml的几种方式哪种最好? 举报|2012-06-12 09:31laifu901 | 分类:JAVA相关 | 浏览10546次 需要导入jar ...
  • revivec
  • revivec
  • 2014年04月11日 11:05
  • 351

ASP.NET MVC验证 - 使用哪种方式实现客户端服务端双重异步验证

本篇将通过一个案例来体验使用MVC的Ajax.BeginForm或jQuery来实现异步提交,并在客户端和服务端双双获得验证。希望能梳理、归纳出一个MVC异步验证的通用解决思路。本篇主要涉及: 1、...

转载:用哪種統計分析方式好?(卡方,Anova,T-test, or regression?)

  转载: http://newgenerationresearcher.blogspot.com/2008/08/anovat-test-or-regression.html   許多人(包括...
  • veryv
  • veryv
  • 2011年04月01日 03:26
  • 2011

Java接口RandomAccess(判断哪种迭代访问方式更快)

本文转载自:http://www.jb51.net/article/92127.htm 在jdk文档中对RandomAccess接口的定义如下:  public interface Ra...

生物识别大PK:你该选择哪种生物识别方式?

你是不是经常有一个疑问:生物识别技术这么多类别,我选择哪种才是最合适的哪?小T也经常遇到人问:人脸识别、虹膜识别和指纹识别,哪种技术更好?今天,小T在请教了天诚盛业的技术专家之后,特别推出此专题:生物...

集合框架_集合的特点和数据结构、如何选择使用哪种集合呢、各种集合常见功能和遍历方式总结

1:集合 Collection(单例集合) List(有序,可重复) ArrayList 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 Vector ...

JSP中使用哪种输出方式更好?

提问:JSP中使用哪种输出方式更好? 已经学习过使用 out.println() 和 输出的形式,那么在开发中使用哪种输出方式更好呢? 回答: 在JSP的开发中,实际上就是在HTML中加入了一些...

volatile、synchronized、lock有什么区别,以及在哪些场景下使用哪种方式?

[转]JVM锁机制volatile/synchronized/lock 2014-9-9阅读213 评论0 1.volatile实现原理 (1)聊聊并发(一)——深入分析Volati...
  • asdf717
  • asdf717
  • 2015年08月03日 10:33
  • 1768
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:批量修改style采取哪种方式好
举报原因:
原因补充:

(最多只允许输入30个字)