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

如何在JSX中添加JavaScript实现鼠标悬停显示效果?

问题:React中实现鼠标悬停显示元素功能失败

我正在学习JavaScript和React,想实现一个鼠标悬停效果——当鼠标悬停在.tech-title元素上时,显示默认隐藏(CSS中visibility: hidden)的.tech-text元素,但代码运行有问题,请求帮助。

我的代码:

const Resume = () => {
  let techTitle = document.querySelectorAll('.tech-title')
  let techText = document.querySelectorAll('.tech-text')

  function cardDisplay() {
    techTitle.addEventListener('mouseover', () => {
      techText.style.visibility = 'visible'
    })
  }

  return (
    <div className="app-container">
      <Profile />
      <div className="right-container">
        <Navigation />
        <div className="display">
          <h2 className="exp-title">A propos de moi</h2>
          <p className="about-me">
            Curieux de nature, je suis à la recherche d'un nouvel environnement de travail pour une première expérience
            en tant que Développeur, je suis motivé et prêt à apprendre de nouvelles compétences.
          </p>
          <h3 className="skills">Skills</h3>
          <div class="skill-content">
            <div className="skill-card">
              <i className="fa-solid fa-puzzle-piece"></i>
              <h4 className="skill-title">Réflexion</h4>
              <p className="skill-text">
                Je trouve très stimulant le fait d'analyser un problème afin d'en trouver la solution
              </p>
            </div>
            <div class="skill-card">
              <i className="fa-solid fa-people-group"></i>
              <h4 className="skill-title">Travail en équipe</h4>
              <p className="skill-text">
                Je suis à l'aise pour travailler en équipe ainsi que pour m'exprimer en public
              </p>
            </div>
            <div className="skill-card">
              <i className="fa-solid fa-user-ninja"></i>
              <h4 className="skill-title">Capacité d'apatation</h4>
              <p className="skill-text">
                Je suis capable de m'adapter à tout type de situation ainsi que de gérer mon stress
              </p>
            </div>
            <div className="skill-card">
              <i className="fa-solid fa-lightbulb"></i>
              <h4 className="skill-title">Curiosité</h4>
              <p className="skill-text">
                Curieux de nature, j'adore apprendre de nouvelles connaissances et compétences
              </p>
            </div>
          </div>
          <h3 className="skills">Tech</h3>
          <div className="tech-container">
            <div className="tech-card">
              <div className="img-tech"></div>
              <h4 className="tech-title">Html, CSS</h4>
              <p className="tech-text">bla</p>
            </div>
            <div className="tech-card">
              <div className="img-tech"></div>
              <h4 className="tech-title">SASS</h4>
              <p className="tech-text"></p>
            </div>
            <div className="tech-card">
              <div className="img-tech"></div>
              <h4 className="tech-title">Javascript</h4>
              <p className="tech-text">bla</p>
            </div>
            <div className="tech-card">
              <div className="img-tech"></div>
              <h4 className="tech-title">React Js</h4>
              <p className="tech-text">bla</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
export default Resume

问题分析与修复方案

你的代码存在三个核心问题:

  1. DOM操作时机错误:组件初始化时调用document.querySelectorAll,此时组件还未渲染到DOM,获取的是空集合,无法绑定事件。React不推荐直接操作DOM,应使用内置的状态和事件机制。
  2. NodeList操作错误:querySelectorAll返回的是多个元素的集合,不能直接调用addEventListener或修改style,必须遍历每个元素。
  3. 事件绑定函数未执行:定义了cardDisplay但从未调用,导致事件绑定根本没生效。

推荐修复方案:组件化+React状态管理

将每个技术卡片抽成独立子组件,让每个卡片自己管理悬停状态,符合React组件化思想,代码更易维护:

import React, { useState } from 'react';

// 抽离技术卡片子组件
const TechCard = ({ title, text }) => {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <div className="tech-card">
      <div className="img-tech"></div>
      <h4 
        className="tech-title"
        onMouseOver={() => setIsHovered(true)}
        onMouseOut={() => setIsHovered(false)}
      >
        {title}
      </h4>
      <p 
        className="tech-text"
        style={{ visibility: isHovered ? 'visible' : 'hidden' }}
      >
        {text}
      </p>
    </div>
  );
};

const Resume = () => {
  return (
    <div className="app-container">
      <Profile />
      <div className="right-container">
        <Navigation />
        <div className="display">
          <h2 className="exp-title">A propos de moi</h2>
          <p className="about-me">
            Curieux de nature, je suis à la recherche d'un nouvel environnement de travail pour une première expérience
            en tant que Développeur, je suis motivé et prêt à apprendre de nouvelles compétences.
          </p>
          <h3 className="skills">Skills</h3>
          <div className="skill-content">
            <div className="skill-card">
              <i className="fa-solid fa-puzzle-piece"></i>
              <h4 className="skill-title">Réflexion</h4>
              <p className="skill-text">
                Je trouve très stimulant le fait d'analyser un problème afin d'en trouver la solution
              </p>
            </div>
            <div className="skill-card">
              <i className="fa-solid fa-people-group"></i>
              <h4 className="skill-title">Travail en équipe</h4>
              <p className="skill-text">
                Je suis à l'aise pour travailler en équipe ainsi que pour m'exprimer en public
              </p>
            </div>
            <div className="skill-card">
              <i className="fa-solid fa-user-ninja"></i>
              <h4 className="skill-title">Capacité d'apatation</h4>
              <p className="skill-text">
                Je suis capable de m'adapter à tout type de situation ainsi que de gérer mon stress
              </p>
            </div>
            <div className="skill-card">
              <i className="fa-solid fa-lightbulb"></i>
              <h4 className="skill-title">Curiosité</h4>
              <p className="skill-text">
                Curieux de nature, j'adore apprendre de nouvelles connaissances et compétences
              </p>
            </div>
          </div>
          <h3 className="skills">Tech</h3>
          <div className="tech-container">
            <TechCard title="Html, CSS" text="bla" />
            <TechCard title="SASS" text="" />
            <TechCard title="Javascript" text="bla" />
            <TechCard title="React Js" text="bla" />
          </div>
        </div>
      </div>
    </div>
  );
};

export default Resume;

替代方案:父组件统一管理悬停状态

如果不想抽离子组件,可以在父组件中用索引跟踪当前悬停的卡片:

import React, { useState } from 'react';

const Resume = () => {
  const [hoveredIndex, setHoveredIndex] = useState(null);
  // 把技术项数据抽成数组,方便遍历
  const techItems = [
    { title: 'Html, CSS', text: 'bla' },
    { title: 'SASS', text: '' },
    { title: 'Javascript', text: 'bla' },
    { title: 'React Js', text: 'bla' },
  ];

  return (
    <div className="app-container">
      <Profile />
      <div className="right-container">
        <Navigation />
        <div className="display">
          {/* 其他内容保持不变 */}
          <h3 className="skills">Tech</h3>
          <div className="tech-container">
            {techItems.map((item, index) => (
              <div className="tech-card" key={index}>
                <div className="img-tech"></div>
                <h4 
                  className="tech-title"
                  onMouseOver={() => setHoveredIndex(index)}
                  onMouseOut={() => setHoveredIndex(null)}
                >
                  {item.title}
                </h4>
                <p 
                  className="tech-text"
                  style={{ visibility: hoveredIndex === index ? 'visible' : 'hidden' }}
                >
                  {item.text}
                </p>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Resume;

不推荐方案:原生DOM操作(仅作参考)

如果一定要用原生DOM操作,需要用useEffect在组件渲染完成后绑定事件,同时添加清理函数防止内存泄漏:

import React, { useEffect } from 'react';

const Resume = () => {
  useEffect(() => {
    const techTitles = document.querySelectorAll('.tech-title');
    const techTexts = document.querySelectorAll('.tech-text');

    // 遍历绑定事件
    techTitles.forEach((title, index) => {
      const text = techTexts[index];
      const showText = () => text.style.visibility = 'visible';
      const hideText = () => text.style.visibility = 'hidden';
      
      title.addEventListener('mouseover', showText);
      title.addEventListener('mouseout', hideText);

      // 清理函数
      return () => {
        title.removeEventListener('mouseover', showText);
        title.removeEventListener('mouseout', hideText);
      };
    });
  }, []); // 空依赖确保只执行一次

  return (
    {/* 原返回内容保持不变 */}
  );
};

export default Resume;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 00:50:23