使用 CSS 网格布局 UI

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 团队和诺基亚研究中心工作。我传播良好的用户体验。我和我的妻子和天才乐高建造者孩子住在阳光明媚的俄勒冈州尤金市。

更多 Josh Marinacci 的文章…


17 条评论

  1. Hayk Saakian

    您认为 CSS 网格会使 Flexbox 过时吗?

    我看到越来越多的关于设计师从 Flexbox 转移的消息,就在 Flexbox 在浏览器中获得主流支持的时候

    2018 年 2 月 14 日 下午 11:59

    1. Josh Marinacci

      不会。网格和 Flexbox 在不同的方面都很出色。它们都将存在很长时间。

      2018 年 2 月 22 日 下午 10:02

  2. Alberto

    太棒了,太可惜 IE11 不支持它了 :(

    2018 年 2 月 15 日 下午 2:26

    1. Josh Marinacci

      IE11 支持较早版本的网格。它可能适合您的需要。
      https://gridbyexample.com/browsers/

      2018 年 2 月 22 日 下午 10:05

  3. llama

    如果这是一个简单的问题,请原谅我,但我尝试将您的 codepen 代码分叉到一个 github 页面,并遇到了以下问题

    08:14:08.410 TypeError: elem 为 null 1 code.js:3:41
    https://[url]/code.js:3:41
    https://[url]/code.js:5:4

    来自您的 codepen 的 javascript 代码位于 code.js 中,并使用简单的 script 标签进行引用。我还包含了 jQuery 库,认为它可能是由于缺少依赖项造成的。

    感谢您提供的任何帮助!我非常喜欢这个布局,尤其是使用最小宽度约束来确定侧边栏大小。

    2018 年 2 月 21 日 上午 5:17

    1. Josh Marinacci

      您能告诉我更多关于该问题的信息吗?您是尝试将 codepen 中的脚本直接从网站放入另一个网站吗?

      2018 年 2 月 21 日 下午 2:00

      1. llama

        抱歉,我忘了澄清我是如何出现错误的。当我尝试点击侧边栏切换按钮(页脚上的箭头)时,它就会弹出。切换功能无法正常工作,并且我在控制台中看到了我引用的错误

        08:14:08.410 TypeError: elem 为 null 1 code.js:3:41
        https://[url]/code.js:3:41
        https://[url]/code.js:5:4

        code.js 的第 3 行是

        const on = (elem, type, listener) => elem.addEventListener(type,listener)

        是的,我从 codepen 中提取了代码:将 HTML 放入 index 页面,将 CSS 放入 .css,将 JS 放入 .js,并将所有内容成功导入到 HTML 页面中,并使用必要的链接和 script 标签。我检查了您在该笔上的设置,发现您也没有包含任何外部库(jQuery、jQueryUI 等),但我还是添加了一个对 jQuery 的包含,以防万一。仍然不行。

        2018 年 2 月 22 日 上午 5:43

        1. Josh Marinacci

          您不需要 jQuery 或任何其他外部库。示例提供了自己的 $ 函数来执行选择器。错误看起来像是“on”函数尝试使用空元素。请检查您的选择器是否正确。您是否在 on($(“#foo”),’type’,()=>()) 中拼错了选择器?

          2018 年 2 月 22 日 下午 10:08

  4. öküz”

    完美

    2018 年 2 月 22 日 上午 5:15

  5. 大卫

    我想看看截图中出现的那个游戏 :) 让我想起了 Windows 95 的 skifree。

    2018 年 2 月 22 日 下午 3:54

    1. Josh Marinacci

      哈哈。这是游戏。 https://9bit.io/xmas2017/

      2018 年 2 月 22 日 下午 10:09

      1. 大卫

        太棒了!非常感谢。我真的认为布局机制应该像用于游戏开发一样被构思。塞尔达传说中没有网页中不存在的东西,一个通往过去的链接。

        2018 年 2 月 23 日 上午 6:51

  6. 匿名

    我不确定“模拟” jQuery 是一种好做法。

    2018 年 2 月 22 日 下午 4:44

  7. 卡米洛·雷耶斯

    好文章,谢谢!我花了很长时间研究 CSS Grid,这是我第一次“点击”。二维布局与 Flexbox 的一维布局相比,太棒了!

    TBF,“模拟” jQuery 还可以。我宁愿增加一点体重,比如两三行函数式编程,也不愿增加 50KB 的依赖项。

    2018 年 2 月 25 日 上午 11:13

  8. 罗伯特

    不错!

    2018 年 2 月 25 日 下午 4:45

  9. secretboot

    我有一个 bootstrap。简单的网页是用 bootstrap 制作的。我不是设计师。你真的需要学习 CSS 网格布局吗?谁需要网格布局?谁需要 bootstrap?

    2018 年 3 月 1 日 下午 10:07

    1. Josh Marinacci

      使用 CSS 网格,对 bootstrap 的需求要少得多。你只需很少的代码就可以构建复杂的布局。

      2018 年 3 月 2 日 上午 11:38

本文评论已关闭。