Saber2pr's Blog

DropDownMenu

Meaning

Drop-down menus can be seen on many websites, and submenus pop up automatically when the mouse hovers over navigation elements. Benefits:

  1. Used for navigation classification two。 More content can be displayed in a limited area of view.

DOM structure

The semantic label of navigation is nav, the content is list ul, and the horizontal distribution of list elements can be floating or flex. For example:

<nav>
  <ul>
    <li>导航1</li>
    <li>导航2</li>
    <li>导航3</li>
  </ul>
</nav>

Drop-down menus are also list structures, such as:

<nav>
  <ul>
    <li>
      <a>导航1</a>
      <ul>
        <li>导航1-1</li>
        <li>导航1-2</li>
        <li>导航1-3</li>
      </ul>
    </li>
    <li>导航2</li>
    <li>导航3</li>
  </ul>
</nav>

if it is a non-automatic (hover) pop-up, you can directly use HTML5/select & options.

Respond to mouse events

  1. Close dropdown menu when the mouse leaves the nav-item and all its child elements, and the corresponding DOM Event is mouseleave. (not mouseout) two。 When the mouse enters the nav-item root element, open dropdown menu, and the corresponding DOM Event is mouseenter. (more accurate than mouseover)

TSX structure of Dropdown

The list is generated by array rendering, treats items as Functor, and applies rendering functions to each element. That is, fmap renderToTSX items.

type Anchor = {
  name: string
  href?: string
}

const Dropdown = ({ items }: { items: Anchor[] }) => (
  <ul className="head-dropdown">
    {items.map(({ name, href }) => (
      <li>
        <a href={href}>{name}</a>
      </li>
    ))}
  </ul>
)

Corresponding Cascading Style Sheets

In order to avoid DOM structure reflow caused by dropdown menu dynamic display, absolute positioning is used to get it out of the document stream.

.head-dropdown {
  position: absolute;
  z-index: 999;
}

Use Hook/useState

Because you want to display dropdown menu dynamically, it is an asynchronous DOM operation. Use setState to implement.

type NavItemProps = Anchor & {
  items: Anchor[]
}

const NavItem = ({ name, href, items }: NavItemProps) => {
  const [display, setComponent] = useState(<></>)
  const visible = () => setComponent(<Dropdown items={items} />)
  const hidden = () => setComponent(<></>)

  return (
    <li className="nav-item" onMouseEnter={visible} onMouseLeave={hidden}>
      <a href={href}>{name}</a>
      {display}
    </li>
  )
}

Cascading style sheets for nav-item. Use floats below if you need to be compatible with IE10.

.nav-item {
  float: left;
  padding: 0 0.5rem;
}

App

const App = () => (
  <nav>
    <ul>
      <NavItem
        name="item1"
        items={[
          { name: "subitem1", href: "#" },
          { name: "subitem2", href: "#" },
          { name: "subitem3", href: "#" }
        ]}
      />
    </ul>
  </nav>
)