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

React组件DOM嵌套警告修复及ReadMore过渡动画实现求助

ReadMore组件问题解决方案

一、修复DOM嵌套警告 (<ul> cannot appear as a descendant of <p>)

问题原因

HTML规范中,<p>标签属于行内块级元素,仅能包含行内元素或文本,无法嵌套<ul>这类块级元素。你的ReadMore组件用<p>包裹了包含<ul>的子元素,违反DOM嵌套规则,导致控制台警告。

修复步骤

  1. 替换ReadMore中的<p>为<div>
    将ReadMore组件内的<p>标签改为<div>(通用块级容器,支持嵌套任意块级元素):

    const ReadMore = ({ children }) => {
      const [isReadMore, setIsReadMore] = useState(true);
      const toggleReadMore = () => {
        setIsReadMore(!isReadMore);
      };
    
      return (
        <div className="read-more-wrapper">
          <div className={`content-container ${isReadMore ? 'collapsed' : 'expanded'}`}>
            {children}
          </div>
          <span
            onClick={toggleReadMore}
            className="read-or-hide"
            style={{ color: "#00ADB5", cursor: 'pointer', marginTop: '8px', display: 'inline-block' }}
          >
            {isReadMore ? "Position Activities" : " show less"}
          </span>
        </div>
      );
    };
    
  2. 修正<ul>内部的非法嵌套
    原代码中<ul>直接包含<p>标签,违反<ul>子元素只能是<li>的规范。将<p>改为<li>并添加样式去除列表标记:

    <ReadMore>
      <div className={`role_explanation_experience`}>
        <ul>
          <li className="section-header">Daily Activities:</li>
          <li>SOMETHING</li>
          <li>SOMETHING</li>
          {/* ...其他列表项 */}
          <li className="section-header">Achievements:</li>
          <li>SOMETHING</li>
          <li>SOMETHING</li>
        </ul>
      </div>
    </ReadMore>
    

    配套CSS:

    .section-header {
      list-style: none;
      font-weight: 600;
      margin: 12px 0 8px;
    }
    

二、实现过渡动画效果

问题分析

之前动画失效的核心原因:

  • 直接切换内容(isReadMore ? text[0] : text)会导致DOM元素被替换,无法触发CSS过渡
  • <p>标签的高度特性不适合做过渡动画

实现方案:用max-height实现平滑过渡

通过控制内容容器的max-height属性实现展开/收起动画(height: auto无法直接过渡,max-height是可行替代方案):

  1. 添加CSS过渡样式

    .content-container {
      max-height: 120px; /* 收起时的高度,根据初始展示内容调整 */
      overflow: hidden;
      transition: max-height 0.3s ease-in-out;
      position: relative;
    }
    
    .content-container.expanded {
      max-height: 2000px; /* 足够大的值,确保容纳所有内容 */
    }
    
    /* 可选:添加渐变遮罩,提示内容被截断 */
    .content-container.collapsed::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 40px;
      /* 替换为你的页面背景色 */
      background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(17, 24, 39, 1));
    }
    
  2. 调整ReadMore组件逻辑
    不再直接替换内容,而是通过max-height控制显示/隐藏,确保DOM元素始终存在以触发过渡:

    const ReadMore = ({ children }) => {
      const [isReadMore, setIsReadMore] = useState(true);
      const toggleReadMore = () => {
        setIsReadMore(!isReadMore);
      };
    
      return (
        <div className="read-more-wrapper">
          <div className={`content-container ${isReadMore ? 'collapsed' : 'expanded'}`}>
            {children}
          </div>
          <span
            onClick={toggleReadMore}
            className="read-or-hide"
            style={{ color: "#00ADB5", cursor: 'pointer', marginTop: '8px', display: 'inline-block' }}
          >
            {isReadMore ? "Position Activities" : " show less"}
          </span>
        </div>
      );
    };
    

最终完整代码示例

ReadMore组件

import { useState } from 'react';

const ReadMore = ({ children }) => {
  const [isReadMore, setIsReadMore] = useState(true);
  const toggleReadMore = () => {
    setIsReadMore(!isReadMore);
  };

  return (
    <div className="read-more-wrapper">
      <div className={`content-container ${isReadMore ? 'collapsed' : 'expanded'}`}>
        {children}
      </div>
      <span
        onClick={toggleReadMore}
        className="read-or-hide"
        style={{ color: "#00ADB5", cursor: 'pointer', marginTop: '8px', display: 'inline-block' }}
      >
        {isReadMore ? "Position Activities" : " show less"}
      </span>
    </div>
  );
};

export default ReadMore;

Experience组件

import ReadMore from './ReadMore';
import Nymbus from './path-to-nymbus-image';

export default function Experience() {
  return (
    <>
      <div className='main_div_experience_section'>
        <div className='experience_h1'>
          <span className='text-6xl font-bold text-white'>
            Experience
          </span>
        </div>
        <div className='parent_section'>
          <div className='rectangle'>
            <div className='grid'>
              <div className='col1'>
                <div className='years_experience'><p>Sep 2023 - Present</p></div>
              </div>
              <div className='col2'>
                <div className='text_company'>
                  <div className='grouper_name_and_logo'>
                    <div className='img_div_experience'>
                      <a href="home">
                        <img src={Nymbus} alt="Nymbus Logo" className='company_img_experience' />
                      </a>
                    </div>
                    <h1 className='role_experience text-gray-400 text-xl font-normal'>
                      Full Stack Developer - asdfasdf
                    </h1>
                  </div>
                  <ReadMore>
                    <div className={`role_explanation_experience`}>
                      <ul>
                        <li className="section-header">Daily Activities:</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                        <li className="section-header">Achievements:</li>
                        <li>SOMETHING</li>
                        <li>SOMETHING</li>
                      </ul>
                    </div>
                  </ReadMore>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>
  )
}

配套CSS

.read-more-wrapper {
  margin-top: 12px;
}

.content-container {
  max-height: 120px;
  overflow: hidden;
  transition: max-height 0.3s ease-in-out;
  position: relative;
}

.content-container.expanded {
  max-height: 2000px;
}

.content-container.collapsed::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 40px;
  background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(17, 24, 39, 1));
}

.section-header {
  list-style: none;
  font-weight: 600;
  margin: 12px 0 8px;
  color: #fff;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:20:59