CSS设计指南(页面布局)

布局的基本概念


多栏布局有三种基本的实现方案:固定宽度、流动、弹性。

  • 固定宽度布局的大小不会随用户调整浏览器窗口大小而变化,一般是900 到1100像素宽。其中960 像素是最常见的,因为这个宽度适合所有现代显示器,而且能够被16、12、10、8、6、5、4 和3 整除,不仅容易计算等宽分栏的数量,而且计算结果也能得到没有小数的像素数。流行的CSS 布局框架960 Grid(http://www.960.gs),就是基于960 像素宽的网格创建的。
  • 流动布局的大小会随用户调整浏览器窗口大小而变化。这种布局能够更好地适应大屏幕,但同时也意味着放弃对页面某些方面的控制,比如随着页面宽度变化,文本行的长度和页面元素之间的位置关系都可能变化。Amazon.com 的页面采用的就是流动中栏布局,在各栏宽度加大时通过为内容元素周围添加空白来保持内容居中,而且现在的导航条会在布局变窄到某个宽度时收缩进一个下拉菜单中,从而为内容腾出空间。今天,越来越多的浏览器都支持CSS 媒体查询了。这就让基于浏览器窗口宽度提供不同的CSS 样式成为可能。在这种形势下,适应各种屏幕宽度的可变固定布局,正逐步取代流动布局。这种可变的固定布局能够适应最大和最小的屏幕,业界称之为响应式设计。
  • 弹性布局与流动布局类似,在浏览器窗口变宽时,不仅布局变宽,而且所有内容元素的大小也会变化,让人产生一种所有东西都变大了的感觉。到目前为止,我还没见过设计得非常好的弹性布局,主要是因为它太过复杂了。
布局高度与布局宽度

在实际地创建页面布局之前,我想先说说应该怎么看待布局的高度和宽度,因为这两者的控制方法实在太不一样了。

布局高度
多数情况下,布局中结构化元素(乃至任何元素)的高度是不必设定的。事实上,我甚至想告诉你根本不应该给元素设定高度。除非你确实需要这样做,比如在页面中创造一个绝对定位的元素。
为什么正常情况下都应该保持元素height 属性的默认值auto 不变呢?很简单,只有这样元素才能随自己包含内容的增加而在垂直方向上扩展。这样扩展的元素会把下方的元素向下推,而布局也能随着内容数量的增减而垂直伸缩。假如你明确设定了元素的高度,那么超出的内容要么被剪掉,要么会跑到容器之外——取决于元素overflow 属性的设定。

布局宽度
与高度不同,我们需要更精细地控制布局宽度,以便随着浏览器窗口宽度的合理变化,布局能够作出适当的调整,确保文本行不会过长或过短。如果随意给元素添加内边距、边框,或者元素本身过大,导致浮动元素的宽度超过包含元素的布局宽度,那浮动元素就可能“躲”到其他元素下方。
当然啦,即使必须设定栏宽,也不要给包含在其中的内容元素设定宽度,应该让这些内容元素自动扩展到填满栏的宽度。本书前面已经讲过了,这是块级元素的默认行为。简言之,就是让栏宽限制其中内容元素的宽度。

三栏-固定宽度布局


<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <!-- <link rel="stylesheet" href="./css/test.css"> -->
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        #wrapper {
            width: 960px;
            margin: 0 auto;
            border: 1px solid;
        }
        header {
            background: #f00;
        }
        nav {
            width: 150px;
            float: left;
            background: #dcd9c0;
        }
        nav li {
            list-style-type: none;
        }
        article {
            width: 600px;
            float: left;
            background: #ffed53;
        }
        aside {
            width: 210px;
            float: left;
            background: #3f7ccf;
        }
        footer {
            clear: both;
            background: #000;
        }
    </style>
</head>
<body>
    <div id="wrapper">
        <header>
            标题
        </header>
        <nav>
            <ul>
                <li>无序列表1</li>
                <li>无序列表2</li>
                <li>无序列表3</li>
            </ul>
        </nav>
        <article>
            如图5-2 所示,把两栏容器元素的总宽度设定为外包装的宽度(150 + 810 = 960), 并浮动它们,就可以创造出并肩排列的两栏来。每一栏的长度取决于内容多少。采 用同样的方法,可以添加第三栏(或任意多个栏)。
        </article>
        <aside>
            文本
        </aside>
        <footer>
            footer
        </footer>
    </div>
</body>

</html>
为栏设定内边距和边框

