在React嵌套菜单上的悬停状态

huangapple go评论117阅读模式
英文:

Hover states on React nested menu

问题

在以下代码中,我有一个嵌套菜单。在悬停时,我希望将 selected 类名称添加到 Nav.Item,并且仅在我悬停到另一个 Nav.Item 上时将其移除。我可以通过 onMouseOver 来实现这一点。不幸的是,它不会向任何子级 Nav.Item 添加 selected 类。

因此,要求如下:

  • 对于悬停在任何 nav.item 上,应添加 selected 类
  • 如果鼠标离开项目,则不要删除 selected
  • 当出现子菜单时,在 Nav.Item 上保留 selected
  • 只有在悬停到另一个相同级别的 Nav.Item 上时才移除 selected
  1. "use client";
  2. import Nav from 'react-bootstrap/Nav';
  3. import Link from 'next/link';
  4. import { getMenu } from '@/lib/APIs/menu';
  5. import { use, useEffect, useRef, useState } from 'react';
  6. import { usePathname } from 'next/navigation';
  7. import { Button, NavDropdown } from 'react-bootstrap';
  8. const dataPromise = getMenu();
  9. const GlobalNav = () => {
  10. const MENU = use(dataPromise);
  11. const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
  12. function handleItemHover(itemId: string) {
  13. setSelectedItemId(itemId);
  14. }
  15. function renderMenuItems(menuItems: any[]) {
  16. return menuItems.map((item: any) => (
  17. <Nav.Item
  18. as='li'
  19. key={item.id}
  20. onMouseOver={() => handleItemHover(item.id)}
  21. className={selectedItemId === item.id ? 'selected' : ''}
  22. >
  23. <Nav.Link
  24. as={Link}
  25. href={item.url}
  26. >
  27. <span>
  28. {item.title}
  29. </span>
  30. </Nav.Link>
  31. {item.children && item.children.length > 0 && (
  32. <Nav as='ul'>
  33. {renderMenuItems(item.children)}
  34. </Nav>
  35. )}
  36. </Nav.Item>
  37. ));
  38. }
  39. return (
  40. <Nav as='ul'>
  41. {renderMenuItems(MENU)}
  42. </Nav>
  43. );
  44. }
  45. export default GlobalNav;

希望有人能帮助您解决这个问题。

英文:

In the following code I have a nested menu. On hover I wanted to add selected class name to the Nav.Item and only remove it when I hover over another Nav.Item. I could achive that with onMouseOver. Unfortunately, it doesn't add selected class to any of the sub-level Nav.Item.

