Why Does the If Statement Only Run Once?

If you've ever built a toggle menu or an accordion in JavaScript, you might have run into a baffling issue: you click once, it opens; you click again, it closes; but on the third click, nothing happens. It feels as if your if statement has permanently stopped working.

Here is the culprit code pattern:

function pullDown() {
  if (document.getElementById("dropdown").style.height == 0) {
    document.getElementById("dropdown").style.height = "50px";
    document.getElementById("menu").style.top = "50px";
  } else {
    document.getElementById("dropdown").style.height = "0px";
    document.getElementById("menu").style.top = "0px";
  }
}

The Hidden Bug: JavaScript Type Coercion

The problem is not that the function stops running, but rather that the conditional check style.height == 0 behaves unexpectedly across consecutive clicks due to how JavaScript coerces data types:

  • Click 1: Initially, the inline style.height property is an empty string ("") because it is defined in an external or internal CSS stylesheet, not inline. In JavaScript, "" == 0 evaluates to true. The if block runs and sets style.height = "50px".
  • Click 2: style.height is now "50px". Because "50px" == 0 is false, the else block executes and sets style.height = "0px".
  • Click 3 and beyond: style.height is now the string "0px". When JavaScript tries to compare "0px" == 0, it converts "0px" to a number, which results in NaN (Not a Number). Since NaN == 0 is false, the code falls into the else branch every single time from this point forward!

Solution 1: The Best Practice (CSS Classes & classList.toggle)

Directly reading and mutating inline styles inside your JavaScript logic is prone to bugs and violates the separation of concerns. The cleanest, modern way to handle animated dropdowns is by toggling a CSS class using element.classList.toggle().

HTML

<div id="dropdown">
  <p>dropdown!</p>
</div>

<div id="menu">
  <p>menu</p>
</div>

CSS

body {
  margin: 0;
  padding: 0;
}

#dropdown {
  height: 0;
  overflow-y: hidden;
  background-color: #04AA6D;
  position: fixed;
  transition: height 1s ease;
}

#menu {
  background: rgb(44, 122, 190);
  position: absolute;
  top: 0;
  transition: top 1s ease;
  cursor: pointer;
}

/* The active/open state */
#dropdown.open {
  height: 50px;
}

#menu.open {
  top: 50px;
}

JavaScript

const menu = document.getElementById('menu');
const dropdown = document.getElementById('dropdown');

menu.addEventListener('click', () => {
  menu.classList.toggle('open');
  dropdown.classList.toggle('open');
});

With this approach, CSS handles layout and transitions, while JavaScript simply manages state. This avoids string comparisons entirely and eliminates unpredictable coercion bugs.


Solution 2: Quick Inline Fix (Matching Exact Units)

If you must inspect inline styles directly without restructuring your code, make sure you compare exact string values, including units (e.g., "0px" instead of 0), and check for the initial empty string (""):

function pullDown() {
  const dropdown = document.getElementById("dropdown");
  const menu = document.getElementById("menu");
  
  const currentHeight = dropdown.style.height;

  // Check for both the default empty string and "0px"
  if (currentHeight === "" || currentHeight === "0px") {
    dropdown.style.height = "50px";
    menu.style.top = "50px";
  } else {
    dropdown.style.height = "0px";
    menu.style.top = "0px";
  }
}

Summary

  • Comparing CSS dimensions with plain numbers (like "0px" == 0) fails because strings with unit suffixes convert to NaN.
  • Unset inline styles return an empty string (""), not their stylesheet values.
  • Prefer CSS class toggling (classList.toggle) combined with addEventListener over inline style manipulation for predictable, maintainable UI behavior.