onclick事件无法获取className元素追加内容 报length读取错误
报错根因
控制台抛出Cannot read properties of null (reading 'length')是三处代码逻辑错误导致的:
- 你在页面刚加载完成、还没有任何按钮被选中时,就执行了
let results = document.querySelector('.btn.button-focus'),此时没有匹配元素,querySelector会返回null,后续提交事件中读取results.length就会触发空引用错误。另外querySelector只会返回第一个匹配的DOM元素,本身也没有length属性,就算初始有选中按钮也不符合获取所有选中项的需求。 - 判断按钮是否带
button-focus类的逻辑存在缺陷:遍历classList时,只要最后一个遍历到的类名不是button-focus,就会把isPresent重置为false,哪怕之前已经遍历到目标类名也会被覆盖,导致选中状态判断错误。 - 提交逻辑的遍历写法有误:就算拿到了元素集合,循环里push的是整个集合对象,不是单个选中按钮的值,达不到收集选中内容的目的。
修复方案
按以下点修改代码即可:
- 将获取选中按钮的逻辑移到提交按钮的点击事件内部,每次点击提交时再实时查询当前带
button-focus类的按钮,保证拿到最新的选中状态 - 用
querySelectorAll('.btn.button-focus')替换querySelector,该方法会返回所有匹配元素的NodeList集合,支持长度读取和遍历 - 替换手写的classList遍历判断逻辑,直接用原生API
element.classList.contains('button-focus')判断类是否存在,避免逻辑漏洞 - 遍历选中元素集合时,逐个读取元素的
value属性存入结果数组,不要直接传入整个集合对象
修复后可运行代码
<!DOCTYPE html> <html> <head> <style> :root { --primary_orange: #fea501; --dark: #000; --primary_med: #f5f5f5; --shadow: 0 2px 4px rgba(0,0,0,0.2); } .my_dataviz { width: auto; height: auto; margin-top: 15px; margin-bottom: 15px; display: flex; flex-wrap: wrap; align-content: center; justify-content: space-evenly; } .my_dataviz button { font-family: "walkway", sans-serif; font-size: 16px; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; width: 130px; height: 25px; background-color: Transparent; -webkit-tap-highlight-color: transparent; background-repeat:no-repeat; border: none; letter-spacing: 1.6px; margin: 10px; border: 1px solid transparent; } .my_dataviz button:hover { box-sizing: border-box; background-color: var(--primary_orange); color: var(--dark); font-weight: bold; border: 1px solid #000; border-radius: 5px; cursor: pointer; } .btn.button-focus { background-color: var(--primary_orange); color: var(--dark); font-weight: bold; border: 1px solid #000; border-radius: 5px; } .modal-footer { display: flex; flex-wrap: wrap; justify-content: center; height: 50px; align-items: center; text-align: center; background: var(--primary_med); border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; } .modal-footer .modal-btn { font-family: "walkway", sans-serif; font-size: 16px; font-weight: bold; width: 100px; height: 28px; border-radius: 5px; align-items: center; letter-spacing: 1.6px; box-shadow: var(--shadow); cursor: pointer; } </style> </head> <body> <div class="my_dataviz" id="my_dataviz"> </div> <div class="modal-footer"> <input class="modal-btn" type="button" value="Select it" /> </div> <script> var container = document.getElementById('my_dataviz'); var array = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" ] let SelectCount = 0; array.forEach(element => { const button = document.createElement("button"); button.className = "btn"; button.id = element; button.value = element; button.type = "button"; const text = document.createTextNode(element); button.appendChild(text); container.appendChild(button); }); let btn = document.getElementsByClassName("btn"); for (let i = 0; i < btn.length; i++) { btn[i].addEventListener("click", function() { console.log("Clicked Button: " + i); // 直接用原生API判断类是否存在,修复原遍历判断的逻辑bug const isPresent = this.classList.contains("button-focus"); if (isPresent) { this.classList.remove("button-focus"); SelectCount -= 1; // 取消选中后恢复所有按钮可用 document.querySelectorAll('.btn').forEach(item => { item.disabled = false }) } else { this.classList.add("button-focus"); SelectCount += 1; if (SelectCount > 2) { // 选中超过2个时禁用未选中按钮 document.querySelectorAll('.btn').forEach(item => { if (!item.classList.contains('button-focus')) item.disabled = true }) } } }) } const dataResults = [] let submit_button = document.querySelector('.modal-btn'); submit_button.onclick = function() { // 每次点击提交时实时获取最新的选中按钮,用querySelectorAll获取集合 const selectedBtns = document.querySelectorAll('.btn.button-focus'); // 清空上次提交的结果,避免重复累加 dataResults.length = 0; for (let i = 0; i < selectedBtns.length; ++i) { // 存入按钮的value值,不是DOM对象本身 dataResults.push(selectedBtns[i].value) } console.log("收集到的选中值:", dataResults) } </script> </body> </html>
修复后最多可选3个选项,点击提交后控制台会打印当前选中的所有值,不会再抛出空引用错误。
内容的提问来源于stack exchange,提问作者Yan
相关产品推荐
相关产品推荐

