使用 CSS3 弹性盒模型的应用布局

得益于 CSS3 弹性盒布局模块,创建灵活的应用布局变得非常容易。在本文中,我们将实现一个简单的应用布局,它填充整个屏幕,随着浏览器窗口大小调整,并且附带一个可拖动的分割器的额外好处。

除了经典的 <div> 元素外,我们还可以使用一些 HTML5 结构化标签。这不仅使代码更具语义化,而且更方便使用,因为我们可以直接使用 CSS 类型选择器来定位元素,而无需依赖 id 属性或父子关系。

查看 完整的演示 以了解其工作原理。

第一步:添加垂直盒子

我们从主体中的三个标签(<header>、<main> 和 <footer>)开始。



    CSS3 Application Layout 



让我们添加 CSS 以使这三个元素垂直填充空间。这是通过将 <body> 的 CSS display 属性设置为 flex 以及 flex-direction 属性设置为 column 来实现的。这告诉浏览器将主体的子元素(<header><main><footer>)作为垂直弹性盒子进行布局。

可以使用 flex 简写属性控制可用空间的分配方式。您可以在 MDN 上阅读相关信息。但是,在此应用布局中,我们不希望大小按比例缩小或扩展。相反,<header><footer> 元素应具有固定高度,而 <main> 应通过将其 flex 属性设置为 auto 来填充剩余空间。

html, body {
    height: 100%;
    width: 100%;
    padding: 0;
    margin: 0;
}

body {
    display: flex;
    flex-direction: column;
}

header {
    height: 75px;
}

main {
    flex: auto;
}

footer {
    height: 25px;
}

显示演示

第二步:水平盒子

让我们在 <main> 元素内再添加三个元素(<nav><article><aside>)。但这次我们希望它们水平而不是垂直地填充 <main> 元素内的空间。

这是通过将 <main> 元素的 display 属性也设置为 flex,但 flex-direction 属性设置为 row 来实现的(这是默认值)。<nav><aside> 元素应具有固定宽度,而 <article> 应填充剩余空间:这与之前的方式相同。

main {
    display: flex;
    flex-direction: row;
    flex: auto;
}

nav {
    width: 150px;
}

article {
    flex: auto;
}

aside {
    width: 50px;
}

显示演示

就是这样。调整浏览器窗口大小并享受灵活的应用程序布局。

下一步:CSS 优化

但是等等。当内容很多时,某个元素可能会变得比指定的小,并且还会出现滚动条。

因此,我们需要为添加了 width 属性的所有元素添加 min-width 属性。我们还应将 <body><main> 元素的 overflow 属性设置为 hidden,并将 <article><aside>overflow-y 设置为 auto,以便仅在我们需要的地方显示滚动条。

body {
	overflow: hidden;
	display: flex;
	flex-direction: column;
}

header {
	height: 75px;
	min-height: 75px;
}

footer {
	height: 25px;
	min-height: 25px;
}

main {
	display: flex;
	flex-direction: row;
	flex: auto;
	border: solid grey;
	border-width: 1px 0;
	overflow: hidden;
}

nav {
	width: 150px;
	min-width: 150px;
}

article {
	border: solid grey;
	border-width: 0 0 0 1px;
	flex: auto;
	overflow-x: hidden;
	overflow-y: auto;
}

aside {
	width: 50px;
	min-width: 50px;
	overflow-x: hidden;
	overflow-y: auto;
}

注意:这可能在 Safari 中尚无法使用。您可以尝试使用 -webkit- 前缀来使其工作。

最后一步:加入一些 JavaScript

作为最后一步,我们希望用户能够在用鼠标拖动时调整 <aside> 元素的大小。为此,我们添加了一个 <div> 元素作为分割器,它将用作拖动手柄。

我们将手柄的宽度设置为 4px,并将 cursor 属性的值设置为 col-resize,以向用户显示此元素可以东西方向调整大小。

.splitter {
    border-left: 1px solid grey;
    width: 4px;
    min-width: 4px;
    cursor: col-resize;
}

现在剩下的就是添加一小段 JavaScript 来启用移动分割器。

var w = window, d = document, splitter;

