我每天都在 Shopify 与 合作伙伴 交谈,他们不断突破电商设计领域的界限。最近,我注意到许多设计师都在他们的商店中尝试使用 Flexbox。作为网页设计师和开发人员,我们的主要目标之一是将重点放在内容上,并让访问者轻松浏览这些内容。为了实现这一目标,我们需要一个有效的布局,让技术退居幕后,让内容成为主角。
Flexbox 可以帮助我们创建灵活的布局,这些布局针对网页和移动设备进行了优化。但我们真的在使用它吗?我们中的许多人仍然在使用浮动和内联块进行布局。当然,您最了解您的受众,因此,如果您有大量用户使用 IE 9 及更低版本,并且没有准备创建可接受的回退体验,您可能会被困在浮动世界中。但是,如今 Flexbox 已经获得了大量的支持(支持)。
我相信实践出真知的理念。本文将带您了解最近发布的名为 Venture 的免费 Shopify 主题,以及使用 Flexbox 重新创建以下产品布局的步骤。

如果您想在本文中使用示例,请 查看完整的 Pen。
在本教程文章中,我将演示如何使用 Flexbox 创建灵活且响应式的产品布局,该布局将根据视窗宽度以独特的方式将重点放在产品上。此外,我们将在不到 100 行 CSS 代码内完成所有操作。
这是基于“Venture”主题的
Shopify 的主题设计团队最近为 Shopify 商家发布了一个非常棒的模板,名为 Venture。该布局针对最佳购物体验进行了优化,并清晰地突出了产品。虽然该布局是为适应多种业务案例而开发的,但在此教程示例中,我们将重点关注布局的核心,并使用 Flexbox 重新创建它。在以下步骤中,我们将学习如何水平居中对齐元素、设置完美的粘性页脚、根据视窗和设备优先显示某些产品、使用媒体查询定位 Flexbox 元素,以及学习有关 Flexbox 的基础知识,以便您可以在下一个 Web 项目中开始实施 Flexbox 布局。

如果您想在 Shopify 商店(包含真实产品)上使用本文中的代码示例,请注册成为 Shopify 合作伙伴并设置一个免费的开发商店。开发商店为您提供了访问 Shopify 商店所有付费功能的权限,因此您可以将其用作沙盒环境来进行实验或为客户制作主题。
页眉布局
我们要做的第一件事是设置我们的筛选导航,其中包含我们的标题和两个带有标签的筛选元素(下拉菜单)。

让我们从设置一个带有其内容的 Flexbox 容器开始。
<nav class="product-filter">
<h1>Jackets</h1>
<div class="sort">
<div class="collection-sort">
<label>Filter by:</label>
<select>
<option value="/">All Jackets</option>
</select>
</div>
<div class="collection-sort">
<label>Sort by:</label>
<select>
<option value="/">Featured</option>
</select>
</div>
</div>
</nav>
我们的 .product-filter
将成为我们的 Flex 容器,因此我们可以相应地对齐 Flex 项目子元素。我们如下声明 Flex 容器:
.product-filter {
display: flex;
}
我们的 <h1>
元素的 flex-grow
值为 1,因此它既扩展 Flex 容器以使其充满整个宽度,又扩展自身以充满剩余空间(这会将排序下拉菜单右对齐)。
.product-filter h1 {
flex-grow: 1;
}
要水平对齐 .sort container
的子元素,我们也将其设置为 Flex 容器。您可以嵌套 Flex 容器!
.sort {
display: flex;
}
排序容器(<div>
)默认情况下会堆叠在一起。

默认情况下,display: flex;
会水平对齐子元素。我们将对排序容器使用它来水平对齐它们。我们还将使每个单独的排序容器成为 Flex 容器(第三个嵌套 Flex 容器!),并也使用 flex-direction: column;
来对筛选器进行垂直对齐。
.collection-sort {
display: flex;
flex-direction: column;
}

在几行 CSS 代码中,我们的标题和筛选器就以我们想要的方式设计好了。现在,凭借我们目前对 Flexbox 的了解,我们将着手设计产品的网格布局。
产品布局
我们将使用以下 HTML 来创建 Flexbox 布局。
<section class="products">
<!-- It's likely you'll need to link the card somewhere. You could add a button in the info, link the titles, or even wrap the entire card in an <a href="..."> -->
<div class="product-card">
<div class="product-image">
<img src="assets/img/coat-01.jpeg">
</div>
<div class="product-info">
<h5>Winter Jacket</h5>
<h6>$99.99</h6>
</div>
</div>
<!-- more products -->
</section>

