Heim >Web-Frontend >HTML-Tutorial >从三栏自适应宽度布局到css布局的讨论_html/css_WEB-ITnose

从三栏自适应宽度布局到css布局的讨论_html/css_WEB-ITnose

WBOY
WBOYOriginal
2016-06-24 11:32:441005Durchsuche

      如何实现一个三栏自适应布局,左右各100px,中间随着浏览器宽度自适应?

      第一个想到的是使用table布局,设置table的宽度为100%,三个td,第1个和第3个固定宽度为100px,那么中间那个就会自适应了,下面是一个实时的demo:

left  middle  right 

      但是table布局是不推荐的,table布局是css流行之前使用的布局,有很多缺点:当table加载完之前,整个table的都是空白的,table将数据和排版参和在一起,使得页面混乱,并且table布局修改排版十分麻烦和困难。

      如果不用table布局,那么第二个想到的办法是采用float,让左边的div float left,右边的div float right,如下边所示:

Action 1 先让左右两个div浮动

float left 

float right 

      中间还有一个div,如果将中间的div排在第二:

<div style="float:left;">left</div><div>middle</div><div style="float:right;">right</div>

     那么效果是这样的:

Action 2 右边的div浮动到了第二行

left 

middle 

right 

      因为div默认的display为block,如果不设置width的话,块级元素会尽可能多地占用水平空间。如果设置了width: 200px,效果是这样的:

Action 3 右边的div仍然浮动到了第二行

float left 

middle 

right 

      第三个div仍然会换行,因为float right会排到当前行尽可能右边的位置,即它的容器盒的边缘或者挨着的上一个float的元素,如果当前行没有空间的话,会不断地往下移,直到有足够的空间。由于middle是一个块盒,即使设置了width,当前行的空间也会被占用,所以right只能到下一行才有空间。

      同时注意到middle虽然设置了200px,但是看起来和left一样是100px宽了。这是因为float了的元素虽然在正常的文档流之内,但是只是让相邻(非float)的元素的内容围绕着它排列,它仍然占据相邻元素的background和border空间。如果给middle添加一个白色的border,那么看起来是这样的:

Action 4 浮动的元素占据了文档流相应元素的背景和边缘空间

float left 

middle 

right 

       明显看到,float left的元素占据了middle的background和border的空间,同时middle的内容围绕着left排列。理解这点很重要。

       假设middle里面有个p标签,而p标签的内容比较长,那么围绕的效果是这样的:

Action 5 浮动的环绕效果

float left 

middle 

环绕的元素一旦超出float元素高度之后,会以正常的宽度显示

float right 

      正如上面的注释一样,在float元素的那一行,相邻的元素的内容的宽度将会缩短,以适应float元素占去的宽度,而一旦超过float元素的区域,相邻元素的内容显示宽度就会正常。

      由于默认的div会占一行,所以不能将middle放在第二个div,得放到第三个div。把第二个div和第三个div换一下顺序:

<div style="float:left;">left</div><div style="float:right;">right</div><div>middle</div>

      先让float right的div渲染,再渲染middle的div。因为渲染left之后,left的那一行仍然有空间,这是由于float left之后,只会占据当前行的background和border,而当前行还有很大的空间,于是第二步渲染right时就和left同一行了,效果:

Action 6 先渲染左右两个div,再渲染中间的div

left 

right 

middle 

       如果不设置middle的width,那么middle将围绕着left和right环绕,和left一样,right也会占用middle的空间。

Action 7 中间的div围绕着左右的div环绕

left 

right 

~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ middle围绕着left和right环绕,同时left和right占据着middle的空间 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

       为了让middle和left/right中间有一个margin值,设置下middle的左右margin各为110px,这样就和左右和中间就各有10px的间距:

Action 8 设置中间div的margin值为100px + 10px

left 

right 

middle 设置margin: 0 110px;

       这种办法的优点是实现简单,支持性好。

       这种自适应宽度的原理是利用了float的围绕特性,占据自然文档流的background/border位置。这个围绕特性不仅会影响当前行的内容,还会影响下一行的内容,如下说明:

