为终端用户设计响应式的导航菜单几乎不是一个简单的过程。前端开发人员必须考虑某些参数--如设备断点和可访问性--以创造一个愉快的导航体验。在像React这样的前端框架中,它可能会变得更具挑战性,因为CSS-in-JS往往会变得很棘手。
在这篇文章中,你将学习如何用CSS和React Hooks创建一个响应式的导航条。你可以在这里摆弄源代码并查看实时项目。
- 创建项目
- 了解导航布局
- 设计导航条组件的样式
- 使用媒体查询的响应性
- 利用媒体查询切换导航条视图
[useState](#toggling-the-navbar-view-with-usestate)
前提条件
要继续学习本教程,你需要。
- 对React和React Hooks的基本了解
- 一些CSS知识--特别是Flexbox
现在,让我们建立一个新的React应用程序吧
创建项目
要在CodeSandbox中启动一个新的React项目,请打开一个新的浏览器标签并输入react.new 。这将创建一个初始的React应用程序。
// App.js
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
目前,你的应用程序的默认样式表位于根部,在styles.css 。让我们编辑这个文件,为页面布局赋予我们自己的感觉。
// styles.css
@import url("https://fonts.googleapis.com/css2?family=Karla:wght@200;400&display=swap");
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.container {
max-width: 90%;
margin-right: auto;
margin-left: auto;
padding: 1rem;
}
article h1 {
margin: 1rem 0;
}
上面的规则将Karla设置为文件的主要字体,并包括一个container 类,用于填充和对齐页面上的内容。
完成这些后,让我们用App.js 写一些标记来显示内容。
// App.js
import Navbar from "./components/Navbar";
import "./styles.css";
function App() {
return (
<div>
<Navbar />
<div className="container">
<article>
<h1>What is Lorem Ipsum? </h1>
Lorem Ipsum is simply dummy text of the printing and typesetting industry...
</article>
</div>
</div>
);
}
export default App;
注意第二行的Navbar 组件的导入。我已经通过CodeSandbox的侧边栏创建了Navbar.js 文件,文件路径如下:src/components/Navbar.js 。
这个文件的内容,到目前为止,是组件的默认导出语句,允许你将其导入到App.js 。
// Navbar.js
export default function Navbar() {
return (
<nav>Navigation</nav>
)
}
了解导航布局
我们的目标是创建一个响应式的导航条,最初以水平布局显示导航菜单--ul 元素。在到达移动视口时,菜单会在导航条下重新定位,并跨越屏幕的剩余高度和宽度。
这种布局将通过Flexbox和CSS定位规则的融合来实现。
将后续代码块中的标记写入Navbar.js 。
// Navbar.js
export default function Navbar() {
return (
<nav className="navigation">
<a href="/" className="brand-name">
MacroSoft
</a>
<button className="hamburger">
{/* icon from heroicons.com */}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="white"
>
<path
fillRule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z"
clipRule="evenodd"
/>
</svg>
</button>
<div
className="navigation-menu">
<ul>
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/about">About</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
</div>
</nav>
);
}
上面的标记包括brand-name 、hamburger 图标和navigation-menu ,它们是我们导航条的三个元素。
现在,让我们继续对这个组件进行样式设计。
设计导航条组件的样式
在以下文件路径中为导航条组件创建样式表。src/styles/navbar.css
并将其导入到Navbar.js 。
// Navbar.js
import "../styles/navbar.css"
export default function Navbar() {
return(
{/* navbar markup */}
)
}
我们将从navigation 类开始。
// navbar.css
.navigation {
height: 60px;
width: 100%;
display: flex;
align-items: center;
position: relative;
padding: 0.5rem 0rem;
background-color: #fff;
color: black;
box-shadow: 0 2px 2px 2px rgba(9, 9, 9, 0.23);
}
在这里,我们将导航条的width 设置为100% ,这样它就可以跨越设备的整个宽度。通过使这个元素成为一个灵活的容器,并为其分配一个特定的height 属性,Flexbox让我们使用align-items 属性来使灵活项目垂直居中。
另外,在这个元素上将position 设置为relative ,可以确保任何子元素的position 值都是相对于它而言的。你很快就会看到这个效果。
让我们对brand-name 和navigation-menu 。
// navbar.css
.brand-name {
text-decoration: none;
color: black;
font-size: 1.3rem;
margin-left: 1rem;
}
.navigation-menu {
margin-left: auto;
}
上面代码块中的主要规则是应用于navigation-menu 的margin-left: auto 规则。这将菜单推到最右边,占用了它左边的可用空间。
现在我们可以为navigation-menu 的子元素设置样式。
// navbar.css
.navigation-menu ul {
display: flex;
padding: 0;
}
.navigation-menu li {
// removes default disc bullet for li tags and applies margin to left & right side
list-style-type: none;
margin: 0 1rem;
}
.navigation-menu li a {
// increases the surface area of the anchor tag to span more than just the anchor text
text-decoration: none;
display: block;
width: 100%;
}
display: flex 在 元素上,将其变成一个柔性容器。然后,子 元素被安排在一个 ,这就是 属性的默认值。其他规则的作用是使导航链接看起来更好。ul li row flex-direction
让我们继续用hamburger 类来设计菜单图标的样式。
// navbar.css
.hamburger {
// removes default border on button element
border: 0;
height: 40px;
width: 40px;
padding: 0.5rem;
border-radius: 50%;
background-color: #283b8b;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
// positions the icon to the right and center aligns it vertically
position: absolute;
top: 50%;
right: 25px;
transform: translateY(-50%);
display: none;
}
.hamburger:hover {
background-color: #2642af;
}
在这里,我们使用CSS定位,将菜单图标定位在导航栏的右侧。
请记住,父元素nav 的位置设置为relative 。因此,将图标的position 属性设置为absolute ,我们可以使用top 和transform 属性将图标沿着父元素的边框垂直居中。如果你想知道这是如何工作的,请阅读更多关于CSS定位的内容。
由于我们希望菜单图标在到达移动视口之前保持隐藏,让我们把它的display 属性设置为none ,并继续用CSS媒体查询来设计移动布局。
使用媒体查询的响应性
媒体查询是一个CSS功能,它可以让你指定你的内容布局将如何响应不同的条件--比如视口宽度的变化。
查询是用@media 规则编写的,后面是目标媒体类型和应用样式的中断点。
@media screen and (max-width: 768px) {
// rules go here
}
在这里,max-width: 768px ,确保只有当设备宽度在768px 或更低时才会实施样式。
现在让我们显示汉堡包的图标。
// navbar.css
@media screen and (max-width: 768px) {
.hamburger {
display: block;
}
}
并隐藏navigation-menu 中的ul 元素。
// navbar.css
@media screen and (max-width: 768px) {
.navigation-menu ul {
display: none;
}
}
到目前为止,你的移动布局应该是这样的。
一般来说,一个导航条有两种可能的视图:展开和隐藏。你可以通过让不同的类来控制这两个导航条的视图,从而在你的应用程序中实现这一点。
我们将首先对菜单的扩展版本进行造型。随后,你将看到如何用Hooks在这两种视图之间切换。
// navbar.css
@media screen and (max-width: 768px) {
.navigation-menu ul {
// navigation menu is positioned to start 60px from the top of the document (which is directly below the navbar)
position: absolute;
top: 60px;
left: 0;
// stacks the li tags vertically
flex-direction: column;
// makes menu span full height and width
width: 100%;
height: calc(100vh - 77px);
background-color: white;
border-top: 1px solid black;
}
.navigation-menu li {
// centers link text and strips off margin
text-align: center;
margin: 0;
}
.navigation-menu li a {
color: black;
// increases the surface area of the anchor tag to span the full width of the menu
width: 100%;
padding: 1.5rem 0;
}
.navigation-menu li:hover {
background-color: #eee;
}
}
上面,我们把导航条60px ,从文档的顶部--导航条的正下方。为了确定这个元素的高度,我利用了CSS的calc 功能,我将解释为什么。
理想情况下,我们希望通过使用视口高度单位vh ,使菜单横跨整个文档的高度。但是,由于视口单位考虑了页面上的所有元素,60px 导航栏对页面的整体高度有贡献,导致导航菜单从屏幕底部获取额外的视口单位,从而产生一个垂直滚动条。
calc 函数通过允许我们在指定CSS属性值时进行计算来帮助我们应对这种情况。因此,从100vh 中减去60px ,就可以得到导航菜单的准确高度。
前面的样式应该会产生下面的布局(黄色区域标志着文档的结束)。
然而,由于我们想让另一个类来控制导航菜单的显示,我们将把它的display 设置为none 。
// navbar.css
@media screen and (max-width: 768px) {
.navigation-menu ul {
/* previous styles */
dipslay: none;
}
}
并创建一个expanded 类,附属于navigation-menu ,将display 的属性设置回block 。
// navbar.css
@media screen and (max-width: 768px) {
.navigation-menu.expanded ul {
display: block;
}
}
在这个阶段,你只能通过手动编辑Navbar.js 中的标记来观察导航条的两种状态,包括expanded 类。
除非你的用户是一个devtools专家,否则你不会想在这个时候停止开发。让我们使用React的useState Hook,用菜单图标在不同的导航条视图之间进行切换。
切换导航条视图的方法是useState
为了监控导航菜单的当前状态,我们将在Navbar 组件中引入状态。
创建一个isNavExpanded 状态,并给它一个初始值,即false ,这样。
// Navbar.js
import { useState } from "react;
import "../styles/navbar.css";
export default function Navbar(){
const [isNavExpanded, setIsNavExpanded] = useState(false)
return (
<nav className="navigation">
<a href="/" className="brand-name">
MacroSoft
</a>
<button className="hamburger" >
{/* hamburger svg code... */}
</button>
{/* nav menu code... */}
</nav>
)
}
现在让我们使用button 元素上的onClick 事件处理程序来切换isNavExpanded 的状态。
// Navbar.js
import { useState } from "react;
import "../styles/navbar.css";
export default function Navbar(){
const [isNavExpanded, setIsNavExpanded] = useState(false)
return (
<nav className="navigation">
<a href="/" className="brand-name">
MacroSoft
</a>
<button
className="hamburger"
onClick={() => {
setIsNavExpanded(!isNavExpanded);
}}
>
{/* hamburger svg code... */}
</button>
{/* nav menu code... */}
</nav>
)
}
这里,我们在onClick 事件处理程序中调用了一个匿名箭头函数。这个函数使用更新器函数setIsNavExpanded ,以扭转isNavExpanded 状态的当前值。
这意味着每当图标被点击时,isNavExpanded 将在true 和false 之间切换。
现在你可以使用JavaScript三元操作符,根据isNavExpanded 的值,有条件地为导航菜单选择合适的类名。
// Navbar.js
import { useState } from "react"
import "../styles/navbar.css"
export default function Navbar() {
const [isNavExpanded, setIsNavExpanded] = useState(false)
return (
<nav className="navigation">
<a href="/" className="brand-name">
MacroSoft
</a>
<button
className="hamburger"
onClick={() => {
setIsNavExpanded(!isNavExpanded)
}}
>
{/* hamburger svg code... */}
</button>
<div
className={
isNavExpanded ? "navigation-menu expanded" : "navigation-menu"
}
>
<ul>
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/about">About</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
</div>
</nav>
);
}
这就结束了!现在你应该有一个功能齐全、反应灵敏的导航条了。
总结
导航菜单在你的网络应用的整体体验中起着重要作用。它通常是用户在试图了解你的应用程序时接触到的第一个组件。因此,让它尽可能的有条理和可访问性对你有好处,因为它可以提高用户体验甚至是SEO性能。
The postCreate a responsive navbar with React and CSSappeared first onLogRocket Blog.