MyMsn动态Resize页框架的布局详解

    微软的MyMsn提供了一个自由resize和drag&drop的个人Portal,虽然这样的东西不难见到,像SharePoint、DotNetNuke等都有很好的这种操作实现。而且我们也知道在Web页中托拽一个html元素是很容易的,不过要是整个页面都是绝对定位,其实还是非常难于管理的,所以我们看看MyMsn是怎么来做的呢?

    大家应该都玩过MyMsn了吧?http://my.msn.com/,需要注册一个passpost账号。

    MyMsn的整体结构是按照channel来分的一个个Tab,这个是在服务器端处理并生成,不是我们要研究的重点。它的外观如下:
   

    它的每个独立的Tab的布局结构,是我们要研究的重点。一个TabPage在MyMsn中就是一个<table>,然后被分为了n个column,显示效果如截图下:
   
    并且MyMsn支持用户任意的Resize Column的宽度,效果如下图:
   

    嗯,我们可以看出,MyMsn的页面布局就是最外的<table>里放了两个<tr>,第一行的<tr>作为Column Header,第二行的<tr>放置具体的内容。第二行<tr>中的每个<td>就是一个放置内容的container,图中类似Windows窗口的每个block也是<table>块,他们顺序的放置于其container(<td>)中,一切的布局皆由默认布局完成。什么意思呢? 就是说此时的布局效果没有使用任何的position、top和left等style来强制约束。

    这个好像没有什么特别,只要会做网页就能做出这样的布局嘛。对,不过这样的布局支持MyMsn的特性还是有问题的,MyMsn提供的一个很sexy的功能就用户可以自由的在界面上做定制!包括reszie和drag&drop。问题是什么呢?我们知道在IE解析HTML并绘制图形的时候,它是从HTML树的每个leaf节点开始绘制的。这样的绘图顺序才能保证把所有的元素外观都表现出来而不被隐藏,只要做过Web页肯定都遇到过表格被撑开这种情况是吧?这就是因为IE按先根遍历DHTML树来输出所产生的结果,当然这不是IE特有的规则,所有的browser都是这样的策略来输出图形的。

    这样一来,就是说我们可以任意的变宽container的width,但是要想减小container的width,就要看其内部元素的布局策略了。如果container内部元素可以任意折行并且其width为百分比单位,减小container的width可以强制内部元素同时变窄(同时高度会增加哈)。相反的情况下,要想随意的改变container的宽度来约束其内容,就是做不到的了。

    MyMsn既然是使用的表格来布局,那么它是怎么处理resize的时候,每个room内的内容的宽度的呢?看一下上图(第三张),当我们在第一个column和第二个column之间resize第一个column的宽度时,居然可以出现room跑到container外去了的情况,看room "Add Content"!原来MyMsn是这样做的,当用户在resizer上MouseDown的时候,使用JavaScript把宽度受影响的container内的room的定位方式改为"absolute",然后计算每个room在页面中的位置,绝对定位每个room。拖动resizer时,在onmousemove更新受影响的container内的每个room的宽度。Room "Welcome" & "Add Content"表现出来的不同外观,就是我前面讲的IE输出图形策略造成的,就是说即使我们把"Add Content"这个Room的宽度设置为1,它还会是那么宽的。

    同时为了保证表格嵌套的这种布局策略,当用户停止resize动作,在resizer上触发onmosueup事件后,使用脚本把container内的room的position属性又全部设置回"static",让它们服从IE的默认布局流。这样的好处就是,始终能保存页面清晰的布局关系,而且依赖于IE默认布局,不会有产生任何潜在的混乱,也便与支持动态添加Column或Room。同时由于MyMsn还需要把用户自定义的设置信息传回到服务器保存,这也需要它能有一个清晰的布局结构,对于drag&drop,更需要这样的结构,如果是全部绝对定位,position属性为"absolute"的元素满页面飞,要记录其状态是非常麻烦的。MyMsn全部脚本代码2700于行,在这种布局结构下,完备的支持resize和drag&drop,只需要600行左右的代码(那剩的2000多行代码在嘛,我也不知道微软在搞什么飞机)。

    最后再附一个MyMsn页面元素布局图示(比实际稍微有些简化,但完全支持其现有功能):
  
    // 整个黑色框框、每一个蓝色绿色框框都是一个<table>元素。

posted on 2005-04-04 13:52  birdshome  阅读(...)  评论(...编辑  收藏

导航