So the criterias would be:
for hover on any nav.item selected class should be added

  • don't remove selected class if mouse leaves the item
  • keep selected class on the Nav.Item when a child menu appears
  • remove selected class only, if hover over another Nav.Item but on the same level
  1. &quot;use client&quot;;
  2. import Nav from &#39;react-bootstrap/Nav&#39;;
  3. import Link from &#39;next/link&#39;;
  4. import { getMenu } from &#39;@/lib/APIs/menu&#39;;
  5. import { use, useEffect, useRef, useState } from &#39;react&#39;;
  6. import { usePathname } from &#39;next/navigation&#39;;
  7. import { Button, NavDropdown } from &#39;react-bootstrap&#39;;
  8. const dataPromise = getMenu();
  9. const GlobalNav = () =&gt; {
  10. const MENU = use(dataPromise);
  11. const [selectedItemId, setSelectedItemId] = useState&lt;string | null&gt;(null);
  12. function handleItemHover(itemId: string) {
  13. setSelectedItemId(itemId);
  14. }
  15. function renderMenuItems(menuItems: any[]) {
  16. return menuItems.map((item: any) =&gt; (
  17. &lt;Nav.Item
  18. as=&#39;li&#39;
  19. key={item.id}
  20. onMouseOver={() =&gt; handleItemHover(item.id)}
  21. className={selectedItemId === item.id ? &#39;selected&#39; : &#39;&#39;}
  22. &gt;
  23. &lt;Nav.Link
  24. as={Link}
  25. href={item.url}
  26. &gt;
  27. &lt;span&gt;
  28. {item.title}
  29. &lt;/span&gt;
  30. &lt;/Nav.Link&gt;
  31. {item.children &amp;&amp; item.children.length &gt; 0 &amp;&amp; (
  32. &lt;Nav as=&#39;ul&#39;&gt;
  33. {renderMenuItems(item.children)}
  34. &lt;/Nav&gt;
  35. )}
  36. &lt;/Nav.Item&gt;
  37. ));
  38. }
  39. return (
  40. &lt;Nav as=&#39;ul&#39;&gt;
  41. {renderMenuItems(MENU)}
  42. &lt;/Nav&gt;
  43. );
  44. }
  45. export default GlobalNav;

I hope someone can help with it.

答案1

得分: 1

请尝试以下代码:

  1. function hasSelectedChild(menuItem) {
  2. return menuItem.children && menuItem.children.length > 0 && menuItem.children.find(item => item.id === selectedItemId || hasSelectedChild(item))
  3. }
  4. function renderMenuItems(menuItems) {
  5. return menuItems.map(item => (
  6. <Nav.Item
  7. as='li'
  8. key={item.id}
  9. onMouseOver={(e) => {
  10. e.stopPropagation();
  11. handleItemHover(item.id)
  12. }}
  13. className={(hasSelectedChild(item) || selectedItemId === item.id) ? 'selected' : ''}
  14. >
  15. <Nav.Link
  16. as={Link}
  17. href={item.url}
  18. >
  19. <span>
  20. {item.title}
  21. </span>
  22. </Nav.Link>
  23. {item.children && item.children.length > 0 && (
  24. <Nav as='ul'>
  25. {renderMenuItems(item.children)}
  26. </Nav>
  27. )}
  28. </Nav.Item>
  29. ));
  30. }

希望这有帮助。

英文:

Try this:

  1. function hasSelectedChild(menuItem) {
  2. return menuItem.children &amp;&amp; menuItem.children.length &gt; 0 &amp;&amp; menuItem.children.find(item =&gt; item.id === selectedItemId || hasSelectedChild(item))
  3. }
  4. function renderMenuItems(menuItems: any[]) {
  5. return menuItems.map((item: any) =&gt; (
  6. &lt;Nav.Item
  7. as=&#39;li&#39;
  8. key={item.id}
  9. onMouseOver={(e) =&gt; {
  10. e.stopPropagation();
  11. handleItemHover(item.id)
  12. }}
  13. className={(hasSelectedChild(item) || selectedItemId === item.id) ? &#39;selected&#39; : &#39;&#39;}
  14. &gt;
  15. &lt;Nav.Link
  16. as={Link}
  17. href={item.url}
  18. &gt;
  19. &lt;span&gt;
  20. {item.title}
  21. &lt;/span&gt;
  22. &lt;/Nav.Link&gt;
  23. {item.children &amp;&amp; item.children.length &gt; 0 &amp;&amp; (
  24. &lt;Nav as=&#39;ul&#39;&gt;
  25. {renderMenuItems(item.children)}
  26. &lt;/Nav&gt;
  27. )}
  28. &lt;/Nav.Item&gt;
  29. ));
  30. }

答案2

得分: 1

为了实现在鼠标悬停在Nav.Item元素上时添加"selected"类,并在出现子菜单时保持该类,您可以按照以下方式修改代码:

  1. import Nav from 'react-bootstrap/Nav';
  2. import Link from 'next/link';
  3. import { getMenu } from '@/lib/APIs/menu';
  4. import { usePathname } from 'next/navigation';
  5. import { Button, NavDropdown } from 'react-bootstrap';
  6. const dataPromise = getMenu();
  7. const GlobalNav = () => {
  8. const MENU = use(dataPromise);
  9. const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
  10. function handleItemHover(itemId: string) {
  11. setSelectedItemId(itemId);
  12. }
  13. function handleItemMouseLeave() {
  14. setSelectedItemId(null);
  15. }
  16. function renderMenuItems(menuItems: any[]) {
  17. return menuItems.map((item: any) => (
  18. <Nav.Item
  19. as='li'
  20. key={item.id}
  21. onMouseOver={() => handleItemHover(item.id)}
  22. onMouseLeave={handleItemMouseLeave}
  23. className={selectedItemId === item.id ? 'selected' : ''}
  24. >
  25. <Nav.Link as={Link} href={item.url}>
  26. <span>{item.title}</span>
  27. </Nav.Link>
  28. {item.children && item.children.length > 0 && (
  29. <Nav as='ul' className={selectedItemId === item.id ? 'selected' : ''}>
  30. {renderMenuItems(item.children)}
  31. </Nav>
  32. )}
  33. </Nav.Item>
  34. ));
  35. }
  36. return (
  37. <Nav as='ul'>
  38. {renderMenuItems(MENU)}
  39. </Nav>
  40. );
  41. }
  42. export default GlobalNav;

在嵌套的Nav组件上添加了className属性,以在选定父级Nav.Item时添加"selected"类。并且添加了onMouseLeave={handleItemMouseLeave}事件处理程序到Nav.Item上,以触发selectedItemId的重置。

英文:

To achieve the desired behavior of adding the "selected" class to the Nav.Item elements on hover and keeping it when a child menu appears, you can modify the code as below:

  1. import Nav from &#39;react-bootstrap/Nav&#39;;
  2. import Link from &#39;next/link&#39;;
  3. import { getMenu } from &#39;@/lib/APIs/menu&#39;;
  4. import { usePathname } from &#39;next/navigation&#39;;
  5. import { Button, NavDropdown } from &#39;react-bootstrap&#39;;
  6. const dataPromise = getMenu();
  7. const GlobalNav = () =&gt; {
  8. const MENU = use(dataPromise);
  9. const [selectedItemId, setSelectedItemId] = useState&lt;string | null&gt;(null);
  10. function handleItemHover(itemId: string) {
  11. setSelectedItemId(itemId);
  12. }
  13. function handleItemMouseLeave() {
  14. setSelectedItemId(null);
  15. }
  16. function renderMenuItems(menuItems: any[]) {
  17. return menuItems.map((item: any) =&gt; (
  18. &lt;Nav.Item
  19. as=&#39;li&#39;
  20. key={item.id}
  21. onMouseOver={() =&gt; handleItemHover(item.id)}
  22. onMouseLeave={handleItemMouseLeave}
  23. className={selectedItemId === item.id ? &#39;selected&#39; : &#39;&#39;}
  24. &gt;
  25. &lt;Nav.Link as={Link} href={item.url}&gt;
  26. &lt;span&gt;{item.title}&lt;/span&gt;
  27. &lt;/Nav.Link&gt;
  28. {item.children &amp;&amp; item.children.length &gt; 0 &amp;&amp; (
  29. &lt;Nav as=&#39;ul&#39; className={selectedItemId === item.id ? &#39;selected&#39; : &#39;&#39;}&gt;
  30. {renderMenuItems(item.children)}
  31. &lt;/Nav&gt;
  32. )}
  33. &lt;/Nav.Item&gt;
  34. ));
  35. }
  36. return (
  37. &lt;Nav as=&#39;ul&#39;&gt;
  38. {renderMenuItems(MENU)}
  39. &lt;/Nav&gt;
  40. );
  41. }
  42. export default GlobalNav;

Adding the className prop to the nested Nav component to add the "selected" class when the parent Nav.Item is selected. And adding the onMouseLeave={handleItemMouseLeave} event handler to the Nav.Item to trigger the reset of selectedItemId

huangapple
  • 本文由 发表于 2023年7月13日 18:22:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678321.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定