与之前一样,我们需要一个 Flex 容器。在本例中,我们将使用 .products
作为我们的 Flex 容器。我们将添加两个新属性,这些属性将允许 Flex 子元素水平对齐,并在视窗宽度扩展和收缩时换行到新行。
.products {
display: flex;
flex-wrap: wrap;
}
默认情况下,display: flex;
会从左侧开始水平排列子元素,但我们添加了 flex-wrap: wrap;
,以便在视窗宽度不足以容纳同一行上的所有元素时,将子元素换行到新行。
要开始控制 Flex 项目的宽度,我们将添加 flex: 1;
,以便所有 Flex 项目在行中占据相同的空间。在我们的示例中,我们有 10 件夹克产品,添加 flex: 1;
将使所有产品都排列在一行上。

对于我们的设计,我们希望每行 5 个项目,并根据需要将剩余项目换行到新行。要实现每行 5 个项目,它们的宽度需要为 20%(5 * 20 = 100)。设置 flex-basis: 20%
可以解决问题,但当我们考虑填充时,它会超过 100%,我们只会得到每行 4 个项目。使用 2% 的两侧填充和 16% 的 flex-basis
则刚刚好。
.products {
display: flex;
flex-wrap: wrap;
}
.product-card {
padding: 2%;
flex-grow: 1;
flex-basis: 16%;
display: flex; /* so child elements can use flexbox stuff too! */
}
我们也可以使用以下简写形式来实现:
.product-card {
flex: 1 16%;
}
为产品设置 flex-grow
为 1 将确保产品行始终填满整个空间。
要确保内部图像完美地拟合:
.product-image img {
max-width: 100%;
}
底部对齐产品页脚
有时设置固定页脚或将内容设置为容器底部可能会很棘手。Flexbox 也可以帮助我们解决这个问题。如果您一直在逐步关注代码,您会发现我们的夹克标签没有完全对齐到夹克图像下方。

这种情况很常见:我们无法控制内容的高度或长度,但希望另一个元素完全位于容器底部。Flexbox 已经帮助我们保持容器本身在每行具有相同的高度,但图像仍然具有可变高度。
底部对齐的常用方法可能需要使用绝对定位甚至 JavaScript。幸运的是,使用 Flexbox,只需将以下 CSS 添加到我们的 .product-info
容器即可完成这项复杂的任务。
.product-info {
margin-top: auto;
}
就是这样!Flexbox 足够聪明,可以将元素放置到 Flex 容器的底部。通过几行样式,我们获得了以下结果。

响应式 Flexbox
随着可用的水平空间越来越少,我们希望减少每行的产品数量。例如,如果最大视窗为 920 像素,我们希望每行的项目数量限制为 4 个,我们可以通过以下方式实现。
@media (max-width: 920px) {
.product-card {
flex: 1 21%;
}
}
(请记住,它不是 25%(100% / 4),因为我们必须对之前添加的填充进行补偿。我们可以使用 box-sizing: border-box 来避免这种情况,但这取决于您自己的选择。)
之前的 CSS 代码几乎给我们带来了想要的结果,因为我们得到了每行 4 个项目。但是,最后一行有 2 个大项目。

Flexbox 足够聪明,可以填补任何可用空间,这在我们使用其他布局方法时不必担心。为了改善此视窗的布局,我们更希望在顶部而不是底部显示更大的夹克图像,以更好地突出显示产品。
我们可以将第一个而不是最后两个变大的一种方法是选择它们并直接更改其大小。
/* Select the first two */
.products .product-card:first-child,
.products .product-card:nth-child(2) {
flex: 2 46%;
}
现在,应用了 CSS 后,我们获得了非常棒的布局,该布局针对较小的视窗(例如纵向模式下的 iPad)进行了优化。

对于更小的视窗,我们更希望使用两列布局来显示夹克。我们可以使用以下媒体查询来实现这种布局。
@media (max-width: 600px) {
.product-card {
flex: 1 46%;
}
}

如果我们现在在较小的视窗(例如 iPhone 6)上查看我们的页面,我们会发现筛选导航栏与我们的标题重叠。

出现这种情况的原因是我们的.product-filter
被设置为将所有 flex 项目包含在水平线上,无论它包含多少个项目(不换行)。使用以下代码,我们可以通过媒体查询轻松改变这一点,使我们的内容垂直排列。
@media (max-width: 480px) {
.product-filter {
flex-direction: column;
}
}

