批量修改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也是可行的。

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

怎样选择基金分红方式

怎样选择基金分红方式http://www.sina.com.cn 2007年01月02日 08:30 每日新报  近期,许多绩优基金公司开始大面积、高比例分红,很多基金投资者拿到大把的现金红利后喜不自...
  • thunder4393
  • thunder4393
  • 2007年09月07日 16:49
  • 790

5种PHP创建数组的方式,你都了解哪些?

看这篇文章之前相信大家都已经看过PHP中文手册关于数组这一节的讲解了,怎么样呢,看懂了多少?至少我第一次阅读文档时是一头雾水,也许是因为在翻译的不够通俗易懂吧^_^!!这里UncleToo根据自己的经...
  • zxjiayou1314
  • zxjiayou1314
  • 2015年07月08日 21:49
  • 420

一个比较好的代码注释方法

.h文件中,每个函数前一行都做说明public: //用pGeo构造一棵八叉树 void BuildOCTree(CGeometry * pGeo); .cpp文件中,如下,函数前说明内容分别是功能,...
  • a117653909
  • a117653909
  • 2010年07月14日 20:59
  • 717

车用安全座椅有三种安装方式,到底哪个好

安全性方面,LATCH接口方式有三个连接点,在连接锚点之后可形成稳固的三角形,不过这种方式的固定稳定性比ISOFIX稍差,行驶时会稍有晃动,在美式车型上均提供这种接口。另外,ISOFIX接口方式因为是...
  • z656931038
  • z656931038
  • 2017年08月01日 15:10
  • 36

java List深拷贝的两种方式

大家都知道java里面分为浅拷贝和深拷贝。举个简单的例子,区分一下浅拷贝和深拷贝的区别 public class Address{ private String address; ...
  • u010648159
  • u010648159
  • 2018年01月23日 20:35
  • 27

XML的四种生成方式

XML文件的生成1.DOM方式生成xmlpublic void createDOMXml() throws Exception{ //创建一个DocumentBuilderFactor...
  • AndCo
  • AndCo
  • 2017年09月03日 23:09
  • 64

搭建Git服务器的最简单方式

环境 本地环境Ubuntu,服务器环境Ubuntu创建服务器端 以root账户登录服务器,注意这个地方必须使用root账户 ssh root@服务器地址创建git用户,并且为git用户在/hom...
  • TE28093163
  • TE28093163
  • 2017年09月11日 11:23
  • 180

Android实现微信底部导航条

简单微信导航条
  • zhangzhiwen0612
  • zhangzhiwen0612
  • 2017年12月24日 18:09
  • 62

两个路由器桥接方法

家里新买一个4口的无线TP_LINK路由器型号是TL-WR 740N,原来已有一个4口的TP_LINK有线路由器型号是TL-R 402+,本来想一个无线路由就可以搞定家里两台台式,3台笔记本来,可是好...
  • Shenkxiao
  • Shenkxiao
  • 2011年11月12日 13:32
  • 58551

springboot使用elasticsearch本地集群踩得坑(Windows版)

org.springframework.boot spring-boot-starter-parent 1.5.9.RELEASE 我的springboot版本如上...
  • wangh92
  • wangh92
  • 2018年01月30日 11:46
  • 52
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:批量修改style采取哪种方式好
举报原因:
原因补充:

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