You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中实现跨<li>元素的<mark>文本hover联动效果

问题

现有HTML结构:

<ul>
  <li>First bullet point</li>
  <li>Second bullet point</li>
</ul>

修改后实现跨列表项的文本高亮效果:

<ul>
  <li>First bullet <mark>point</mark></li>
  <li><mark>Second bullet</mark> point</li>
</ul>

需求:鼠标悬停任意一个<mark>时,另一个<mark>也触发相同的hover样式,让用户感知为单个高亮块。已知CSS兄弟选择器~和Tailwind的peer类仅支持前置元素影响后置元素,请问:

  1. 仅通过CSS/Tailwind能否实现该效果?
  2. 若需要JavaScript,求React项目中的可行方案(需注意DOM操作相关事项)

回答

一、CSS/Tailwind 无法实现

两个<mark>分别属于不同的<li>父元素,属于跨父级的非同级节点。CSS的所有选择器(包括兄弟选择器、Tailwind peer)只能处理同一父级下的元素,或者单向的层级依赖关系,无法实现跨父元素的双向hover联动——既做不到第一个<mark>hover时影响第二个,也做不到第二个hover时反向影响第一个。

二、React 项目的 JavaScript 实现方案

推荐两种方案,优先用状态驱动的方式,符合React的数据流原则:

方案1:状态统一控制hover状态

用React状态管理全局hover状态,所有<mark>的样式都由该状态控制:

import { useState } from 'react';

const LinkedHighlightList = () => {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <ul>
      <li>
        First bullet
        <mark
          className={isHovered ? 'bg-yellow-200' : 'bg-yellow-100'}
          onMouseEnter={() => setIsHovered(true)}
          onMouseLeave={() => setIsHovered(false)}
        >
          point
        </mark>
      </li>
      <li>
        <mark
          className={isHovered ? 'bg-yellow-200' : 'bg-yellow-100'}
          onMouseEnter={() => setIsHovered(true)}
          onMouseLeave={() => setIsHovered(false)}
        >
          Second bullet
        </mark>
        point
      </li>
    </ul>
  );
};

export default LinkedHighlightList;
  • 优点:完全遵循React数据驱动视图的理念,无直接DOM操作,易维护、易扩展。
  • 样式说明:示例用Tailwind的背景色类,也可以自定义CSS类替换。

方案2:Ref批量绑定事件(适合大量<mark>的场景)

如果有多个跨元素的<mark>,可以用useRef收集所有节点,统一绑定hover事件:

import { useRef, useEffect } from 'react';

const LinkedHighlightList = () => {
  const markRefs = useRef([]);

  useEffect(() => {
    const marks = markRefs.current;
    const handleHoverIn = () => marks.forEach(mark => mark.classList.add('hover-active'));
    const handleHoverOut = () => marks.forEach(mark => mark.classList.remove('hover-active'));

    // 绑定事件
    marks.forEach(mark => {
      mark.addEventListener('mouseenter', handleHoverIn);
      mark.addEventListener('mouseleave', handleHoverOut);
    });

    // 组件卸载时清理事件,避免内存泄漏
    return () => {
      marks.forEach(mark => {
        mark.removeEventListener('mouseenter', handleHoverIn);
        mark.removeEventListener('mouseleave', handleHoverOut);
      });
    };
  }, []);

  return (
    <ul>
      <li>
        First bullet
        <mark ref={el => el && markRefs.current.push(el)}>point</mark>
      </li>
      <li>
        <mark ref={el => el && markRefs.current.push(el)}>Second bullet</mark>
        point
      </li>
    </ul>
  );
};

export default LinkedHighlightList;

对应的CSS样式:

mark {
  background-color: #ffed4a;
  transition: background-color 0.2s;
}
mark.hover-active {
  background-color: #fdd835;
}

DOM操作注意事项

  1. 优先用状态而非直接DOM操作:React是数据驱动视图,直接修改DOM会导致状态与视图不一致,增加维护难度。
  2. 清理事件监听:用ref绑定DOM事件时,必须在组件卸载时移除监听,防止内存泄漏。
  3. ref的访问时机:确保DOM节点挂载后再访问ref,通常在useEffect(空依赖数组)或useLayoutEffect中处理。
  4. 避免过度使用ref:能用状态实现的逻辑,尽量不用ref,保持组件的可预测性和可测试性。

内容的提问来源于stack exchange,提问作者jimmy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:55:18