我们的标题和过滤器不再重叠,但我们可以通过将过滤器浮动到左侧进一步改进布局。之前,我们使用align-self: flex-end;
属性将元素浮动到右侧。现在,我们想要添加align-self: flex-start;
。
@media (max-width: 480px) {
.product-filter {
flex-direction: column;
}
.sort {
align-self: flex-start;
}
}

就这样,我们现在拥有了产品灵活且响应式的布局。
兼容性
Flexbox 最大的阻力始终是浏览器支持。但正如我们在这篇文章开头提到的,现在的支持已经相当不错了。不再支持 Flexbox 的旧 IE 版本甚至已经不再被微软支持。
就像你参与的每一个网页项目一样,你应该始终进行彻底的测试,以确保你的访问者体验优化,并且你的布局满足你的项目需求。
总结
在上面的教程中,我们使用 flexbox 为显示一组产品构建了一个强大的响应式布局。与其他并非旨在构建布局的 CSS 方法不同,flexbox 是一款强大的工具,专注于此目标,你应该充分利用它。Flexbox 布局可以使我们的网站和应用程序更加灵活和稳健。
关于 flexbox,还有很多东西需要了解,所以一定要参考Flexbox 完整指南。
不错的文章,感谢,但我更喜欢使用 box sizing border box,而不是将填充考虑在我的计算中。
感谢 Ado 的反馈,这是一个很好的观点!
我尝试过这样做,但存在一个明显的 IE 错误,我相信它也存在于更新的、受支持的浏览器中,其中 box-sizing 没有考虑在布局中。最终结果是内容超出 flex 容器的范围。糟糕 :(
鉴于 Flexbox 在 IE9 中不受支持,你认为在 IE9 的全球使用率不到 1% 的情况下,提供 IE9 的回退是否仍然是最佳实践?
https://caniuse.cn/#search=flexbox.
我认为为旧浏览器提供回退始终是最佳实践,但我个人不会花费太多时间来支持 IE9。微软已正式停止对 IE8、IE9 和 IE10 的支持:http://thenextweb.com/microsoft/2016/01/05/web-developers-rejoice-internet-explorer-8-9-and-10-die-on-tuesday/
你尝试过使用 Flexibility 来处理 IE 兼容性吗?
Levin,为仍然支持这些旧 IE 的人更新兼容性部分是否有价值,甚至是否可能?
https://github.com/10up/flexibility
嗨,Jonathan,我还没有尝试过 Flexibility,但它看起来确实是一个不错的替代方案。有关 Flexbox 兼容性支持的详细信息,请访问:https://caniuse.cn/#feat=flexbox
感谢阅读 :)
我们现在还有来自 Jon Neal 的这个!https://github.com/10up/flexibility
非常酷,感谢分享这个资源!
product-card 项目的顶部和底部填充在 Firefox 中不起作用。根据你相信的人,这要么是 Firefox 的错误,要么是其他所有浏览器的错误。:)
从技术上讲,如果顶部和底部填充设置为 %,则它是根据容器的高度计算的,而不是根据容器的宽度计算的。由于容器没有固定高度,因此这些值被设置为 0。
作为一种解决方法,你可以使用任何其他单位设置顶部和底部填充。
感谢分享这个见解,Richard,在排查 Firefox 的问题时肯定会有用 :)
你在代码演示中忘记将
<label>
与<select>
关联。感谢你的反馈!
你将前两个元素放大的部分之所以有效,是因为你在此示例中只有特定的项目数量,对吧?那么,有没有办法在不使用 JS 的情况下对其进行泛化?
嗨,Mark,无论你的 flexbox 容器中有多少个项目,你都可以放大前两个元素。要详细了解它是如何工作的,我建议你研究 flex-grow 属性。更多信息请查看:https://css-tricks.org.cn/snippets/css/a-guide-to-flexbox/ :)
好文章。
但我不同意浏览器支持的说法。现在 Flexbox 本身已经得到很好的支持了。但一旦你需要使用换行,情况就大不相同了。例如,Safari 6 及更早版本(在 OS 和 iOS 上)不支持换行,而 4.3 及更早版本的标准 Android 浏览器也不支持。旧的 Mac OS 根本无法更新他们的 Safari。不幸的是,当你的代码考虑到换行时,没有换行的结果看起来通常比完全不支持 Flexbox 更糟糕。
我认为这是一个值得一提的事情。
这是一个很好的观点,René,我们应该始终确保为旧浏览器提供良好的回退。在我们将资源投入到支持特定浏览器之前,查看市场份额也很重要。
感谢阅读和你的反馈!
值得一提的是,flexbox 的支持比简单的“是”或“否”更细致。
值得注意的是,
flex-wrap
有一个问题多多的支持矩阵,在4.4
之前的 Android 浏览器中不支持。我不确定,但由于Flexability 使用微软的专有
currentStyle
来分析 flexbox css,所以我假设它不能解决 Android 问题。感谢你的反馈,我不确定 Flexability 是否支持 4.4 之前的 Android 浏览器。这绝对是需要研究的事情。我们是否有关于 Android 浏览器的市场份额的可靠数据?如果有任何资源,请分享一下。
谢谢!
根据caniuse,4.3 之前的 Android 版本的全球使用率为 1.66%。
太好了,感谢提供这些信息。我不确定这些数据。
TxHawks:关于 4.4 之前的 Android 版本的使用情况,我认为检查 Google 的统计数据比 Can I use 更重要。根据这些统计数据,从 4.0 到 4.3(包括)的所有 Android 版本的使用率占所有 Android 版本的 25% 以上。
Modernizr v3 现在包括一个可选的测试,用于 flexboxtweener 和 flex 行换行 - 这将有助于避免所有(我认为)与 flex-wrap 相关的问题。还有一些可用的解决方法:https://github.com/philipwalton/flexbugs
个人而言,我会尝试逐步增强,而不是为所有浏览器提供可能成为回退的功能。使用现代浏览器的用户应该因为他们的合规性而获得奖励:)
我有两件事要说
很棒的文章,我绝对希望看到你更多的文章
去死吧 IE
谢谢 Julio!:)
第一篇很棒的文章,我会用它来创建我自己的商店,但使用 woocomerce。
@Julio:没错。当我在为客户工作时,我会确保提供回退,但由于这将是我的个人项目,并且我的目标群体是了解 IE 应该只用于下载其他浏览器(更具 Web 特色)的一群人,因此我将是安全的。
再次感谢你为我提供了这篇很棒的文章,我一直都在寻找这样的文章。
此致敬礼。
很高兴听到你发现这篇文章有用!如果你有一天决定尝试 Shopify,请与我联系:)
我在我构建的电子商务网站上许多部分使用 flexbox 布局,包括产品。我们必须支持 IE9,因此我有一个使用内联块布局的
.no-flexbox
回退。感谢你的见解,Alex,这是现实世界中逐步增强的一个很好的例子。
不知道你们是否测试过,但我对移动设备上的
flex-direction: column
有过一些奇怪的体验。它发生在我参与的两个项目中。当我在浏览器中测试它们时,一切都很正常。但当我用移动设备打开项目时,它们就崩溃了。
为了不丢失我的所有工作,我不得不做一些棘手的事情,那就是对我想以列显示的元素使用
flex-wrap: wrap
和flex-basis: 100%
。我希望没有人需要使用这个技巧,但它是我找到解决问题的方法:)
嘿,Guilherme,
你有为你代码设置的 CodePen 吗?看到你的实际代码会很有趣。感谢分享!
真尴尬……我创建了一个 CodePen 只是为了展示我所说的内容,测试它,它居然运行了!哈哈
所以……不用管它了。可能是那天我的代码出了问题。
无论如何,还是要感谢你!:D
听到这个消息真是太好了!:)
很棒的文章,只有一个问题。当一行中 flex-item 的总和是 100%(“固定”)并且没有剩余空间时,为什么要使用任何 flex-grow 或 flex-shrink?这里的 flex-basis 不够吗?
没有更多空间供任何“灵活”使用,我们使用 @media 元素将 4 列布局调整为 3 列布局,然后调整为 2 列布局,但我们从不使用 flex-grow 或 flex-shrink,因为我们始终使用行 的 100% 空间。
嗨,Oliver,感谢你阅读并提出问题。我建议你使用 flex-grow 项目来确定 flex 容器的宽度,因为这将为你使用 @media 查询提供更大的灵活性。你有没有设置 CodePen,让我们看看你的代码示例?
我还建议你查看以下资源,尝试使用 Flexbox 进行大小调整:http://the-echoplex.net/flexyboxes/
哦,我多么希望能为一家允许我这样说的公司工作:“IE 10 及之前的版本已被微软停用,我们不再需要支持它。” 我参与的每个项目中,都要求必须支持 IE 9 和 10。
也许我终于可以在两年左右的时间里正确使用 Flexbox(带包装)了。
嗨,Ed,对于企业项目来说尤其如此,因为计算机升级速度较慢。话虽如此,许多企业机构现在正在从台式机迁移到平板电脑,以便团队能够更加移动。祝你好运,也许 Flexbox 可以帮助你支持 IE 9 和 10:https://github.com/10up/flexibility
将产品信息移到容器底部时,你的方法与使用 flex-end 之间有什么区别吗?
嘿,Dan,如果你使用我上面提到的方法或 flex-end,没有区别。使用 flexbox 可以用多种方法来完成同样的事情。感谢你提出了这个非常重要的观点!
我尝试过使用 flexbox 来显示产品,但我不喜欢它处理最后一行的方式,如果你不知道要显示多少产品。如果你有一个网格,每行 4 个项目,总共 10 个项目,最后一行将有两个项目。理想情况下,最后两个项目将与上面行中的项目具有相同的宽度,但会左对齐。
使用 flexbox 没有简单的方法可以做到这一点。我找到的一个解决方法是,如果你最后一行没有填满,可以用空项目填充剩余的两个“位置”,但这感觉很奇怪,而且要求你根据最后一行是否已填满来输出 HTML。
希望网格 CSS 布局规范能够尽快完成并被浏览器使用。
flex-basis
万岁!!我实际上刚找到一个解决我问题的方案!http://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid/34816625#34816625
不错!
我不知道这可以奏效。
@Derek
我查看了你提供的 jsfiddle 中的代码示例(通过你的 stackoverflow 链接),并提出了以下解决方案(基于你的代码)。
它并不完美,代码肯定存在冗余,但完全使用纯 CSS。
仍然有一些神奇的数字
20% 和 5n 因为有 5 列
margin-rigth 的 20/40/60/80% 用于将 flex-item 推送到位置,具体取决于最后一行是否包含 4/3/2 或仅 1。
但它可以很好地调整大小。
这些数字需要根据所需的列数进行更改(并且需要添加或删除一些代码,具体取决于情况的数量)。
.exposegrid {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.exposetab {
border: 1px solid black; /* 用于使框可见 */// height: 2rem; /* 用于使框可见 */
box-sizing: border-box;
flex: 0 0 20%;
}
.exposetab:last-child:nth-child(5n-1) {
margin-right: 20%;
}
.exposetab:last-child:nth-child(5n-2) {
margin-right: 40%;
}
.exposetab:last-child:nth-child(5n-3) {
margin-right: 60%;
}
.exposetab:last-child:nth-child(5n-4) {
margin-right: 80%;
}
这里有很棒的讨论!
其中一个回复中给出的链接指向了以下有趣的 CodePen
http://codepen.io/DanAndreasson/pen/ZQXLXj?editors=1100
我在文章和 CodePen 之间发现的主要区别是这两个属性
它们不能很好地组合使用,因此你需要选择其中一个。
但是,你认为这个 CodePen 仍然被视为 flexbox 解决方案吗?
是的,它们都是 flexbox 解决方案。每种方法都将为你提供不同的方式来操作 flex-item,例如用于响应式设计,就像我在本文中做的那样。我建议使用你认为最适合你项目的方案。
谢谢!
很棒的文章,已将其收藏。
谢谢:)
* 错误
“我们可以放大第一个,而不是最后两个”
应该是
“我们可以放大前两个,而不是最后两个”
*/错误
感谢你阅读并反馈错别字!:)
这篇文章很棒,展示了实际的布局需求。我特别喜欢“:first-child,:nth-child(2)”技巧。
这是使用我们的 ngMaterial UI 组件和 Layout API 的 Angular Material 版本:http://codepen.io/team/AngularMaterial/pen/YweVWZ
哇,太棒了!感谢分享!
这意味着 Shopify 的主题商店不再要求主题支持 IE9 吗?
这是一个快速示例,展示了如何使用新的 CSS 方法重新创建布局。主题仍然需要支持 IE 9 :)
我个人很喜欢 Flexbox,但我认为现在最好避免使用它。IE 10 和 11 的支持存在 bug(查看 caniuse 以确认)。如果你尝试做一些稍微高级的事情,这会给你带来麻烦。
感谢你的反馈 Nikk。在实施新工具时,了解目标受众始终很重要。再次感谢。