<p>第一段内容,略<img  src=""   style="max-width:90%"从三栏自适应宽度布局到css布局的讨论_html/css_WEB-ITnose" >float:left;</strong>"></img></p><p>第二段内容,略</p>

Action 8 float元素占据了下一行的空间

第一段落围绕着图片排列

图片的float属性也影响了第二段落,也就是说float会占据自然文本流相应位置元素的背景和边框,即使和float的元素不在同一行

 

       网上还有一种margin负值法。margin负值法的步骤是:第一步让中间的middle占100%的宽度,而middle的内容设置左右margin各为100px,这样就实现了middle居中自适应宽度的效果:

<div style="width:100%;">      <div style="margin: 0 100px;">middle</div></div>

middle

       接下来让left的margin-left值为-100%,由于这个比例是相对于浏览器窗口大小的,所以要是left和middle是在同一行的话,left就可以跑到middle的最左边。但是因为middle的容器盒是一个普通的div,会占据一整行,left就会排到下一行,这个时候设置margin-left为一个负数时就跑出屏幕了。所以让middle float一下,left就会排到第一行最左边,同时middle覆盖在上面:

<div style="width:100%; <strong>float:left</strong>;">      <div style="margin: 0 100px;">middle</div></div><div style="margin-left: 0">left</div>

middle

left

      从上面可以看到:这样实现,导致left的内容被挤出目标区域,因为正如上面所说,middle占据了left的背景空间,上面的情况是把它占满了,left内容只能overflow了。所以这样实现是有问题的,得让left也向左float一下,这样left就紧挨在middle后面了,由于middle占了100%的宽度,所以再让left向左边margin了-100%后,left就刚好在最左边了。

<div style="width:100%; float:left;">      <div style="margin: 0 100px;">middle</div></div><strong><div style="float: left; margin-left: -100%;">left</div></strong>

middle

left

       注意这里,虽然left float之后看起来也是被排到下一行了,但和默认的div独占一行是不一样的。float之后的left仍然和middle是同一行的,因为空间不足的时候,float只是把当前行盒的空间撑大,就和一个div块盒里面有很多个display为inline-block的行内级盒是同样的道理。例如:

<style>     button{ width: 150px; }</style><div style="width: 300px;">    <button>按钮1</button>     <button>按钮2</button>     <button style="margin-left: -200px;">按钮3</button></div>

     按钮3设置了一个很大的margin-left的负值后并不是跑到屏幕外了,而是在和其它两个按钮同一行的位置,显示如下:

按钮1 按钮2 按钮3

     注意,设置了float的元素并不是把display改成了 inline-block,大部份display的css计算值都变成了block,同时对原本是display: flex的没有改变:

指定值 计算值
inline block
inline-block block
inline-table table
table-row block
table-row-group block
table-column block
table-column-group block
table-cell block
table-caption block
table-header-group block
table-footer-group block
flex flex, but float has no effect on such elements
inline-flex inline-flex, but float has no effect on such elements
other unchanged

来自MDN

     由上表可看出,一个span设置了float: left/right之后,就不需要再设置成display: block/inline-block了,直接设置宽高即可。

     回归正题,left的div设置了margin-left: -100%之后就跑到左边去了,而right也是同样道理,将right的margin-left相应地设置为-100px,就跑到最右边去了:

<div style="width:100%; float:left;">      <div style="margin: 0 100px;">middle</div></div><div style="margin-left: -100%;">left</div><strong><div style="margin-right: -100px;"</strong>>right</div>

Action 9 margin负值法

middle

left

right

 

      下面讨论第三种方法,使用display: table-cell

      由于table的展示拥有自适应的特点,因此把需要自适应宽度的middle的display属性设置为table-cell。

<div style="float:left;">left</div><div style="float:right;">right</div><div style="display:table-cell;">middle</div>

      效果如下:

left

right

middle

      发现table-cell的宽度是根据内容自适应的,这里是要根据浏览器窗口自适应,因此给middle添加一个很大的width就可以了,例如width:2000px:

Action 10 让中间的div使用table-cell自适应宽度

