在本教程中,我们将拍摄一堆照片并将它们转换为可过滤的缩略图布局。我们将结合所有最新的 css 好东西(CSS Grid、flexbox 和 CSS 变量)以及一些自定义 javascript 来构建一个惊人的演示!
这是我们将要创建的内容:
一定要在大屏幕(>900px) 上查看演示,因为那时魔法就会发生!事不宜迟,来杯咖啡,让我们开始吧!
1.从页面标记开始
我们将从.container
包含.toolbar
元素和照片列表的 a 开始:
<div class="container"> <div class="toolbar">...</div> <ol class="image-list grid-view">...</ol> </div>
工具栏布局将如下所示:
在其中我们将放置两个元素:
允许我们搜索特定照片的搜索框
包含确定缩略图布局的三个选项的列表。默认情况下,照片显示在网格视图中,但我们可以通过单击右上角的图标切换到列表视图。此外,每次我们在网格视图中时,我们都可以选择更改每行显示的照片数量。为此,我们将使用范围滑块。
这是所有这些的相关标记:
<div class="search-wrapper"> <input type="search" placeholder="Search for photos"> <div class="counter"> Total photos: <span>12</span> </div> </div> <ul class="view-options"> <li class="zoom"> <input type="range" min="180" max="380" value="280"> </li> <li class="show-grid active"> <button disabled> <img src="IMG_SRC" alt="grid view"> </button> </li> <li class="show-list"> <button> <img src="IMG_SRC" alt="list view"> </button> </li> </ul>
在图像列表中,我们将放置 12张Unsplash照片。每张照片都附有其描述以及所有者的姓名。以下是一些基本样式的外观(我们很快就会谈到):
这是单张照片的标记:
<li> <figure> <img src="IMG_SRC" alt=""> <figcaption> <p>...</p> <p>...</p> </figcaption> </figure> </li> <!-- 11 list items here -->
请务必注意,图像列表将始终包含image-list
该类。此外,它还将接收grid-view
或 list-view
类,如下所示: <ol class="image-list grid-view">...</ol>
它的第二类将取决于用户选择的布局视图。在接下来的部分中会对此进行更多介绍。
2. 定义一些基本样式
我们首先设置了一些 CSS 变量和一些重置样式:
:root { --black: #1a1a1a; --white: #fff; --gray: #ccc; --darkgreen: #18846C; --lightbrown: antiquewhite; --darkblack: rgba(0,0,0,0.8); --minRangeValue: 280px; } * { margin: 0; padding: 0; outline: none; border: none; } button { cursor: pointer; background: none; } img { display: block; max-width: 100%; height: auto; } ol, ul { list-style: none; } a { color: inherit; }
最重要的是,注意minRangeValue
变量的值。它的值 (280px) 与范围滑块的默认值相匹配。
记住我们滑块的标记: <input type="range" min="180" max="380" value="280">
. 稍后我们将使用该值来设置照片的最小宽度。
注意:为简单起见,我不会 在教程中介绍所有CSS 规则。您可以通过单击 演示项目的CSS选项卡来检查其余部分。
样式化工具栏
下一步,我们将为工具栏设置样式。以下是有关此元素的要点:
我们使用 flexbox 来布局它的内容。
每次用户选择布局(网格或列表)时,相应的按钮都会标记为活动并接收深绿色边框。此外,我们禁用它。
范围滑块仅在网格视图处于活动状态以及最小宽度为 901 像素的屏幕上出现。
对应样式的重要部分如下图所示:
/*CUSTOM VARIABLES HERE*/ .toolbar { display: flex; justify-content: space-between; align-items: center; } .view-options { display: flex; align-items: center; } .view-options li:not(:last-child) { margin-right: 1.2rem; } .view-options button { padding: 2px; border: 3px solid transparent; } .view-options .active button { border-color: var(--darkgreen); } @media screen and (max-width: 900px) { .toolbar input[type="range"] { display: none; } }
样式化图像列表
如前所述,图像列表布局将取决于用户选择的布局。无论如何,我们将利用 CSS Grid 来构建这个布局。
在此之前,让我们将一些通用样式应用于图像列表:
/*CUSTOM VARIABLES HERE*/ .image-list { margin: 3rem 0; } .image-list li { background: var(--lightbrown); background: var(--darkblack); } .image-list img { background: #e6e6e6; } .image-list p:first-child { font-weight: bold; font-size: 1.15rem; } .image-list p:last-child { margin-top: 0.5rem; }
在网格视图中,列表项将被分成重复的列/单元格,它们之间有排水沟:
每行显示的项目数取决于屏幕大小。最初,每个项目的最小宽度为 280 像素,最大宽度与其容器宽度相匹配。但正如我们稍后会看到的,我们将添加一点交互性,并让用户可以选择修改最小宽度。出于这个原因,我们不会对其值进行硬编码,而是将其存储在minRangeValue
变量中。
minmax()
通过将CSS 函数与 CSS Grid相结合,我们能够生成这种真正响应式的布局 。以下是上述要求在样式方面的翻译方式:
:root {
--minRangeValue: 280px;
}
.grid-view {
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(var(--minRangeValue), 1fr));
}
在列表视图中,列表项将具有默认的块级行为:
在它们内部,图像将具有 150 像素的固定宽度,并且其描述将覆盖剩余的可用空间 (1fr)。另外,这两个元素将垂直居中,它们之间有一个排水沟。
相关样式:
.list-view li + li { margin-top: 1.5rem; } .list-view figure { display: grid; grid-gap: 1.5rem; grid-template-columns: 150px 1fr; align-items: center; }
要了解有关如何minmax()
使用 CSS 网格布局的更多信息,这里有一个初学者教程:
3. 在列表视图和网格视图之间切换
每次用户单击所需的内容呈现模式时,我们会做两件事:
为活动按钮添加深绿色边框。
检查用户选择了哪种类型的视图。
如果用户选择网格视图,我们会显示范围滑块并确保图像列表包含grid-view
类而不是list-view
类。
另一方面,如果用户选择列表视图,我们隐藏范围滑块并确保图像列表包含list-view
类而不是grid-view
类。
所需的 JavaScript 代码:
const imageList = document.queryselector(".image-list"); const btns = document.querySelectorAll(".view-options button"); const imageListItems = document.querySelectorAll(".image-list li"); const active = "active"; const listView = "list-view"; const gridView = "grid-view"; const dNone = "d-none"; for (const btn of btns) { btn.addeventListener("click", function() { const parent = this.parentElement; document.querySelector(".view-options .active").classList.remove(active); parent.classList.add(active); this.disabled = true; document.querySelector('.view-options [class^="show-"]:not(.active) button').disabled = false; if (parent.classList.contains("show-list")) { parent.previousElementSibling.previousElementSibling.classList.add(dNone); imageList.classList.remove(gridView); imageList.classList.add(listView); } else { parent.previousElementSibling.classList.remove(dNone); imageList.classList.remove(listView); imageList.classList.add(gridView); } }); }
4. 通过 javaScript 更新 CSS 变量
我们已经讨论过范围滑块的初始值为 280px 并且与 minRangeValue
变量的值相匹配。另外,它的最小值是 180px,而最大值是 380px。
我们需要跟踪滑块值的变化并minRangeValue
相应地更新变量。这将使我们的网格视图布局灵活,因为每行不包含固定数量的列。
实现这一目的的 JavaScript 代码利用了该input
事件:
const rangeInput = document.querySelector('input[type = "range"]'); rangeInput.addEventListener("input", function() { document.documentElement.style.setProperty("--minRangeValue", `${this.value}px`); });
要了解此代码,请打开浏览器工具并更新滑块值。你会注意到html
元素接收到一个内联样式,它覆盖了通过 CSS 设置的属性值:
5. 构建搜索功能
目前我们只有 12 张图像,但是想象一个我们有几十张图像的场景。在这种情况下,如果用户能够搜索特定的照片,那就太好了。
所以,让我们继续构建一个自定义搜索组件,如下所示:
请注意,它仅适用于图像描述:
作为第一步,我们执行以下操作:
遍历所有照片。
对于我们找到的每张照片,我们用两个属性初始化一个对象字面量。
第一个属性是
id
具有每个对象唯一的增量编号。第二个属性是text
存储目标照片描述的属性。将所有对象存储在一个数组中。
实现此功能的 JavaScript 代码:
const captions = document.querySelectorAll(".image-list figcaption p:first-child"); const myArray = []; let counter = 1; for (const caption of captions) { myArray.push({ id: counter++, text: caption.textContent }); }
需要注意的一点是,我们的计数器从 1 开始,而不是从 0 开始。我们这样做是故意的,因为这将有助于我们稍后轻松定位所需的元素。
用户输入
接下来,每次用户在搜索输入中键入内容时,我们都会执行以下操作:
隐藏所有照片。
抓住搜索查询。
检查是否存在在其
text
属性值中包含搜索查询的数组元素(对象)。显示满足上述要求的元素。
在屏幕上打印他们的号码。如果没有任何元素打印 0。
所需的 JavaScript 代码:
const searchInput = document.querySelector('input[type="search"]'); const imageListItems = document.querySelectorAll(".image-list li"); const photosCounter = document.querySelector(".toolbar .counter span"); const dNone = "d-none"; searchInput.addEventListener("keyup", keyupHandler); function keyupHandler() { // 1 for (const item of imageListItems) { item.classList.add(dNone); } // 2 const text = this.value; // 3 const filteredArray = myArray.filter(el => el.text.includes(text)); // 4 if (filteredArray.length > 0) { for (const el of filteredArray) { document.querySelector(`.image-list li:nth-child(${el.id})`).classList.remove(dNone); } } // 5 photosCounter.textContent = filteredArray.length; }
这里使用的 CSS 类:
.d-none { display: none; }
注意: 我们可以使用不同的方法来阻止回调函数在用户每次释放键(keyup
事件)时运行。虽然超出了本教程的范围,但一个有效的解决方案可能是使用 Lodash 的 debounce 函数。
提示:如果要进行不区分大小写的搜索,一种简单的解决方案是将 filteredArray
上述步骤 ( ) 中的常量替换为// 3
:
const filteredArray = myArray.filter(el => el.text.toLowerCase().includes(text.toLowerCase()));
结论
就是这样的人!这确实是一个漫长的旅程,但我希望你学到了一些新东西,并且你喜欢我们在这里构建的演示。玩弄它,如果您有任何问题,请告诉我。
- 样式化工具栏
- 样式化图像列表
- 用户输入