如何用React和CSS创建一个响应式的导航条

727 阅读9分钟

为终端用户设计响应式的导航菜单几乎不是一个简单的过程。前端开发人员必须考虑某些参数--如设备断点和可访问性--以创造一个愉快的导航体验。在像React这样的前端框架中,它可能会变得更具挑战性,因为CSS-in-JS往往会变得很棘手。

在这篇文章中,你将学习如何用CSS和React Hooks创建一个响应式的导航条。你可以在这里摆弄源代码并查看实时项目

Navbar Demo

前提条件

要继续学习本教程,你需要。

  • 对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-namehamburger 图标和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-namenavigation-menu

// navbar.css
.brand-name {
  text-decoration: none;
  color: black;
  font-size: 1.3rem;
  margin-left: 1rem;
}
.navigation-menu {
  margin-left: auto;
}

上面代码块中的主要规则是应用于navigation-menumargin-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

Desktop Version Of Navbar

让我们继续用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 ,我们可以使用toptransform 属性将图标沿着父元素的边框垂直居中。如果你想知道这是如何工作的,请阅读更多关于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;
  }
}

到目前为止,你的移动布局应该是这样的。

Mobile Version Of Navbar

一般来说,一个导航条有两种可能的视图:展开和隐藏。你可以通过让不同的类来控制这两个导航条的视图,从而在你的应用程序中实现这一点。

我们将首先对菜单的扩展版本进行造型。随后,你将看到如何用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 ,就可以得到导航菜单的准确高度。

前面的样式应该会产生下面的布局(黄色区域标志着文档的结束)。

Expanded Version Of Navbar

然而,由于我们想让另一个类来控制导航菜单的显示,我们将把它的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 将在truefalse 之间切换。

现在你可以使用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>
  );
}

这就结束了!现在你应该有一个功能齐全、反应灵敏的导航条了。

Final Navbar

总结

导航菜单在你的网络应用的整体体验中起着重要作用。它通常是用户在试图了解你的应用程序时接触到的第一个组件。因此,让它尽可能的有条理和可访问性对你有好处,因为它可以提高用户体验甚至是SEO性能。

The postCreate a responsive navbar with React and CSSappeared first onLogRocket Blog.