只要一调整各栏中的内容,布局就可能超过容器宽度,而右边的栏就可能滑到左边的栏下方。一般来说,两种情况下可能会发生这种问题。

  • 为了让内容与栏边界空开距离,为栏添加水平外边距和内边距,或者为了增加栏间距,为栏添加外边距(只要开始给布局添加样式,就一定会采用这里说的一种做法,甚至双管齐下),导致布局宽度增大,进而浮动栏下滑。换句话说,右边浮动的栏因为没有足够的空间与其他栏并列,就会滑到左边栏的下方。
  • 在栏中添加大图片,或者没有空格的长字符串(如长URL),也会导致栏宽超过布局宽度。同样,这种情况下右边的栏也会滑到左边的栏下方。

为固定宽度的元素添加水平外边距、边框和内边距,会导致元素盒子变宽

好在,我们也有三种方法来预防该问题发生。

  • 从设定的元素宽度中减去添加的水平外边距、边框和内边距的宽度和。
  • 在容器内部的元素上添加内边距或外边距。
  • 使用CSS3 的box-sizing 属性切换盒子缩放方式,比如section {box-sizing:border-box;}。 应用box-sizing 属性后,给section 添加边框和内边距都不会增大盒子,相反会导致内容变窄。

直接给栏应用内边距会导致内容变窄,但不会影响布局。听起来容易
的办法总会有一个“但是”,这里的“但是”要说的是IE6 和IE7 不支持box-sizing
属性。不过,有一个专门解决这个问题的腻子脚本(polyfill),名叫borderBoxModel.js。
你可以使用条件注释(以便只有IE6 和IE7 加载)把它添加到HTML 标记之后、结
束的</body>标签之前,以保证在加载DOM 之后再执行该脚本:

<body>
  <!-- HTML 标记 -->
  <!-- 只让IE8 之前的IE 加载它 -->
  <!--[if lt IE 8 ]>
  <script src="helpers/borderBoxModel.js"></script>
  <![endif]-->
</body>

然而,一栏之中可能会包含大量不同内容的元素。假如将来又决定调整内容与容器边界的距离,就必须每个元素都要进行调整,这样不仅麻烦,而且容易出错。况且,给栏添加边框同样会增大栏宽,不可能通过为其包含的内容元素逐个应用样式来做到。
所以说,与其为容器中的元素添加外边距,不如在栏中再添加一个没有宽度的div,让它包含所有内容元素,然后再给这个div 应用边框和内边距。如此一来,只要为内部div 设定一次样式,就可以把让所有内容元素与栏边界保持一致的距离。而且,将来再需要调整时也会很方便。任何新增内容元素的宽度都由这个内部div 决定。

关于表现性标记的思考
HTML 的目的是语义,也就是给内容赋予含义。而CSS 呢,是为了把表现性的样式分离出来才发明的。不过,有些表现性标记是有害的,而有些则没有副作用。使用表格来创建多栏布局,或者使用<br />标签在段间换行,却不使用<p>标签,这种做法的确不值得提倡,因为这会造成内容难以移植。比如说吧,用三个表格单元作为三栏,这种布局到哪都会显示成表格,就算是在完全不合适的智能手机里也一样。如果表现性标记无法用CSS 修改,或者在CSS 不可用时也要迫使用户接受,那就是滥用HTML。可是,div 或span 这种中性的元素,对默认样式没有影响,除非你给它们应用样式,否则它们就跟不存在一样。所以,我认为添加这种元素达到表现性的目的是完全可以接受的。

子-星选择符
所谓“子-星选择符”就是一个组合选择符,利用它可以不使用内部div 就能设定一栏中所有元素的外边距。
星号选择符可以选择“所有元素”,故而,在一个选择符后面加个星号,比如someSelector *就可以选择someSelector 所代表元素的所有后代元素。子选择符可以选择“某元素的子元素”,故而,把子选择符放到星号前面,比如someSelector > *就会只选择someSelector所代表元素的所有子元素,而非后代元素。这正好适用于选择容器内部的所有顶部元素,然后设定它们的外边距。比如,对于section 栏,设定section > * {margin:0 10px;},就能为栏中所有子元素,不包括其他后代元素,各应用10 像素的左、右外边距。 使用“子-星选择符”要注意两点。
第一,在为子元素设定垂直外边距时,只能使用margin-top 和margin-bottom,不能使用简写的margin,否则会抵消用“子-星选择符”应用给这些元素的水平外边距。如果你想进一步缩进某个子元素的内容,就应该给该子元素应用内边距。
第二,“子-星选择符”有潜在性能问题,因为它会导致浏览器遍历整个DOM 结构去查找所有匹配的元素。但我也发现这一点性能影响几乎可以忽略不计。假如你的页面真的包含几千上万个元素,那倒确实该考虑用ySlow 或其他性能度量工具测一测这个选择符的影响。

