CSS 网格是内容驱动的网站(包括长篇文本)的优秀布局工具,并且对各种传统的 UI 布局也具有巨大的价值。在本文中,我将向您展示如何使用 CSS 网格来改进需要响应并适应用户交互和不断变化条件的应用程序布局,并始终使面板滚动得当。
CSS 网格 用于构建网站布局。它允许网页设计师使用少量支持的代码来创建美观的动态布局,而不是多年来我们不得不使用的无休止的浮动技巧。我的朋友和同事 Jen Simmons 一直在谈论 CSS 网格多年,不懈地推动它在浏览器中实施,她的努力终于有了回报。截至去年年底,所有主要浏览器(台式机和移动设备)的当前版本都支持 CSS 网格。
CSS 网格确实功能强大,您可以轻松地构建动态内容驱动的网站,例如这些示例。但是,网格不仅适用于布局漂亮的块状内容。网格使您可以完全控制布局的两个维度,包括滚动。这意味着我们可以在原生应用程序中将某些功能视为理所当然,例如折叠侧边栏和固定工具栏,现在这些功能的实现变得微不足道。不再需要使用技巧,也不再需要调试。网格工作正常。
我多年来一直在构建 Web 工具。这是一张我为我的复古 RPG 制作的游戏构建工具的屏幕截图。当Flexbox 出现时,我立即开始使用它。我使用嵌套的水平和垂直框构建了复杂的布局,并使用一些实用程序类来处理诸如滚动和拉伸等操作。
Flexbox 当然使我比绝对定位的 div 和浮动技巧更有效率,但它仍然存在问题。看看面板连接处的特写。看到左侧和右侧的页脚没有对齐吗?
这是另一张屏幕截图。工具栏位于绘图画布的顶部,根据我的框架,它应该固定在顶部,但您一旦开始滚动,就会发生这种情况。工具栏消失了。
每个问题都可以通过更多定位和浮动技巧来解决,但结果总是脆弱的。每次我添加一个新的面板时,我都必须重新调试布局;搜索以确定哪个div
在调整大小期间占用了额外的空间。而且标记很丑。嵌套的水平和垂直框变得非常复杂,而这个示例只有两层深。随着交互和功能变得更加复杂,设计也变得更加具有挑战性。
<div class='hbox'>
<div class='vbox'>
<div class='hbox'>header</div>
<div class='scroll'>
<div class='sidebar'>sidebar</div>
</div>
<div class='footer'>footer</div>
</div>
<div class=vbox>
<div class='hbox'>button button
spacer label spacer button button </div>
<div class='center'>main content</div>
<div class='hbox'>the footer</div>
</div>
<div class=vbox>
<div class=’hbox’>header</div>
<div class=’scroll’>
<div class=’sidebar’>sidebar</div>
</div>
<div class=’footer’>footer</div>
</div>
</div>
进入第二维
Flexbox 的根本问题在于它是一维的。这使得 Flexbox 非常适合一维用途,例如工具栏和导航栏,但当我需要同时水平和垂直对齐内容时,它就开始失效了。相反,我需要真正的二维布局,这就是我需要 CSS 网格的原因。从根本上说,网格是二维的。
这里有一个使用 CSS 网格构建的类似布局。
仔细观察底部的页脚。它们完美地结合在一起。而且通过使用grid-gap
来表示线,而不是向每个面板添加边框,我不必担心网格线宽度不一致。一切都正常工作。
我从 CSS 网格中获得的最大好处是适应不断变化的条件。我的应用程序通常有侧边栏。我需要确保布局中的所有内容在面板展开或折叠时都能正常工作,理想情况下无需在 JavaScript 中重新计算布局。侧边栏由多个组件组成,例如标题和页脚。所有这些都需要对齐,无论哪个组件更大或更小。 网格也可以使用名为minmax()
的神奇功能来做到这一点。
如果您以前研究过 CSS 网格,那么您就会知道可以使用模板 来定义行和列的布局。 像200px 1fr 200px
这样的模板将为您提供 200 像素宽的侧边栏,中间内容区域将占用剩余的空间。但是,如果面板应该折叠怎么办?现在,列将保持在 200 像素,即使内容已经缩小。相反,我们可以对max
参数使用min-content
关键字使用minmax
。
<b>#grid </b>{
<b>display</b>: <b>grid</b>;
<b>box-sizing</b>: <b>border-box</b>;
<b>width</b>: 100<b>vw</b>;
<b>height</b>: 100<b>vh</b>;
<b>grid-template-columns</b>:
[<b>start</b>] <b>minmax</b>(<b>auto</b>, <b>min-content</b>)
[<b>center</b>]1<b>fr </b>
[<b>end</b>] <b>minmax</b>(<b>auto</b>,<b>min-content</b>);
<b>grid-template-rows</b>:
[<b>header</b>]2<b>em </b>
[<b>content</b>]1<b>fr </b>
[<b>footer</b>]2<b>em</b>;
<b>grid-gap</b>: 1<b>px</b>;
<b>background-color</b>: <b>black</b>;
}
现在,网格列将始终足够宽以容纳使用其最小宽度的任何列中的任何内容。因此,如果列的一部分(例如标题)比其他部分更宽,则列将扩展以容纳所有部分。如果它们变得更窄或完全消失,则列将相应地调整。从本质上讲,我们已经复制了 Flexbox 的扩展/收缩行为,但使其与列中的所有内容一起工作,而不仅仅是一个项目。这就是真正的二维布局。
以下是演示其余部分的代码。
.<b>start </b>{
<b>grid-column</b>: <b>start</b>;
}
.<b>center </b>{
<b>grid-column</b>: <b>center</b>;
}
.<b>end </b>{
<b>grid-column</b>: <b>end</b>;
}
<b>header </b>{
<b>grid-row</b>: <b>header</b>;
}
<b>footer </b>{
<b>grid-row</b>: <b>footer</b>;
}
.<b>sidebar </b>{
<b>overflow</b>: <b>auto</b>;
}
<<b>div </b><b>id=</b><b>"grid"</b>>
<<b>header </b><b>class=</b><b>"start"</b>>header</<b>header</b>>
<<b>header </b><b>class=</b><b>"center"</b>>
<<b>button </b><b>id=</b><b>"toggle-left"</b>>toggle left</<b>button</b>>
...
</<b>header</b>>
<<b>header </b><b>class=</b><b>"end"</b>>header</<b>header</b>>
<<b>div </b><b>class=</b><b>"start sidebar"</b>>sidebar</<b>div</b>>
<<b>div </b><b>class=</b><b>"center content"</b>>the center content</<b>div</b>>
<<b>div </b><b>class=</b><b>"end sidebar"</b>>
sidebar<<b>br</b>/>
...
</<b>div</b>>
<<b>footer </b><b>class=</b><b>"start"</b>>left footer</<b>footer</b>>
<<b>footer </b><b>class=</b><b>"center"</b>>center footer</<b>footer</b>>
<<b>footer </b><b>class=</b><b>"end"</b>>right footer</<b>footer</b>>
</<b>div</b>>
为了使上部标题中的切换按钮实际上隐藏侧边栏,我添加了这段代码。请注意,使用现代 DOM API 和箭头函数,我们可以用几行代码来复制 JQuery
<b>const </b><i>$ </i>= (selector) => <b><i>document</i></b>.querySelector(selector)
<b>const </b><i>$$ </i>= (selector) => <b><i>document</i></b>.querySelectorAll(selector)
<b>const </b><i>on </i>= (elem, type, listener) => elem.addEventListener(type,listener)
<i>on</i>(<i>$</i>(<b>'#toggle-left'</b>),<b>'click'</b>,()=>{
<i>$$</i>(<b>".start"</b>).forEach((elem) => elem.classList.toggle(<b>'closed'</b>))
})
<i>on</i>(<i>$</i>(<b>'#toggle-right'</b>),<b>'click'</b>,()=>{
<i>$$</i>(<b>".end"</b>).forEach((elem) => elem.classList.toggle(<b>'closed'</b>))
})
还要注意,CSS 网格不会使 Flexbox 过时。我们仍然在 Flexbox 有意义的情况下使用它:即一维内容,例如工具栏。以下是我用于由标题组成的工具栏的样式
<<b>header </b><b>class=</b><b>"center"</b>>
<<b>button </b><b>id=</b><b>"toggle-left"</b>>toggle left</<b>button</b>>
<<b>button</b>>open</<b>button</b>>
<<b>button</b>>save</<b>button</b>>
<<b>span </b><b>class=</b><b>"spacer"</b>></<b>span</b>>
<<b>span</b>>filename.txt</<b>span</b>>
<<b>span </b><b>class=</b><b>"spacer"</b>></<b>span</b>>
<<b>button</b>>delete</<b>button</b>>
<<b>button </b><b>id=</b><b>"toggle-right"</b>>toggle right</<b>button</b>>
</<b>header</b>>
<b>header </b>{
<b>background-color</b>: <b>#ccc</b>;
<b>display</b>: <b>flex</b>;
<b>flex-direction</b>: <b>row</b>;
}
.<b>spacer </b>{
<b>flex</b>: 1;
}
spacer
类使元素占用所有额外的空间。通过在按钮之间使用两个间隔符,我可以根据需要使工具栏缩小和扩展,而文件名始终位于中间。这类似于原生工具栏。
您可以在这个Codepen 中尝试实时演示,然后对其进行重新混合以进行测试。
查看 Pen 使用 CSS 网格布局 UI,作者:Josh Marinacci (@joshmarinacci) 在 CodePen 上。
CSS 网格非常适合设计具有二维复杂性的交互式应用程序。我们可以保持标记语义。面板和工具栏正确对齐。grid-gap
为我们提供自动边框。它以复杂的方式调整我们的布局,无需任何 JavaScript 代码,并且使我们能够控制水平和垂直方向。我们可以在不使用重量级 CSS 框架的情况下完成所有这些操作。
Jen Simmons 启动了一个新的 YouTube 频道,Layout Land,帮助您了解网格的工作原理。 如果您从事 Web 应用程序或任何类型的富交互式网站开发,您应该尝试使用 CSS 网格。
关于 Josh Marinacci
我是一名作家、研究员和正在康复的工程师。以前曾在 Sun 的 Swing 团队、Palm 的 webOS 团队和诺基亚研究中心工作。我传播良好的用户体验。我和我的妻子和天才乐高建造者孩子住在阳光明媚的俄勒冈州尤金市。
17 条评论