Fix a Const Assignment Error in a Browser Filter
Trace a const reassignment inside an input handler, preserve the original list, and verify that filtering and clearing the query both work.
A page can load correctly and fail only when someone types into a search box. If the event handler tries to replace a const binding, the assignment throws and the rest of that handler does not run. Changing every declaration to let can hide the exception while leaving a broken filter.
This tutorial repairs one complete browser example and checks the behavior after the error disappears. For the general rules about declarations and scope, use the course lesson on let and const. Here the goal is a working filter that can recover the original list.
Reproduce the failure after page load
Save this as draft-filter.html and open it in a current browser. There are no packages, server requests or build steps. The search input filters local example titles only.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Draft filter assignment failure</title>
<style>
body { font: 1rem/1.5 system-ui; max-width: 40rem; margin: 2rem auto; padding: 0 1rem; }
label, input, output { display: block; }
input { box-sizing: border-box; width: 100%; font: inherit; padding: .5rem; }
output { margin-top: 1rem; }
li { overflow-wrap: anywhere; }
</style>
<h1>Find a draft</h1>
<label for="query">Filter by title</label>
<input id="query" type="search" autocomplete="off">
<output id="status" aria-live="polite"></output>
<ul id="results"></ul>
<script>
const drafts = ["Quarterly plan", "Release notes", "Budget draft"];
const queryInput = document.querySelector("#query");
const results = document.querySelector("#results");
const status = document.querySelector("#status");
function render(items) {
results.replaceChildren();
for (const title of items) {
const item = document.createElement("li");
item.textContent = title;
results.append(item);
}
status.textContent = `${items.length} ${items.length === 1 ? "draft" : "drafts"}`;
}
queryInput.addEventListener("input", () => {
const query = queryInput.value.trim().toLowerCase();
drafts = drafts.filter(title => title.toLowerCase().includes(query));
render(drafts);
});
render(drafts);
</script>
</html>
Initially the page shows three drafts. Open DevTools Console, then type release. In Chromium the error message includes Assignment to constant variable; other browsers use different wording. The exception type is TypeError. The list stays at three items because the failing assignment prevents the following render call.
Find the binding that is being replaced
Follow the error's file and line link to the assignment inside the input listener. Its right-hand side, drafts.filter(...), produces a new array. The left-hand side tries to bind the name drafts to that new array, although it was declared with const.
The problem is not the input element or filter. The listener is allowed to read the original array and create another array. It is the reassignment of that particular binding that fails. The DOM references also remain valid const bindings while their elements' text or children change.
Repair the calculation inside the handler
Replace the whole input listener with this version, keeping the original declarations and initial render(drafts) call:
queryInput.addEventListener("input", () => {
const query = queryInput.value.trim().toLowerCase();
const visibleDrafts = drafts.filter(title => title.toLowerCase().includes(query));
render(visibleDrafts);
});
Each input event now creates a local visibleDrafts binding for that calculation. Nothing needs to reassign it. The source drafts array stays available, so an empty query can produce all three titles again.
Use let when a binding really must be reassigned across operations. This repair does not require persistent replacement state: the visible list can be derived from the source and the current input. Keeping that calculation local removes the unnecessary reassignment.
Check behavior, not just the Console
Reload after saving. Test the sequence below in the same page session, including clearing the query after narrowing the list:
| Input | Expected list | Expected count |
|---|---|---|
| Empty initially | All original titles | 3 drafts |
| release | Release notes | 1 draft |
| Clear the input | All original titles return | 3 drafts |
| BUDGET | Budget draft | 1 draft |
| Only spaces | All original titles | 3 drafts |
| zzz | No list items | 0 drafts |
Check that the input remains keyboard accessible and the status announces the updated count without moving focus.
Changing just const drafts to let drafts stops the TypeError, but repeatedly filters an already narrowed array. After searching for release, clearing the input still leaves only Release notes. That is a different bug, and a no-error Console does not catch it.
Distinguish mutation from replacement
A const binding to an array does not freeze that array. A call such as drafts.push("Meeting outline") changes its contents; an assignment such as drafts = [] attempts to replace the binding and fails. Neither operation is needed for this search: the source should remain intact while the displayed result changes.
The render function creates list elements and assigns textContent, so titles are displayed as text rather than interpreted as HTML. This also keeps the example's UI update separate from the array-binding question.
If the repaired page still behaves incorrectly
- The same assignment error remains: confirm the edited file is the one open in the browser and follow the current error line. Another const reassignment may exist.
- The count changes twice or resets unexpectedly: check that the old listener was replaced, not left beside a second listener.
- Clearing fails to restore all titles: check whether the original array was reassigned, mutated, or replaced with a filtered copy earlier.
- No handler runs: inspect earlier errors and element lookup results before changing declarations.
Try the tempting one-word repair
In a separate copy of the broken page, change only const drafts to let drafts. Search for release, clear the input, then search for budget. What survives?
Answer: release leaves one source entry, clearing keeps that one, and budget then filters it to zero. The other titles were discarded from the working source. Restore the original const source and the derived local result, then repeat the sequence: one, three, one. The smallest correct repair preserves the behavior the page needs.
Reference: MDN on invalid const assignment.
About
Trace a const reassignment inside an input handler, preserve the original list, and verify that filtering and clearing the query both work.
- Responsible
- Jay P
- Last Update
- 09/06/2026
- Reading Time
- 5 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.