预防过大的元素
设计一个将来可能由他人维护的动态网站时,需要考虑得更长远一些。比如,应该预见到可能出现一些过大的元素。如果将来有一张比浮动栏更宽的图片被放到栏中,就会导致布局变宽,而右边的栏又会滑到下方。为此, 一个简单的预防措施就是添加一条.inner img{max-width:100%;}声明,以便限制图片的宽度不超过其父元素(在此就是内部div)。
另一个办法是给每个栏(或者内部div,如果你用了的话)添加overflow:hidden 声明。这条声明不会缩小图片以适应父元素,而会将它(以及任何过大元素)超出容器边界的部分剪切掉。动态网站中另一个潜在的问题是换行。HTML 只会在单词间空格的地方换行。一些长URL,甚至一些长单词,在栏比较窄的情况下,都会导致栏宽过大。因此,还应该给所有栏的外包装元素应用word-wrap:break-word 声明,以便所有栏及其内容继承这个设定。有了这条声明,浏览器会把过长的词断开显示在不同行上。只是word-wrap 没有定义在哪里断开,因此结果完全是随机的,而且没有连字符。不过,这条规则只在需要时才会起作用,而且能保护布局不会被长URL 顶得支离破碎。建议你在每一栏中都用长URL、大图片,以及包含内容过多的元素测试一下布局,看看这些声明到底会不会起作用,并发现更多需要事先考虑保护措施的其他漏洞。

三栏-中栏流动布局


实现中栏流动布局有两种方法。一种是在中栏改变大小时使用负外边距定位右栏,另一种是使用CSS3 让栏容器具有类似表格单元的行为。负外边距适合比较老的浏览器,而CSS 的table 属性则要简单得多。

用负外边距实现

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        body {
            font: 1em helvetica, arial, sans-serif;
        }
        div#main_wrapper {
            min-width: 600px;
            max-width: 1100px;
            /*超过最大宽度时,居中布局*/
            margin: 0 auto;
            /*背景图片默认从左上角开始拼接*/
            background: url(images/bg_tile_150pxw.png) repeat-y #eee;
        }
        header {
            padding: 5px 10px;
            background: #3f7ccf;
        }
        div#threecolwrap {
            /*浮动强制它包围浮动的栏*/
            float: left;
            width: 100%;
            /*背景图片右对齐*/
            background: url(images/bg_tile_210pxw.png) top right repeat-y;
        }
        div#twocolwrap {
            /*浮动强制它包围浮动的栏*/
            float: left;
            width: 100%;
            /*把右栏拉到区块外边距腾出的位置上*/
            margin-right: -210px;
        }
        nav {
            float: left;
            width: 150px;
            background: #f00;
            padding: 20px 0;
        }
        /*让子元素与栏边界保持一定距离*/
        nav>* {
            margin: 0 10px;
        }
        article {
            width: auto;
            margin-left: 150px;
            /*在流动居中的栏右侧腾出空间*/
            margin-right: 210px;
            background: #eee;
            padding: 20px 0;
        }
        /*让子元素与栏边界保持一定距离*/
        article>* {
            margin: 0 20px;
        }
        aside {
            float: left;
            width: 210px;
            background: #ffed53;
            padding: 20px 0;
        }
        /*让子元素与栏边界保持一定距离*/
        aside>* {
            margin: 0 10px;
        }
        footer {
            clear: both;
            width: 100%;
            text-align: center;
            background: #000;
        }
    </style>
</head>

<body>
    <div id="main_wrapper">
        <header>
            <!-- 页眉-->
        </header>
        <!-- /*三栏外包装(包围全部三栏)*/ -->
        <div id="threecolwrap">
            <!-- /*两栏外包装(包围左栏和中栏)*/ /*左栏*/ -->
            <div id="twocolwrap">
                <nav>
                    <!-- 导航 -->
                </nav>
                <!-- /*中栏*/ -->
                <article>
                    <!-- 区块 -->
                </article>
                <!-- /*结束两栏外包装(twocolwrap)*/ /*右栏*/ -->
            </div>
            <aside>
                <!-- 侧栏 -->
            </aside>
            <!-- /*结束三栏外包装(threecolwrap)*/ -->
        </div>
        <footer>
            <!-- 页脚 -->
        </footer>
    </div>
</body>
</html>

用CSS3 单元格实现

