布局转换——考验CSS功底的时刻到来

前几天的时候在QQ群里有一位朋友遇到了这样一个问题,是关于布局转换的,在说这个问题之前,我希望给大家介绍一下什么叫布局转换。

首先我们经常会遇到这种布局:

 

我想大家一看就会想到外面一个父级,里面6个子元素,一浮动,OK了,只有脑子稍稍不正常的人才会用position:absolute定位一个一个定。我和大家想的也一样,因此这样一个布局自然就出来了

1     <ul id="ul1" class="clear">
2         <li>1</li>
3         <li>2</li>
4         <li>3</li>
5         <li>4</li>
6         <li>5</li>
7         <li>6</li>
8     </ul>

对应的样式为

1     .clear:after{ content:""; clear:both; display: block;}
2     #ul1{ margin:100px auto; width:360px; border:1px solid #000;}
3     #ul1 li{ width:100px; height:100px; background:#f00; float: left;}

这个时候,一切看起来都很好,但是如果来了这样一个需求:需要给这6li加一个动画,希望移入每个li的时候从中间放大,类似这种效果

 

首先我们可以考虑一下做这个效果的思路:假如 li 的宽和高都变成了原来的两倍,但同时 left 和 top 也变化了,left 在原来的基础上减小的值是原来矩形宽度的一半,top 减小的值也是原来矩形高度的一半,这个时候,大家感觉到什么问题了吗?

我所有的 li 都是浮动的,哪里来的lefttop啊?这时,一定马上有人会有反应说:这还不简单,改成定位呗。

但是大家想一想,这种布局适合定位吗?如果定位的话,给每一个li设置lefttop值,那将会是一件极其恶心的事情,因此答案一定是否定的。

既然想用浮动,又想获取lefttop的值,这怎么办呢?这个时候就需要用布局转换,顾名思义,就是把浮动的布局转换为定位的布局,从而方便我们拿到它们的lefttop值,很容易的能够想到:

 1     window.onload=function(){
 2         var oUl=document.getElementById("ul1");
 3         var aLi=oUl.getElementsByTagName("li");
 4 
 5         for(var i=0;i<aLi.length;i++){
 6             aLi[i].style.position="absolute";
 7             aLi[i].style.left=aLi[i].offsetLeft+"px";
 8             aLi[i].style.top=aLi[i].offsetTop+"px";
 9         }
10     };

这时在浏览器当中预览,我们就可以看到:

 

如果效果和我的一样,那么恭喜你,这种做法是错误的,很明显,效果不是咱们想要的,那么问题到底出在哪儿呢?

我们在这里打一个断点(第7行):

 1     window.onload=function(){
 2         var oUl=document.getElementById("ul1");
 3         var aLi=oUl.getElementsByTagName("li");
 4 
 5         for(var i=0;i<aLi.length;i++){
 6             aLi[i].style.position="absolute";
 7             debugger;
 8             aLi[i].style.left=aLi[i].offsetLeft+"px";
 9             aLi[i].style.top=aLi[i].offsetTop+"px";
10         }
11     };

预览之后发现一个很诡异的界面:

 

1之后居然是3,2跑到哪里去了?

好的,写到这里,本文的精华部分即将开始:

我们在第7行打了断点之后,当循环进行第一遍时,i的值为0,也就是说此时我们为第1li设置了绝对定位,此时要注意,设置了绝对定位的元素是要脱离文档流的,何为脱离文档流?就是指元素会漂起来,因此,1就会漂在2 3 4 5 6的上方,此时1将会把2遮住,所以我们就看不见2了,此时你再获取1offsetLeftoffsetTop,必然是10(如果给ul加了相对定位的话),那么为什么他们最后都会挤到一块儿呢?不要着急,按下F8,放开现在的断点,进入下次循环。

当进入第二次断点之后,如下图所示:

 

这时,根据我们上次的分析,相信大家对这次的结果心里必然有底,因为给第2li加了absolute,所以第二个li飘起来,在3 4 5 6的上方,将3遮住了,所以我们就看不见3了,但是有人一定着急了,你那2形状怎么那么奇怪啊,好像错出来一部分一样,事实上不是这样的,错出来的那部分实际上不是别人,就是1,不要忘了,我们在第一次断点之后给1设置了lefttop值,因此1就被定位定在那里了,但是可能大家又会想,设置了lefttop值又怎样呢,那不应该是相对于父级,也就是图上黑色的边框lefttop10px吗?

但是大家不要忘了,我们的li可是有10pxmargin啊,如果说到这里还不明吧的话,可以看看下面这张图:

 

我们的2和3实际上是在粉色的框那个位置,所以看起来就错位了,这一块可能稍微有些费解,请大家认真揣摩一下。

这样一来,每次加完定位之后剩下的li都会往前面挤,然后当前li(正在脱离文档流的li)再被定到left:10px;top:10px的位置,就得到了最开始那幅6个li挤到了一起的图。

说了这么多了,那到底该如何解决这个问题呢?这时,我那位QQ里面的朋友就想出这样一种办法:

    window.onload=function(){
        var oUl=document.getElementById("ul1");
        var aLi=oUl.getElementsByTagName("li");

        for(var i=0;i<aLi.length;i++){
            aLi[i].style.left=aLi[i].offsetLeft+"px";
            aLi[i].style.top=aLi[i].offsetTop+"px";
            aLi[i].style.position="absolute";
        }
    };

从上面的代码中我们可以看出,他把position:absolute;放在了设置left和top后面,我们分析一下这样做可以吗?

很显然还是不可以,为什么呢?我们还是从for循环的第一次开始分析,第1个li我们为它设置了left和top值,很好,没什么问题,但是,马上又给第1个li加了绝对定位,那第1个li马上脱离文档流,2 3 4 5 6照样还会挤过去,还会出现上面说的问题,因此,这样做是不对的。

分析到这里,相信大家已经有感觉了,都是定位惹的祸,也就是说这句话

aLi[i].style.position="absolute";

放置的位置,成了关键。

那么这句到底应该放在哪里呢?

我们应该有感觉了,只要在获取到offsetLeft和offsetTop并赋值给left和top之后设置,这样就很好了,所以出现了下面的解决方案:

        for(var i=0;i<aLi.length;i++){
            aLi[i].style.left=aLi[i].offsetLeft+"px";
            aLi[i].style.top=aLi[i].offsetTop+"px";
        }

        for(var i=0;i<aLi.length;i++){
            aLi[i].style.position="absolute";
        }

从代码中,我们可以看出来,我们将设置left和top与设置定位分开来放到两个不同的块级作用域当中了,这样就将它们彻底分开了。

到现在为止,不要高兴的太早,之前我们遗留下来一个问题就是在第二次断点的时候我们发现1和原来的位置相比错位了,那这个错位怎么解决呢?

仔细想想这个错位是怎么来的呢?

是这样的,left和top定位的时候定的是我们整体盒模型(在这里就是指的margin),而不单单是从元素width height开始的那部分,但是offsetLeft和offsetTop在计算的时候,是将margin算上之后赋给left和top的,所以margin就相当于多算了一次,因此,我们还需要把多算的这次margin去掉,来让li回到原来的位置。所以,最终的代码应该是这样:

        for(var i=0;i<aLi.length;i++){
            aLi[i].style.left=aLi[i].offsetLeft+"px";
            aLi[i].style.top=aLi[i].offsetTop+"px";
        }

        for(var i=0;i<aLi.length;i++){
            aLi[i].style.position="absolute";
            aLi[i].style.margin=0;
        }

 

posted on 2015-06-25 23:31  特拉法尔加  阅读(267)  评论(4编辑  收藏  举报