zoukankan      html  css  js  c++  java
  • 布局转换——考验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;
            }
  • 相关阅读:
    AddressFamily 枚举指定 Socket 类的实例可以使用的寻址方案
    在.NET开发中灵活使用TreeView控件
    TreeView初始化,返回节点值的方法(转)收藏
    怎样彻底删除系统服务项(转载)
    SQL Server 返回插入记录的自增编号(转)
    Socut.Data.dll 与AspNetPager.dll使用说明及心得体会 (转载)
    ActionScript最新3D引擎项目(转载)
    XP自动搜索功能修复
    Postgresql 重新安装,数据不丢失
    work with postgis & geoserver
  • 原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/4601238.html
Copyright © 2011-2022 走看看