web 开发中,布局问题是最难解决的一类。大量的css互相影响,元素和元素层层嵌套,相邻的元素也会互相影响布局,浏览器实现各异,又是个黑箱,没法跟踪它到底怎么布局的。这种问题,往往只能蒙。我最近就遇上一个这样的问题。下面是简化的代码
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" >
<html>
<head>
<style type="text/css">
.longdaui .longdaLeft{float:left;}
.longdaBtn{border-width:1px;border-style:solid;text-align:center !important;}
.longdaSection{clear:both;zoom:1;position:relative}
.longdaContent
{
padding:40px 20px 20px 20px;overflow:hidden;
}
.longdaBtn{border-color:#c0c7cd;}
.longdaBtn:hover{border-color:#8e8e8e;}
</style>
</head>
<body class="longdaui" dir="ltr">
<div id="longdaFrame" class="longdaFrame">
<div id="longdaContent" class="longdaContent" style="margin: 0px 11px;">
<div id="longdaBtnContainerId" class="longdaBtnContainer">
<span class="longdaBtn longdaBtnAction longdaLeft"><a href="#">Show/Hide
Forms</a></span>
</div>
<div class="longdaSection">
text
</div>
<!--end section-->
<div id="longdaBtnContainerId2" class="longdaBtnContainer">
<span class="longdaBtn longdaBtnAction longdaLeft"><a href="javascript:abPreSubmit('h_Hide')">Show/Hide
Forms</a></span>
</div>
</div>
<!--end content-->
</div>
<!--end frame-->
<!-- personTag (inline) -->
</body>
</html>
这个bug只会在IE7或者IE8兼容模式下出现。
在上下两个 show/hide form 链接上移动鼠标,text文本上面就会上下跳动。 我并不知道是到底是为啥会这样。但是,里面的zoom属性不是属于css2.1标准,而是ie特有的。所以我尝试去掉zoom:1,这样就不会上下跳了。这样就可以修复问题了。
不过,我更想说的,就是怎么找出来。上面这个html我我从一个近千行的html,包括十来个JavaScript文件,以及总计2000行的5个css文件里面提炼出来的能重现问题的最简单的html和css的组合了。
首先,我把用js动态生成的html从浏览器里面提取出innerhtml,做成静态html。然后用类似折半查找的方法找到去掉哪个css规则会让bug消失,然后留下这个规则,继续查找。直到一条css规则都不能去掉为止。然后,我去掉了所有的js,但是发现问题又没有了,于是我只好折半删除js,发现原来是有个js语句给body加了一个class,于是修改了静态html,然后去掉所有的js。然后,把无关的html元素尽量的去掉。终于得到了上面这个简单的能重现bug的html和css组合。
整个过程用了我差不多4天的时间。这种费时费力的方法,是实在没有办法的时候才不得不用的,那是因为直接调试和分析css已经得不到任何结果了。这个就是笨办法,但是笨办法就是能让缺陷无所遁形。在《代码大全》里面提到 这种方法,称为 暴力调试。
我们往往会倾向于使用”快速肮脏调试法“,宁愿用一种可能在五分钟内发现缺陷的高风险方法,也不愿意为某种保证能找到缺陷的方法花上半个小时。这样的风险就是如果这个简便的方法不能凑效,我们就会麻木,而几个小时甚至几天,几个月就毫无建树的流逝了。所以,我常常提醒自己,是不是已经在用某种”简便“的方法找缺陷上花了太多时间。
代码大全上面列出 暴力调试法包括下面这些方法:
- 对崩溃代码的时间和编码进行彻底检查
- 抛弃有问题的代码,从头开始设计和编程
- 抛弃整个程序,从头开始设计和编程
- 编译代码是生成全部的调试信息
- 在最为苛刻的警告级别中编译代码,不放过任何一个细微的编译器警告
- 全面执行单元测试,并将新的代码蛤蜊起来单独测试
- 开发自动化测试工具,通宵达旦的对代码进行测试
- 在调试器中手动地遍历一个大的循环,知道发现错误条件
- 在代码中基阿鲁打印,显示和其他日志记录语句
- 用另外一个不同的编译器来编译代码
- 在另一个不同的环境里编译和运行程序
- 在代码运行不正确的时候,使用能够产生警告信息的特殊库或者执行环境来链接和运行代码
- 复制最终用户的完整系统配置信息
- 将新的代码分小段进行集成,对每段集成的代码段进行完整的测试