FreeCodeCamp卡路里计数器项目VSCode中TypeError错误求助
我是编程新手,正在通过FreeCodeCamp学习JavaScript,目前在复刻卡路里计数器项目。在VSCode中添加addEntry按钮的JS逻辑后,出现如下错误:
Uncaught TypeError TypeError: Cannot read properties of null (reading 'querySelectorAll')
at addEntry - filepath calorie counter.js 21
该代码在FreeCodeCamp预览窗口可正常运行,但在VSCode中始终报错,即使直接复制平台代码也无法解决,完全不知道该搜索什么关键词。
项目代码
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="device=device-width, initial-scale= 1.0" />\ <link rel="stylesheet" href="C:\Users\babat\OneDrive\Desktop\Vs Code Projects\JsApps\Calorie Counter.css" /> <title>Calorie Counter</title> </head> <body> <main> <h1>Calorie Counter</h1> <div class="container"> <form id="calorie-counter"> <label for="budget">Budget</label> <input id="budget" type="number" min="0" placeholder="Daily Calorie Counter" required /> <fieldset class="breakfast"> <legend>Breakfast</legend> <div class="input-container"></div> </fieldset> <fieldset id="lunch"> <legend>Lunch</legend> <div class="input-container"></div> </fieldset> <fieldset id="dinner"> <legend>Dinner</legend> <div class="input-container"></div> </fieldset> <fieldset id="snacks"> <legend>Snacks</legend> <div class="input-container"></div> </fieldset> <fieldset id="exercise"> <legend>Exercise</legend> <div class="input-container"></div> </fieldset> <div class="controls"> <span> <label for="entry-dropdown">Add food or exercise</label> <select id="entry-dropdown" name="options"> <option value="breakfast" selected>Breakfast</option> <option value="lunch">Lunch</option> <option value="dinner">Dinner</option> <option value="snacks">Snacks</option> <option value="exercise">Exercise</option> </select> <button id="add-entry" type="button">Add Entry</button> </span> </div> <div> <button type="submit">Calculate Remaining Calories</button> <button id="clear" type="button">Clear</button> </div> </form> <div id="output" class="output hide"></div> </div> </main> <script src="C:\Users\babat\OneDrive\Desktop\Vs Code Projects\JsApps\Calorie Counter.js"></script> </body> </html>
CSS
:root { --light-grey: #f5f6f7; --dark-blue: #0a0a23; --fcc-blue: #1b1b32; --light-yellow: #fecc4c; --dark-yellow: #feac32; --light-pink: #ffadad; --dark-red: #850000; --light-green: #acd157; } body { font-family: "Lato", Helvetica, Arial, sans-serif; font-size: 18px; background-color: var(--fcc-blue); color: var(--light-grey); } h1 { text-align: center; } .container { width: 90%; max-width: 680px; } h1, .container, .output { margin: 20px auto; } label, legend { font-weight: bold; } .input-container { display: flex; flex-direction: column; } button { outline: none; cursor: pointer; text-decoration: none; background-color: var(--light-yellow); border: 2px solid var(--dark-yellow); } .clear { background-color: var(--light-pink); color: var(--dark-red); border-color: var(--dark-red); } button, input, select { min-height: 24px; color: var(--dark-blue); } fieldset, label, button, input, select { margin-bottom: 10px; } .output { border: 2px solid var(--light-grey); padding: 10px; text-align: center; } .hide { display: none; } .output span { font-weight: bold; font-size: 1.2em; } .surplus { color: var(--light-green); } .deficit { color: var(--light-pink); }
JavaScript
const calorieCounter = document.getElementById('calorie-counter'); const budgetNumberInput = document.getElementById('budget'); const entryDropdown = document.getElementById('entry-dropdown'); const addEntryButton = document.getElementById('add-entry'); const clearButton = document.getElementById('clear'); const output = document.getElementById('output'); let isError = false; function cleanInputString(str) { const regex = /[+\-\s]/g; return str.replace(regex, ''); } function isInvalidInput(str) { const regex = /\d+e\d+/i; return str.match(regex); } function addEntry() { const targetInputContainer = document.querySelector(`#${entryDropdown.value} .input-container`); const entryNumber = targetInputContainer.querySelectorAll('input[type="text"]').length + 1; const HTMLString = ` <label for="${entryDropdown.value}-${entryNumber}-name">Entry ${entryNumber} Name</label> <input type="text" id="${entryDropdown.value}-${entryNumber}-name" placeholder="Name" /> <label for="${entryDropdown.value}-${entryNumber}-calories">Entry ${entryNumber} Calories</label> <input type="number" min="0" id="${entryDropdown.value}-${entryNumber}-calories" placeholder="Calories" />`; targetInputContainer.insertAdjacentHTML('beforeend', HTMLString); } addEntryButton.addEventListener("click", addEntry);
错误原因与修复方案
错误根源
代码中addEntry函数里的document.querySelector(#${entryDropdown.value} .input-container)默认查找id为breakfast的元素,但HTML里Breakfast对应的<fieldset>只有class="breakfast",没有设置id="breakfast"。这就导致targetInputContainer的值为null,后续调用querySelectorAll自然会抛出"Cannot read properties of null"的错误。
FreeCodeCamp预览能正常运行,大概率是平台自动补全了这个id,或者你在平台上的代码本身就有这个id,复制时遗漏了。
修复步骤
修改HTML中Breakfast的fieldset标签,给它加上id属性:
<fieldset id="breakfast" class="breakfast"> <legend>Breakfast</legend> <div class="input-container"></div> </fieldset>
这样JS就能正确找到对应的input-container元素,错误就会消失。
内容的提问来源于stack exchange,提问作者Babatunji Afolayan

