I’m trying to use a simple JavaScript if statement to check a condition and run some code, but it’s not behaving the way I expect. Sometimes the block never runs, other times it seems to always run, even when the condition should be false. I’m not sure if I’m comparing values wrong, dealing with types incorrectly, or missing some syntax. Can someone explain how to properly structure and debug a basic JavaScript if statement with clear examples so I can spot what I’m doing wrong?
Post your current code if you can, but here are the usual “my if is broken” causes in JavaScript.
- Using assignment instead of comparison
Wrong:
if (x = 5) {
console.log(‘runs’);
}
This sets x to 5 and the condition becomes true.
Use:
if (x === 5) {
console.log(‘runs’);
}
- Comparing string vs number
Wrong:
const value = ‘5’;
if (value === 5) {
// never runs
}
Either convert:
if (Number(value) === 5) { … }
Or compare strings:
if (value === ‘5’) { … }
- Truthy / falsy confusion
Values treated as false:
0, ‘’, null, undefined, NaN, false
Example:
const count = 0;
if (count) {
// will NOT run
}
You might want:
if (count === 0) { … }
- Condition always true
Example:
const isActive = ‘false’;
if (isActive) {
// runs, because non empty string is truthy
}
Use real booleans:
const isActive = false;
- Code never reached
Check for:
return above the if
throw above the if
event not firing
Example with a click handler:
document.getElementById(‘btn’).addEventListener(‘click’, function () {
const value = document.getElementById(‘inp’).value;
if (value === ‘yes’) {
console.log(‘if ran’);
} else {
console.log(‘else ran’);
}
});
Make sure:
• Script tag is at the end of body or runs after DOMContentLoaded
• Element ids match
• No JS errors before the if
- Loose vs strict comparison
== does type coercion.
=== does no coercion.
Safer to use === almost everywhere.
If you post a small snippet like:
- HTML
- Your exact if
- What you expect vs what happens
people can point to the exact bug.
Couple more angles to check that often bite people, on top of what @waldgeist already covered:
- Your
ifnever actually runs
Make sure the function containing theifis called.
function checkStuff() {
const value = getValueSomehow();
if (value === 'yes') {
console.log('if ran');
}
}
// Without this, nothing happens:
checkStuff();
Happens a lot with event handlers too: folks define them but never attach them.
- Mis‑reading console logs
Sometimes you log the same message in multiple places and think theifis “always” running. Try very explicit logs:
console.log('before if, value =', value);
if (value === 'yes') {
console.log('INSIDE IF');
} else {
console.log('INSIDE ELSE');
}
console.log('after if');
Then check the exact sequence in DevTools. It’s boring, but it instantly shows if the condition is actually the problem or if the code path is different than you think.
- “Condition looks right” but variable is not what you think
Log both the value and its type:
console.log('value:', value, 'type:', typeof value);
I’ve debugged so many “my if is broken” issues that were really “this variable is not what I thought it was at this point in the code” issues. Especially with async stuff.
- Async timing issues
If your value comes from fetch / AJAX / setTimeout /addEventListener, theifmight run before the data is ready.
Wrong:
let result;
fetch('/api')
.then(r => r.json())
.then(data => result = data);
// This runs immediately, before `result` is set
if (result) {
console.log('runs?');
}
Correct:
fetch('/api')
.then(r => r.json())
.then(data => {
if (data) {
console.log('now it runs at the right time');
}
});
So sometimes it “never runs” because by the time the value is ready, your if already executed with undefined.
- Braces in the wrong place
JS will happily accept this and do something you don’t expect:
if (value === 'yes')
console.log('if ran');
console.log('this runs ALWAYS'); // not inside if
Always use braces until you’re very sure:
if (value === 'yes') {
console.log('if ran');
console.log('this only runs when yes');
}
- Accidentally negating your logic
Classic:
// You *think* this means 'if user is logged in'
if (!isLoggedIn) {
showDashboard();
}
That ! flips it, so it runs when not logged in. If you see “it always runs when it shouldn’t”, double‑check you didn’t sneak in an extra ! or wrap the wrong part in parentheses:
if (!(user && user.isAdmin)) { ... } // very easy to misread
- Strict vs loose comparison nuance
I’d slightly disagree with treating===as a silver bullet like @waldgeist hinted. It’s safer, yes, but if you know you want loose matching from user input (e.g.'1'vs1from a text field), a deliberate==with clear intent can be fine:
// userInput is always a string from an <input>
if (userInput == 5) {
// okay, we *want* '5' and 5 to be the same here
}
The key is: if you use ==, do it on purpose and test edge cases (', 0, null etc).
If you can paste your actual if plus 3 lines before and after, and say “I expected X, I saw Y,” folks can probably point to the exact 1‑character issue. Right now it’s likley one of the above: not calling the function, async timing, or braces/negation in the wrong place.
Strip it back to the smallest failing example. Most “my if doesn’t work” threads get solved the moment you delete everything that is not strictly needed and the bug finally reveals itself.
1. Build a 5‑line reproduction
Create a new file with just:
<!doctype html>
<html>
<body>
<script>
const value = 'your test here';
if (value === 'yes') {
console.log('INSIDE IF');
} else {
console.log('INSIDE ELSE');
}
</script>
</body>
</html>
Now change value manually and verify the behavior. If this behaves correctly (it almost always will), the problem is not the if syntax but where in your real code it runs.
2. Check scope: are you reading the right variable?
A subtle one that neither @yozora nor @waldgeist focused on: you may have two variables with the same name.
let status = 'ok';
function check() {
let status; // shadows outer one, is undefined
if (status === 'ok') {
console.log('never runs');
}
}
If you declare let or const with the same name inside a function or block, you hide the outer value. Quick test: temporarily rename the variable in your if to something unique like debugStatus123 and see if behavior changes.
3. Operator precedence traps
Complicated conditions often look right but evaluate differently than you think:
// You think: logged in AND (is admin or editor)
if (isLoggedIn && isAdmin || isEditor) {
...
}
What JavaScript actually does:
if ((isLoggedIn && isAdmin) || isEditor) { ... }
So isEditor can pass the condition even if not logged in. Wrap your logic:
if (isLoggedIn && (isAdmin || isEditor)) { ... }
Any time the condition has both && and ||, add parentheses until it reads unambiguously.
4. Guard against NaN specifically
NaN is sneaky because it is falsy in comparisons but not equal to anything:
const n = Number('abc'); // NaN
if (n) {
// does not run
}
if (n === 0) {
// also does not run
}
if (n === NaN) {
// never true, NaN is never equal to NaN
}
If user input or math is involved, explicitly test:
if (Number.isNaN(n)) {
console.log('bad number');
}
Otherwise you can think “my if never runs” when the real problem is NaN quietly propagating.
5. Avoid mixing assignment and condition in the same line
The assignment vs comparison issue was mentioned already, but I’d actually go further and say: do not rely on assignments inside conditions at all when you are still debugging:
// Avoid during debugging
if (result = compute()) {
...
}
Split it:
const result = compute();
if (result) {
...
}
It is more verbose but you immediately see what value the condition gets.
6. Use the debugger, not just console.log
Console logs are fine, but DevTools breakpoints are more precise.
- Open DevTools.
- Go to Sources.
- Click on the line with your
if. - Reload or trigger the event.
- When it pauses, hover over the variables and see their exact values and types.
Step through line by line with “Step over”. You will see whether the if is reached and what the expression evaluates to, which sidesteps most guessing.
7. About the “product” angle
Since you mentioned readability: keeping your conditions small and explicit is effectively the “product” you are selling to your future self. The big pro is that it makes debugging if statements trivial and keeps your JavaScript if statement logic understandable months later. The con is a bit more verbosity and more lines, but that tradeoff is usually worth it.
Compared with what @yozora and @waldgeist already laid out, their answers cover the classic syntax and type pitfalls very well. The extra angle here is: simplify, isolate, check scope, and use the debugger so you are not guessing about what the condition actually sees.