尽管利用HTML 的<table>标签实现多栏布局是难以接受的,但使用CSS 让布局形如表格则是绝对可以接受的。这种方法不会导致固定不变的表格布局,也不会出现难以重新应用样式的问题(比如在手持设备上表现为一栏)。
我们知道,CSS 可以把一个HTML 元素的display 属性设定为table、table-row 和table-cell。通过这种方法可以模拟相应HTML 元素的行为。而通过CSS 把布局中的栏设定为table-cell 有三个好处。

  • 单元格(table-cell)不需要浮动就可以并排显示,而且直接为它们应用内边距也不会破坏布局。
  • 默认情况下,一行中的所有单元格高度相同,因而也不需要人造的等高栏效果了。
  • 任何没有明确设定宽度的栏都是流动的。

CSS3 表格行为在IE7 及更低版本中并没有得到支持,而且也没有稳妥的补救措施。如果你(或者你的客户)愿意摒弃IE7,那么它就是一个既简单又可靠,而且还很彻底的解决方案。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <style>
        nav {
            display: table-cell;
            width: 150px;
            padding: 10px;
            background: #dcd9c0;
        }

        article {
            display: table-cell;
            padding: 10px 20px;
            background: #ffed53;
        }

        aside {
            display: table-cell;
            width: 210px;
            padding: 10px;
            background: #3f7ccf;
        }
    </style>
</head>

<body>
    <nav>
        <!-- 内容 -->
    </nav>
    <article>
        <!-- 内容 -->
        请注意,这个简单、功能完备的布局对IE7 和IE6 可没有任何腻子脚本,甚至连个退
化的后备方案都没有。在这些浏览器中,三栏会上下堆叠在一起。因此,除非你下
定决心不再支持老版本的IE,否则就得使用本章前面讲过的其他布局技术。等吧,
等到这些浏览器没人用为止。
    </article>
    <aside>
        <!-- 内容 -->
    </aside>
</body>
</html>

多行多栏布局


CSS选择符的实际应用

随着页面变得越来越复杂,相同的HTML 元素(如section、article、nav,等等)会出现很多次——比如,前面布局中的article 就出现了7 次。为了选择某个元素,必须区分这些相同的标签名。为此,有些新手会给每个标签都添加一个不同的类名。但这种做法是不值得提倡的。不仅因为类本身就不该这么用(类应该用于标记具有相同特征的元素),而且这么多类会把标记弄得很乱,让CSS 也很难看懂。为了知道每个类代表哪个元素,你必须不断地查看HTML。
更好的做法是给标记中每个主要区域的顶级元素添加一个ID,这也是使用ID 的正确方式,ID 就是标识页面中唯一元素用的。然后,这些ID 就会成为HTML 标记中的
“路标”,放在上下文选择符开头的时候,它们就能起到框定后代元素的作用。这就是在标记中保持类和ID 属性最少的秘诀。而且,相应的上下文选择符也能清晰地传达出路径信息,让人从CSS 中一眼就能看出它要选择哪个元素。

 <div id="wrapper">
        <header>
            <h1>Full-width content</h1>
        </header>
        <nav>
            <p>Navigation menus go here</p>
        </nav>
        <section id="branding">
            <img src="images/grand_canyon.jpg" alt="Grand Canyon" />
        </section>
        <!-- branding 结束 -->
        <section id="feature_area">
            <article>
                <div class="inner">
                    <p>Lorem Ipsum text</p>
                </div>
            </article>
            <!-- 省略另外两个 article 元素 -->
        </section>
        <!-- feature_area 结束-->
        <section id="promo_area">
            <article>
                <div class="inner">
                    <p>Lorem Ipsum text</p>
                </div>
            </article>
            <!-- 省略另外三个 article 元素 -->
        </section>
        <!-- promo_area 结束-->
        <footer>
        </footer>
    </div>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,747评论 1 92
  • 阅读CSS设计指南前5章节时所做的笔记,有些为摘录书本中的原句,有些为自己的想法,如果有什么错误的地方还请大家指教...
    CodingEleven阅读 332评论 1 2
  • 理解盒模型每一个元素都会在页面上生成一个盒子。因此,HTML页面实际上就是由一堆盒子组成的。先从每个元素盒子的属性...
    xpwei阅读 529评论 0 0
  • CSS 是什么 css(Cascading Style Sheets),层叠样式表,选择器{属性:值;属性:值}h...
    崔敏嫣阅读 1,478评论 0 5
  • 姑娘们都喜欢拍婚纱照,想起当年我自己拍婚照的时候,溜溜整了一天,中午饭都是带着厚厚的粉一边吃,一边生怕脸上的粉挂不...
    花团1220阅读 282评论 0 1