鸟食轩

 Microsoft .NET[C#] MVP 2003
随笔 - 424, 文章 - 233, 评论 - 5418, 引用 - 344
数据加载中……

网页中单一表格横纵表头锁定的细节

    五·一之前捣鼓了一个可以锁定横纵表头的HTML表格,不过那天急着回家就只给了一个demo示例。这不长假过后又挺忙活,一拖就给拖到周末了。这个东西虽然不是什么新奇的玩意,不过里面的细节还是有不少,所以有知其然并希望知其所以然的博友,欢迎来一起讨论讨论。

    实现这个锁定表头的效果其实到有很多的方法,不过使用单一表格来实现,需要借助IE的css支持的expression表达式关键字。另外就是对浏览器布局原理的理解,如果不熟悉可以参考我原来写的两篇文章。所以要实现这个效果,我们需要也只需要做两件事情,一是使用css提供的expression功能;二是使用css提供的position修改布局方式的能力。

    首先我们来看上部表头的锁定,我们知道对于container元素,如果要限定内部内容的大小,并出现滚动条。因该是用如下css来定义可以作为容器的元素,这里我使用的是div来做为表格的容器,代码如下:
<div onselectstart="return false" style="border-color:Blue;border-width:2px;border-style:solid;height:100%;width:700px;overflow:auto;cursor:default;">
<!-- table -->
</div>
// 重要的就是那个overflow:auto。顺便说一下,弄出滚动条这个css设置虽然非常的easy,不过不知道IE这么搞的,这个容器只能在height上设置百分比,而width的大小必须是固定的值,否这width方向上就不能出现滚动条

    其实如果就这样我们把table放入div中,就已经是一个可以滚动的表格了,只是所有的单元格也都在滚动而已。所以我们需要修改表格中不希望滚动的单元格的布局方式,tr和td的默认布局方式都是static。如果要让tr相对于div不动,那么在div中的滚动条滚动到scrollTop为100时,相对不动的tr应该停留在相对于div左上角的(0, 100)的位置上。这个相对性我们可以用<tr style="position:relative">获得,那么top为100怎么弄呢?显然这就是expression的事情了,我们需要把行上的css设置为<tr style="top:expression(表格容器.scrollTop)">。这个表格容器是什么呢?就是div,使用dom的层次来查找因该是:
this.parentElement.parentElement.parentElement.scrollTop;
不过可以使用offsetParent来优化,减少引用深度,简化为:
this.offsetParent.scrollTop;

    那么被锁定的行的完整css就因该是:
<tr style="position: relative; top: expression(this.offsetParent.scrollTop);"></tr>

    这么简单啊?就这么简单,不过问题当然还是有啦。这样的锁定效果有两个主要的问题:一是表格背景由于默认是透明的,所以如果真的滚动起来,字是不动了,不过都是镂空的重叠效果。解决办法很简单,就是给tr设置上背景颜色就可以啦。第二个问题,比较郁闷,搞了很久才在别人的帮助下搞定。当tr的position为relative后,这个tr里面的td的border属性就貌似失效了,就是不管设置什么颜色,死活都显示不出来:
    TdBorderEmpty.png
    后来在putee同学的帮助下,发现原来需要把tr中的每个td的position属性也都设置成relative,border才能显示出来!

    搞定了上面的表头,我们再来搞左边的表头。左边原理和上面一样,只是被expression控制的元素是td而不是tr啦。expression中引用div的代码就以该是:
this.parentElement.parentElement.parentElement.parentElement.scrollTop;
比锁定tr多了一个层次的引用,这是使用offsetParent来优化和tr还不同,tr的offsetParent就是容器div;而td的offsetParent还是td,晕!所以要用this.parentElement来取offsetParent,不过这时取到的又是table,再次晕。所以这个优化只能减少一个parent层次的引用,优化为:
this.parentElement.offsetParent.parentElement.scrollTop;

    那么被锁定的列的完整css就因该是:
<td style="position: relative; left: expression(this.parentElement.offsetParent.parentElement.scrollTop);"></tr>

    搞定了横纵两个方向表头的锁定,结果去发现表头的单元格的边框不太对劲,怎么都是双倍的边线呢?我已经设置了table-collapse为collapse,而且单元格的border设置为solid 1px blue。结果发现这又是position设成了relative后带来的问题,解决办法是只给每个表头单元格设置border的两个边,比如top和left或者right和bottom。下图是表头单元格两种不同border样式设置的效果:     
    
    最后就是Grid左上角那个相对于容器div静止的部分,当然这个部分的tr和td除了需要同时包含上面说到的锁定行表头和列表头的css expression外,还有一个问题就是这个部分的单元格的zIndex必须是表格两个表头中最大的,才能保证不管真么滚动表头,始终能显示这个部分。另外又是由于IE的问题,我们不能把左上角这个部分通过colspan和rowspan合并成一个td,如果合并那么除了第一行外的其行,将在表头滚动时被覆盖掉。所以这个部分的每一行仍然是一个单独的td,只是在水平方向上作了合并优化。

    btw: 由于firefox连td的position为relative都不支持,所以没有任何必要讨论对firefox的支持

posted on 2006-05-13 00:44 birdshome 阅读(13606) 评论(12)  编辑 收藏 所属分类: Jscript&Dhtml开发

评论

#1楼    回复  引用  查看    

好东西
前一段时间也在研究这个来着
不过后来因为接手其它项目就先搁置了
如果能应用在gridview里就更好了
2006-05-13 09:25 | aspnetx      

#2楼    回复  引用  查看    

纵横锁定看起来很美,但实际效果去不行,当锁定区域数据较多时,客户端cpu会达到100%,好像还没有好的解决办法
2006-05-13 09:50 | 铱星      

#3楼    回复  引用    

单一表格实现可能还是不太好,用多表格的div实现滚动比较平滑.
2006-05-13 17:38 | 锦瑟 [未注册用户]

#4楼    回复  引用    

铱星:
  你说得应该是没有错,我现在说另外一个现象。请先打开任务管理器,看cpu的使用率,此时,就用这个当前页面,试一下不断地快速或慢速地拖动右边的滚动条,应该会注意到,速度越快,cpu的使用率就越高。任何网页都是这样,或者说任何的程序的滚动条,都是这样的。
2006-05-21 16:09 | baijian_8d [未注册用户]

#5楼    回复  引用    

可是 2003不出现的话 是什么问题?
2006-05-22 11:58 | 3daysking [未注册用户]

#6楼    回复  引用    

还是强烈推荐使用两个表格解决,一个表格的话,expression会很慢,我的机器不好,直接100%了 :(
不过两个表格的话不知道还能不能排序。。。
2006-05-27 15:14 | BaSaRa [未注册用户]

#7楼    回复  引用    

这里有一个相似的 不过不包含纵列的
http://www.microsoft.com/china/MSDN/library/WebServices/WebServices/nacwebteam11042002.mspx?mfr=true
获取滚动 那段

这个不会出现拖动时占用cpu高的情况
2006-06-13 18:23 | netwjx [未注册用户]

#8楼 [楼主]   回复  引用  查看    

@netwjx
    这种使用两个table的方法实现表头锁定最大的问题是:除了最后一个Column外,其他Columns的宽度会被限定死,这是同步表头宽度不能避免的,否则需要实时同步两个table所有Columns的宽度,代价也会很大。再加上表格复制的开销,这也大概是针对较小数据量的一个解决方案。
ScrollTable.png
2006-06-13 22:49 | birdshome      

#9楼    回复  引用  查看    

好 非常好 不是一般的好。。。。
2007-03-15 16:30 | 邱韵      

#10楼    回复  引用    

td的offsetParent还是td
这句话是否有误?
td的offsetParent还是tr

#11楼    回复  引用    

看了你这篇文章之后,我也想做一个来用。但是按照你的方法却老是出问题。不知楼主能否抽出宝贵时间给指点一下。
我的开发环境是.net 2005

我把表格放在DIV里。
可能实现自动滚动了。
然后我按楼主所说的给第一列单元格设置
position: relative;
后。那第一列的单元格都就到DIV的上边了。

2007-07-06 17:24 | janl [未注册用户]

#12楼    回复  引用    

我跟楼上的问题一样,怎么试都没法让它下来

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-05-13 14:13 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: