4

I have the following code with following conditions:

  • A value less than 60 seconds is fast
  • A value more than 60 seconds is slow
  • Must fill out both input fields to show "fast" or "slow"

My only problem is that the code interprets 0 as null. So, If I put 1 minutes 0 seconds, then nothing shows.

I want it to show "fast" when I enter 1 minute 0 seconds.

Thanks in advance.

<html>
<head>

  <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>

  <!-- TODO: Missing CoffeeScript 2 -->

  <script type="text/javascript">//<![CDATA[

    $(window).load(function(){
      
        $('input').keyup(function(){
            var min = 60 * Number($('#D').val());
            var sec = Number($('#E').val());

            if ((min+sec) <= 60) {document.getElementById("Run").innerHTML = "Fast";}
            if ((min+sec) >= 61) {document.getElementById("Run").innerHTML = "Slow";}
            
            if (min == null | min == "") {document.getElementById("Run").innerHTML = "";}
            if (sec == null | sec == "") {document.getElementById("Run").innerHTML = "";}
            
        });
        
 });
</script>

<style>
 h1 { margin: 0 10px 25px 5px; padding: 5px; font-size: 32px; font-family: Arial }
 input { margin: 0 10px 10px 10px; padding: 5px; font-size: 24px; width: 200px }
    label { margin: 0 10px 10px 10px; font-size: 20px; display: block; font-family: Arial }
 span { margin: 0 0px 0px 10px; font-size: 44px; font-family: Arial }
 stat { margin: 0 0px 0px 10px; font-size: 24px; font-family: Arial }
</style>

</head>
<body>
    
    <input id="D"
     type="number" 
        class="form-control formBlock" 
        placeholder="Minutes" 
        required="" 
        min="1" 
        max="59"
        onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }"
        >
    <stat id="Run"></stat><br>
    <input id="E" 
     type="number" 
     class="form-control formBlock" 
     placeholder="Seconds" 
        required="" 
        min="1" 
        max="59"
        onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }"
        >
        <br>
    <br>

  <script>
    // results
    if (window.parent && window.parent.parent){
      window.parent.parent.postMessage(["resultsFrame", {
        height: document.body.getBoundingClientRect().height,
        slug: ""
      }], "*")
    }
    window.name = "result"
    
    //
    
  </script>

</body>

</html>
Jim O.
  • 1,091
  • 12
  • 31
  • Can you explain what you mean by "the code interprets 0 as null?" – Jacob May 10 '20 at 23:56
  • 8
    [`===`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity) – Marco Bonelli May 10 '20 at 23:56
  • Thanks, Marco. If you post an answer to my question, then I will select it as the correct answer. – Jim O. May 10 '20 at 23:57
  • @MarcoBonelli Strict equality doesn't solve the problem here. Both conditions would simply never be entered if `===` is used since a type "number" can't ever be `=== null` or `=== ''`. – plalx May 11 '20 at 01:10
  • @plalx a type number will be `""` with any NaN values, or without any value. To OP, note that you have your `min` attributes set to `"1"`, if you really want to allow `"0"`, then set them to `"0"` ;-) – Kaiido May 11 '20 at 01:27
  • @Kaiido Yeah but the OP converts the input values using `Number`, so it can't be `''`. Not sure what was your point? `===` doesn't help in any way in the above logic and that was mine :P – plalx May 11 '20 at 01:30
  • What did you call *a type "number"* there? I understood it as so my point was that this can have a value of `""`. If you meant a Number as the js type, then I would have misread your comment – Kaiido May 11 '20 at 01:41
  • @Kaiido Yeah I meant typeof, sorry for the confusion. – plalx May 11 '20 at 01:41

3 Answers3

3

"My only problem is that the code interprets 0 as null"

That's an incorrect assumption, since Number('0') == null is false.

The reason your code behaves like that is 0 == '' is true in JavaScript. You could use === for strict equality, but then both of your conditions would never be entered since min nor sec can be === null or === ''.

min and sec are assigned the result of a number conversion which can only yield a valid number or NaN. Anything that doesn't coerce to a number would give NaN. Unfortunately blank strings does coerce to 0, but you can use the || operator to coerce '' into NaN, since empty string is the only falsy value you can get from your input values (given type="number").

From that point the values can either be numbers or NaN. Given that NaN + number gives out NaN you can safely assume that if isNaN(min + sec) then one of the inputs were invalid or blank.

Also note that you're using | (bitwise or) instead of ||. The if branching outcome was the same as || in your code because the left-hand-side and right-hand-side were booleans, but it's more semantically correct to use ||.

EDIT: @Kaiido makes a point using input.valueAsNumber, which properly coerces '' to NaN already, removing the need for Number(input.value || NaN). See his answer for a slightly better solution.

<html>
<head>

  <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>

  <!-- TODO: Missing CoffeeScript 2 -->

  <script type="text/javascript">//<![CDATA[

    $(window).load(function(){
      
        $('input').keyup(function(){
            const min = 60 * Number($('#D').val() || NaN);
            const sec = Number($('#E').val() || NaN);
            const total = min + sec;

            const speed = isNaN(total)? '' : (total <= 60? 'Fast' : 'Slow');
            
            document.getElementById("Run").innerHTML = speed;     
        });
        
 });
</script>

<style>
 h1 { margin: 0 10px 25px 5px; padding: 5px; font-size: 32px; font-family: Arial }
 input { margin: 0 10px 10px 10px; padding: 5px; font-size: 24px; width: 200px }
    label { margin: 0 10px 10px 10px; font-size: 20px; display: block; font-family: Arial }
 span { margin: 0 0px 0px 10px; font-size: 44px; font-family: Arial }
 stat { margin: 0 0px 0px 10px; font-size: 24px; font-family: Arial }
</style>

</head>
<body>
    
    <input id="D"
     type="number" 
        class="form-control formBlock" 
        placeholder="Minutes" 
        required="" 
        min="1" 
        max="59"
        onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }"
        >
    <stat id="Run"></stat><br>
    <input id="E" 
     type="number" 
     class="form-control formBlock" 
     placeholder="Seconds" 
        required="" 
        min="1" 
        max="59"
        onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }"
        >
        <br>
    <br>

  <script>
    // results
    if (window.parent && window.parent.parent){
      window.parent.parent.postMessage(["resultsFrame", {
        height: document.body.getBoundingClientRect().height,
        slug: ""
      }], "*")
    }
    window.name = "result"
    
    //
    
  </script>

</body>

</html>
plalx
  • 42,889
  • 6
  • 74
  • 90
2

Marco's got the fix for your immediate problem, where you need to compare for type as well as value by using === (strict comparison). More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators

You also have an issue with your logic, where you are using the bitwise OR | in your conditionals. I'm sure you mean to use a normal OR comparison, ||:

        if (min === null || min === "") {document.getElementById("Run").innerHTML = "";}
        if (sec === null || sec === "") {document.getElementById("Run").innerHTML = "";}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators

Ed Lucas
  • 5,955
  • 4
  • 30
  • 42
  • In practice using bitwise with booleans gives the same result as the `||` operator. Also, `min` and `sec` could never `=== null` since their only value is either a valid number (empty string also coerces to `0`). Therefore, the two conditions you wrote can never be true and will never be entered. – plalx May 11 '20 at 01:06
1

When using type="number" inputs, use it completely.

They do offer a valueAsNumber property getter, which will coerce your values directly to Numbers, so all you have to do next, is to use them after a fool-guard check against isNaN():

$(window).load(function() {

  const min_input = $('#D')[0];
  const sec_input = $('#E')[0];
  const run_elem = document.getElementById("Run");

  $('input').keyup(function() {
    var min = 60 * min_input.valueAsNumber;
    var sec = sec_input.valueAsNumber;

    if( isNaN(min + sec) ) {
      run_elem.innerHTML = "";
    }
    else if ((min + sec) <= 60) {
      run_elem.innerHTML = "Fast";
    }
    else if ((min + sec) >= 61) {
      run_elem.innerHTML = "Slow";
    }

  });

});
h1 {
  margin: 0 10px 25px 5px;
  padding: 5px;
  font-size: 32px;
  font-family: Arial
}

input {
  margin: 0 10px 10px 10px;
  padding: 5px;
  font-size: 24px;
  width: 200px
}

label {
  margin: 0 10px 10px 10px;
  font-size: 20px;
  display: block;
  font-family: Arial
}

span {
  margin: 0 0px 0px 10px;
  font-size: 44px;
  font-family: Arial
}

stat {
  margin: 0 0px 0px 10px;
  font-size: 24px;
  font-family: Arial
}
<script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>
<input id="D" type="number" class="form-control formBlock" placeholder="Minutes" required="" min="0" max="59" onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }">
<stat id="Run"></stat><br>
<input id="E" type="number" class="form-control formBlock" placeholder="Seconds" required="" min="0" max="59" onkeyup="if(parseInt(this.value) > 59){ this.value = 59; return false; }">
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • +1 Didin't know about `valueAsNumber` which makes my original answer much simpler given you don't have to care about `Number('')` being `0`. One point though, you don't have to check `>= 61`, just use else and avoid duplicating the sum logic. – plalx May 11 '20 at 01:48