作品集网站板块切换报错求助:TypeError无法读取className
作品集网站板块切换功能报错修复
问题现象
实现作品集网站板块切换功能,初期运行正常,后续出现崩溃,抛出错误:
app.js:10 Uncaught TypeError: Cannot read properties of undefined (reading 'className') at HTMLDivElement. (app.js:10:53)
原始代码
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Portfolio</title> <link rel="stylesheet" href="styles/styles.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet"> <script src="https://kit.fontawesome.com/25c3cf235c.js" crossorigin="anonymous"></script> </head> <body class="main-content"> <header class="section sec1 header active" id="home"></header> <main> <section class="section sec2 about" id="about"></section> <section class="section sec3 portfolio" id="portfolio"></section> <section class="section sec4 blogs" id="blogs"></section> <section class="section sec5 contact" id="contact"></section> </main> <div class="controlls"> <div class="control control-1 active-btn" data-id="home"> <i class="fa-solid fa-house-user"></i> </div> <div class="control control-2" data-id="about"> <i class="fa-solid fa-user"></i> </div> <div class="control control-3" data-id="portfolio"> <i class="fa-solid fa-briefcase"></i> </div> <div class="control control-4" data-id="blogs"> <i class="fa-solid fa-newspaper"></i> </div> <div class="control control-5" data-id="contact"> <i class="fa-solid fa-envelope"></i> </div> </div> <script src="app.js"></script> </body> </html>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; list-style: none; } :root { --color-primary: #191d2b; --color-secondary: #27AE60; --color-white: #FFFFFF; --color-black: #000; --color-grey0: #f8f8f8; --color-grey-1: #dbe1e8; --color-grey-2: #b2becd; --color-grey-3: #6c7983; --color-grey-4: #454e56; --color-grey-5: #2a2e35; --color-grey-6: #12181b; --br-sm-2: 14px; --box-shadow-1: 0 3px 15px rgba(0,0,0,.3); } body { font-family: 'Poppins', sans-serif; background-color: var(--color-primary); font-size: 1.1rem; color: var(--color-white ); transition: all .4s ease-in-out; } a { display: inline-block; text-decoration: none; color: inherit; font-family: inherit; } header { height: 100vh; color: var(--color-white); overflow: hidden; } section { min-height: 100vh; width: 100%; position: absolute; left: 0; top: 0; padding: 3rem 18rem; } .section { transform: translateY(-100%) scale(0); transition: all .4 ease-in-out; background-color: var(--color-primary); } .sec1 { display: none; transform: translateV(0) scale(1); background-color: rgb(26, 22, 50); } .sec2 { display: none; transform: translateV(0) scale(1); background-color: slateblue; } .sec3 { display: none; transform: translateV(0) scale(1); background-color: rgb(127, 123, 151); } .sec4 { display: none; transform: translateV(0) scale(1); } .sec5 { display: none; transform: translateV(0) scale(1); } .controlls { position: fixed; z-index: 10; top: 50%; right: 3%; display: flex; flex-direction: column; align-items: center; justify-content: center; transform: translateY(-50%); .active-btn { background-color: var(--color-secondary) !important; transition: all .4s ease-in-out; i { color: var(--color-white) !important; } } .control { padding: 1rem; cursor: pointer; background-color: var(--color-grey-4); width: 55px; height: 55px; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin: .7rem 0; box-shadow: var(--box-shadow-1); i { font-size: 1.2rem; color: var(--color-grey-2); pointer-events: none; } } }
JavaScript
const sections = document.querySelectorAll('.section'); const sectBtns = document.querySelectorAll(".controlls"); const sectBtn = document.querySelectorAll(".control"); const allSections = document.querySelector('.main-content'); function PageTransitions() { for (let i = 0; i < sectBtn.length; i++) { sectBtn[i]. addEventListener("click", function() { let currentBtn = document.querySelectorAll('.active-btn'); currentBtn[0].className = currentBtn[0].className.replace('active-btn',''); this.className += 'active-btn'; }) } allSections.addEventListener('click', (e)=> { const id = e.target.dataset.id; if (id) { sectBtns.forEach((btn) => { btn.classList.remove('active') }) e.target.classList.add('active') sections.forEach((section)=> { section.classList.remove('active') }) const element = document.getElementById(id); element.classList.add('active'); } }) } PageTransitions();
错误原因
- 类名操作不规范:直接拼接
className += 'active-btn'会重复添加类名,多次点击后document.querySelectorAll('.active-btn')可能返回多个元素或空数组,访问currentBtn[0]会得到undefined,触发读取className的错误。 - 事件委托的目标偏差:给
body绑定点击事件,点击按钮内部的<i>标签时,e.target指向<i>而非按钮,此时e.target.dataset.id不存在,后续获取element为undefined,操作classList也会报错。 - CSS显示逻辑冲突:CSS中用
.sec1等类直接控制display,而JS操作.active类,两者功能重叠,导致板块切换逻辑混乱。
修复方案
修复后的JavaScript
const sections = document.querySelectorAll('.section'); const sectBtns = document.querySelectorAll(".controlls .control"); const allSections = document.querySelector('.main-content'); function PageTransitions() { // 为每个控制按钮绑定点击事件 sectBtns.forEach(btn => { btn.addEventListener("click", function() { // 移除所有按钮的active-btn类 sectBtns.forEach(b => b.classList.remove('active-btn')); // 给当前点击的按钮添加active-btn类 this.classList.add('active-btn'); // 获取目标板块ID const targetId = this.dataset.id; if (targetId) { // 隐藏所有板块 sections.forEach(section => section.classList.remove('active')); // 显示目标板块 const targetSection = document.getElementById(targetId); if (targetSection) { targetSection.classList.add('active'); } } }); }); } PageTransitions();
修复后的CSS关键部分
.section { transform: translateY(-100%) scale(0); transition: all .4s ease-in-out; background-color: var(--color-primary); display: none; /* 默认隐藏所有板块 */ } /* 激活状态的板块显示 */ .section.active { display: block; transform: translateY(0) scale(1); } /* 保留各板块的背景色设置 */ .sec1 { background-color: rgb(26, 22, 50); } .sec2 { background-color: slateblue; } .sec3 { background-color: rgb(127, 123, 151); }
修复说明
- 标准化类名操作:使用
classList.add()和classList.remove()替代直接修改className,避免重复添加类名,同时确保操作的可靠性。 - 避免事件目标偏差:直接给每个控制按钮绑定事件,不再通过父元素委托,确保点击时目标始终是按钮本身,不会触发到内部图标。
- 统一显示逻辑:用
.active类统一控制板块的显示/隐藏,删除原始CSS中.sec1等类的display和transform属性,消除逻辑冲突。 - 增加存在性检查:获取目标板块后先判断是否存在,再执行类名操作,防止
undefined报错。
内容的提问来源于stack exchange,提问作者Chin-Erdene Gantulga
相关产品推荐
相关产品推荐