splitter = {
    lastX: 0,
    leftEl: null,
    rightEl: null,

    init: function(handler, leftEl, rightEl) {
        var self = this;

        this.leftEl = leftEl;
        this.rightEl = rightEl;

        handler.addEventListener('mousedown', function(evt) {
            evt.preventDefault();    /* prevent text selection */

            self.lastX = evt.clientX;

            w.addEventListener('mousemove', self.drag);
            w.addEventListener('mouseup', self.endDrag);
        });
    },

    drag: function(evt) {
        var wL, wR, wDiff = evt.clientX - splitter.lastX;

        wL = d.defaultView.getComputedStyle(splitter.leftEl, '').getPropertyValue('width');
        wR = d.defaultView.getComputedStyle(splitter.rightEl, '').getPropertyValue('width');
        wL = parseInt(wL, 10) + wDiff;
        wR = parseInt(wR, 10) - wDiff;
        splitter.leftEl.style.width = wL + 'px';
        splitter.rightEl.style.width = wR + 'px';

        splitter.lastX = evt.clientX;
    },

    endDrag: function() {
        w.removeEventListener('mousemove', splitter.drag);
        w.removeEventListener('mouseup', splitter.endDrag);
    }
};

splitter.init(d.getElementsByClassName('splitter')[0], d.getElementsByTagName('article')[0], d.getElementsByTagName('aside')[0]);

注意:由于某种原因,调整大小在 IE11(、Safari?)或 Chrome 31 中不起作用。这似乎与 display: flex; 属性值有关。

关于 Simon Speich

Simon Speich 是一位 Web 开发人员,相信 Web 标准,并且从 Mozilla 0.8 开始就热爱 Mozilla。他还热衷于 摄影。您可以在他的网站 www.speich.net 上了解更多关于他的信息。

更多 Simon Speich 的文章……

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。发表关于 HTML5、JavaScript 和开放 Web 的演讲和博客文章。Robert 坚定地相信 HTML5 和开放 Web,自 1999 年以来一直从事 Web 前端开发工作 - 在瑞典和纽约市。他还定期在 http://robertnyman.com 上发表博客文章,并且喜欢旅行和结识新朋友。

更多 Robert Nyman [荣誉编辑] 的文章……


6 条评论

  1. Hervé Renault

    您好,Robert,

    注意:由于某种原因,调整大小在 IE11(、Safari?)或 Chrome 31 中不起作用。

    所以,目前我们还不能在生产环境中使用弹性盒,对吗?

    2013 年 12 月 17 日 02:48

    1. Robert Nyman [编辑]

      这取决于生产环境、目标和必要性。例如,您能否提供一个良好的基础,然后在此基础上添加此功能?或者进行一些优雅的降级?基本上,对于生产用例,我认为这取决于您的需求。

      2013 年 12 月 17 日 04:20

    2. Xenon

      我不知道是不是只有我一个人,但在我的一个个人项目中,弹性盒在我的所有测试浏览器中都能完美运行。Chrome 31、Internet Explorer 11 以及 Opera 18。由于我没有 Mac,因此我无法在 Safari 中进行测试。目前唯一无法正常工作的浏览器是 Firefox,因为它不支持某些 flex-wrap 值。

      2013 年 12 月 17 日 17:43

      1. Simon Speich

        Firefox 尚未支持 flex-wrap 属性。但是(基本)支持应该在下一个版本 28 中提供:Mozilla 开发者网络

        2013 年 12 月 18 日 00:16

  2. stripTM

    您好,
    我接下来要说的内容与主题无关,抱歉。
    在实现分割器的 javascript 中,有以下内容

    this.leftEl = leftEl;
    splitter.leftEl
    self.drag
    splitter.drag

    我认为如果所有内容都以统一的方式完成,例如在所有情况下都使用:splitter.element,会更简单。

    2013 年 12 月 17 日 05:22

  3. Tomas Bavington

    只是想说,这对我来说确实效果很好,直到我升级到最新版本的 Firefox。我猜这只是时间问题,但我正在研究它。我也同意 stripTM 关于统一性的观点。

    2014 年 1 月 8 日 10:53

本文的评论已关闭。