Search code examples
javascriptundefinedparseint

parseInt fails in certain strict comparisons ( stringParsedToInteger === 3; // returns undefined)


Just curious:

My code works if I compare a number string (e.g. '10') to an integer (10), using the comparison operator ==.

But if I convert the string to an integer with parseInt() and compare to integers with the strict comparison operator === it fails – not immediately – once you start messing with the parameters (select menus).

The same code will work as expected without parseInt() and with comparison operators as in (time == 5).

Code that fails:

  const productSelect = document.querySelector('#product')
  let productValue = document.querySelector('#product').value
  const prodTimeSelect = document.querySelector('#prodTime')
  let prodTime = parseInt(document.querySelector('#prodTime').value)
  const text = document.querySelector('.textoutput')
 
  // Find start cost
  let findStartCost = function(time) {
    if (time === 5) {
      return startCost = 2000
    } else if (time === 10) {
      return startCost = 1500
    } else if (time === 20) {
      return startCost = 1000
    }
  }
  
  text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`

  // Update text if product changes
  productSelect.addEventListener('change', function(e) {
    productValue = e.target.value
    text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`
  })

  // Update text if production time changes
  prodTimeSelect.addEventListener('change', function(e) {
    prodTime = e.target.value
    text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`
  })
<h3>Select Product</h3>
  <select id="product">
    <option value="1">Product 1</option>
    <option value="2">Product 2</option>
    <option value="3">Product 3</option>
    <option value="4">Product 4</option>
    <option value="5">Product 5</option>
  </select>

  <h3>Select production time</h3>
    <select id="prodTime">
      <option value="5" selected="selected">5 days</option>
      <option value="10">10 days</option>
      <option value="20">20 days</option>
    </select>

<p class="textoutput"></p>

Code that works:

const productSelect = document.querySelector('#product')
let productValue = document.querySelector('#product').value
const prodTimeSelect = document.querySelector('#prodTime')
let prodTime = document.querySelector('#prodTime').value
const text = document.querySelector('.textoutput')
 
// Find start cost
let findStartCost = function(time) {
  if (time == 5) {
   return startCost = 2000
  } else if (time == 10) {
   return startCost = 1500
  } else if (time == 20) {
   return startCost = 1000
 }
   }
  
text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`

// Update text if product changes
productSelect.addEventListener('change', function(e) {
 productValue = e.target.value
 text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`
  })

// Update text if production time changes
  prodTimeSelect.addEventListener('change', function(e) {
prodTime = e.target.value
text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`
  })
<h3>Select Product</h3>
      <select id="product">
        <option value="1">Product 1</option>
        <option value="2">Product 2</option>
        <option value="3">Product 3</option>
        <option value="4">Product 4</option>
        <option value="5">Product 5</option>
      </select>

      <h3>Select production time</h3>
        <select id="prodTime">
          <option value="5" selected="selected">5 days</option>
          <option value="10">10 days</option>
          <option value="20">20 days</option>
        </select>

    <p class="textoutput"></p>


Solution

  • Inside the listener, when you do

    prodTime = e.target.value
    

    This retrieves the changed value, but the .value of an HTMLElement will always be a string. So when you pass that string to findStartCost with findStartCost(prodTime), your conditions:

    if (time === 5) {
      return startCost = 2000
    } else if (time === 10) {
      return startCost = 1500
    } else if (time === 20) {
      return startCost = 1000
    }
    

    always fail because the parameter time is always a string, not a number, so it'll never be === to a number.

    ==, in contrast, implicitly casts its operands when comparing, but it's unpredictable if you haven't memorized all the different possibilities, so it shouldn't be used.

    Use === and explicitly cast the values yourself:

      prodTimeSelect.addEventListener('change', function(e) {
        prodTime = Number(e.target.value);
        text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`;
      })
    

    Also, better not to implicitly create global variables - there's no need for a startCost variable, you can just return the plain value in findStartCost.

    You may also make the code less repetitive by having just a single function that retrieves the values from the selects and populates the text.

    const productSelect = document.querySelector('#product');
    const prodTimeSelect = document.querySelector('#prodTime');
    const text = document.querySelector('.textoutput');
    
    // Find start cost
    let findStartCost = function(time) {
      if (time === 5) {
        return 2000;
      } else if (time === 10) {
        return 1500;
      } else if (time === 20) {
        return 1000;
      }
    }
    
    const updateText = () => {
      const productValue = productSelect.value;
      const prodTime = Number(prodTimeSelect.value);
      text.textContent = `The product number is #${productValue} and the production time is ${prodTime} days. Includes a start cost of ${findStartCost(prodTime)}.`;
    };
    updateText();
    productSelect.addEventListener('change', updateText);
    prodTimeSelect.addEventListener('change', updateText);
    <h3>Select Product</h3>
    <select id="product">
      <option value="1">Product 1</option>
      <option value="2">Product 2</option>
      <option value="3">Product 3</option>
      <option value="4">Product 4</option>
      <option value="5">Product 5</option>
    </select>
    
    <h3>Select production time</h3>
    <select id="prodTime">
      <option value="5" selected="selected">5 days</option>
      <option value="10">10 days</option>
      <option value="20">20 days</option>
    </select>
    
    <p class="textoutput"></p>