left

right

middle

      由于ie6/7不支持display: table-cell,所以如果要支持ie6/7的话,就得用display: inline-block,ie6/7的inline-block和标准不一样,它是用来触发hasLayout特性,使元素拥有布局属性。作用在行内元素,可以使得宽高等设置生效,如果作用在块元素,仅是触发布局特性,还要再设置成inline才是行内块元素,如果不设置inline效果就跟table-cell很像。不一样的地方是:设置了width:2000px,导致太长会换行,因此得用ie6/7的hack,设置*width: auto重新改会width值就可以了:

<style>    .middle{         display: table-cell;         *display: inline-block; /* _和*开头的只有ie6/7会识别 */         width: 2000px;         *width: auto;    }</style><div style="float:left;">left</div><div style="float:right;">right</div><div class="middle">middle</div>

      但是笔者认为:为了互联网的美好,不要再兼容ie6/7了,甚至ie8。

 

      接下来,继续第四种方法,使用flex布局,十分简单:只需要将容器设置为display: flex,然后再设置middle的flex-grow为1即可:

<section style="display:flex;"></section><div>left</div>   <!--宽度为100,省略--><div style="flex-grow: 1;">middle</div><div>right</div>

Action 11 使用flex-grow自适应宽度

left

middle flex-grow: 1

right

      flex-grow: 1的作用是把middle的宽度置为flex容器的剩余宽度,就达到了自适应的目的。flex的使用不作全面介绍,详情可查看CSS-Tricks: A Complete Guide to Flexbox

      但是flex布局ie的支持性较差,具体查看caniuse.

 

      最后再分析另外一个自适应的例子,某个元素的宽度要根据其它元素的宽度自适应。如下图所示,排名的位数变化可能会很大,导致最右边的文字要自适应:

 

       根据上面的一番分析,这个例子就不难实现了,如下面的分析,p标签里的文字宽度就能自适应了:

      

    <div style="width:320px;">        <span style="width:14px;<strong>float:left;</strong>">排名</span>        <span style="font-size:40px;<strong>float:left;</strong>">89</span>        <br />        <img    style="max-width:90%"从三栏自适应宽度布局到css布局的讨论_html/css_WEB-ITnose" >float:left;</strong>" src="..."></img>        <p>你的好友会编程的银猪在土壕榜中排名89</p>    </div>

      实际效果:

排名 1

你的好友会编程的银猪在土壕榜中排名1

排名 6783

你的好友会编程的银猪在土壕榜中排名6783

 

 

       使用float是最简单的,还可以尝试使用flex布局,主要用到flex-shrink属性,flex-shrink的作用是定义收缩比例,容器内的子元素的宽度和若超出容器的宽度时,将按比例收缩子元素的宽度,使得宽度和等于容器的宽度。如下所示,将前面三个span/img的flex-shrink设置为0,而p的flex-shrink设置为1,这样子使得溢出的宽度都在p标签减去,就能够达到p标签宽度自适应的效果。

   <style>        span,img{<strong> flex-shrink: 0;</strong> }        p{<strong> flex-shrink: 1; </strong>}   </style>    <div style="display:flex;width:320px;">                                       <span style="width:14px;">排名</span>        <span style="font-size:40px;line-height:45px;">89</span>                <img    style="max-width:90%" src="..." alt="从三栏自适应宽度布局到css布局的讨论_html/css_WEB-ITnose" ></img>        <p>你的好友会编程的银猪在土壕榜中排名89</p>    </div>

      实时效果:

排名 89

你的好友会编程的银猪在土壕榜中排名89

排名 1890

你的好友会编程的银猪在土壕榜中排名1890

 

 

       上文综合分析了最原始的table布局,然后就是float布局、table-cell、margin负值法、以及flex布局来实现自适应宽度的实现和原理,重点讨论了float的一些特性。如果上面的分析有错误,还望指正。

 

参考:

1. CSS Float Theory: Things You Should Know

2. CSS Tricks: All About Floats

3. CSS-Tricks: display

4. Understanding Floats

5. 视觉格式化模型

 

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn