3

I'm not sure if what i'm asking is possible. But i can't find anything on Google to figure this out. Here's what i'm trying to do... I have 5 input tags, all of them are numbers. I want the border to become green if the value is a valid number(number within max/min value), if letters(or invalid numbers) are present, i want the border to become red. I tried to figure this out on my own yesterday, but cannot.

HTML

 window.onkeyup = function(e) {
   var inputs = document.getElementsByClassName("C1");
   for (i = 0; i < inputs.length; i++) {
     inputsVal = inputs[i].value;
     if (!inputsVal || inputsVal == "" || inputsVal == " ") {
       return false
     }
   }
   document.getElementById('enterSign').style.display = "block";
   document.getElementById('spacer').style.display = "none";
   if (event.which == 13) {
     saveitem();
   }
 };
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <center>
    <p id='spacer'>&nbsp;</p>
    <p id='enterSign' onclick='saveitem()'>Press Enter</p>
  </center>
  <center>
    <button id='btnReset' onclick='resetPage()'>Reset</button>
  </center>
</div>

Currently, the code will return false unless all fields are entered. Then a div will appear that will instruct the user to press enter. But before that, i would like for users to get a sense of what information is valid before they move on to the next field. Thank you in advance to anyone that can both help me get this, and help me understand more about how to use JavaScript to handle these things. (please, no jquery or other libraries.) i'm trying to self learn JS.

5 Answers 5

4

One approach is the following:

window.onkeyup = function(e) {

  // getting all the <input> elements with the class of 'C1':
  var inputs = document.querySelectorAll('.C1'),

  // variables for later use within the loop:
    current, min, max, test;

  // iterating over the <input> elements with a
  // for loop:
  for (var i = 0, len = inputs.length; i < len; i++) {

    // caching the current <input> element:
    current = inputs[i];

    // getting the value of the min and max attributes,
    // parsed as a number in base 10:
    min = parseInt(current.min, 10);
    max = parseInt(current.max, 10);

    // testing whether the current value is
    // equal to, or greater than, the min AND
    // is equal to, or less than, the max:
    isValid = current.value >= min && current.value <= max;

    // if the current value is not the default value
    // (so the user has made a change to the held value):
    if (current.value !== current.defaultValue) {

      // if the number is valid:
      if (isValid) {

        // we remove the 'invalid' class-name (if it's there):
        current.classList.remove('invalid');

        // we add the 'valid' class-name:
        current.classList.add('valid');
      } else {

        current.classList.remove('valid');
        current.classList.add('invalid');
      }
    }
  }


  document.getElementById('enterSign').style.display = "block";
  document.getElementById('spacer').style.display = "none";
  if (event.which == 13) {
    saveitem();
  }
};

window.onkeyup = function(e) {

  var inputs = document.querySelectorAll('.C1'),
    current, min, max, test;

  for (var i = 0, len = inputs.length; i < len; i++) {

    current = inputs[i];

    min = parseInt(current.min, 10);
    max = parseInt(current.max, 10);

    isValid = current.value >= min && current.value <= max;
    if (current.value !== current.defaultValue) {
      if (isValid) {
        current.classList.remove('invalid');
        current.classList.add('valid');
      } else {

        current.classList.remove('valid');
        current.classList.add('invalid');
      }
    }
  }


  document.getElementById('enterSign').style.display = "block";
  document.getElementById('spacer').style.display = "none";
  if (event.which == 13) {
    saveitem();
  }
};
.valid {
  border-color: limegreen;
}
.invalid {
  border-color: red;
}
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <p id='spacer'>&nbsp;</p>
  <p id='enterSign' onclick='saveitem()'>Press Enter</p>

  <button id='btnReset' onclick='resetPage()'>Reset</button>

</div>

JS Fiddle demo.

The above approach has the window 'listening' for the keyup interaction in order to provide the event-handling, which means that not only does that event have to propagate from the <input> element all the way through each and every ancestor element before the function will be called, it's also possible to accidentally call event.stopPropagation() on one of the parent elements which, depending on the event-propagation being stopped, might prevent the function from ever being called.

You could, of course, attach the event-listener to a closer common ancestor to the <input> elements, such as the parent <div>:

var commonAncestor = document.getElementById('nav');

commonAncestor.onkeyup = function(e) {
 // ... all contents removed for brevity
};

var commonAncestor = document.getElementById('nav');

commonAncestor.onkeyup = function(e) {

  var inputs = document.querySelectorAll('.C1'),
    current, min, max, test;

  for (var i = 0, len = inputs.length; i < len; i++) {

    current = inputs[i];

    min = parseInt(current.min, 10);
    max = parseInt(current.max, 10);

    isValid = current.value >= min && current.value <= max;
    if (current.value !== current.defaultValue) {
      if (isValid) {
        current.classList.remove('invalid');
        current.classList.add('valid');
      } else {

        current.classList.remove('valid');
        current.classList.add('invalid');
      }
    }
  }


  document.getElementById('enterSign').style.display = "block";
  document.getElementById('spacer').style.display = "none";
  if (event.which == 13) {
    saveitem();
  }
};
.valid {
  border-color: limegreen;
}
.invalid {
  border-color: red;
}
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <p id='spacer'>&nbsp;</p>
  <p id='enterSign' onclick='saveitem()'>Press Enter</p>

  <button id='btnReset' onclick='resetPage()'>Reset</button>

</div>

JS Fiddle demo.

Or you could implement this functionality with CSS, using the :valid and :invalid selectors:

:valid {
  border-color: green;
}
:invalid {
  border-color: red;
}

:valid {
  border-color: green;
}
:invalid {
  border-color: red;
}
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <p id='spacer'>&nbsp;</p>
  <p id='enterSign' onclick='saveitem()'>Press Enter</p>

  <button id='btnReset' onclick='resetPage()'>Reset</button>

</div>

JS Fiddle demo.

There is, of course, the range selectors — :in-range and :out-of-range — which provide similar functionality as above:

:in-range {
  border-color: green;
}
:out-of-range {
  border-color: red;
}

:in-range {
  border-color: green;
}
:out-of-range {
  border-color: red;
}
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <p id='spacer'>&nbsp;</p>
  <p id='enterSign' onclick='saveitem()'>Press Enter</p>

  <button id='btnReset' onclick='resetPage()'>Reset</button>

</div>

JS Fiddle demo.

References:

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for this, i really appreciate this. You have answered my question, provided references for me to learn more, and you made comments in your code to explain what is going on. Thank you!
You're very welcome indeed, I'm glad to have been of help! :)
Good answer indeed, but you too should mention, that the whole concept of this shouldn't be used since its unneccesary to loop over all inputs on every keyup.
Listening to keyup on every element on the page and then iterating all inputs is not a performant or scalable way to do this. I'd look at event delegation as mention in my answer. However the validation function itself is still useful. I'd just be careful binding events to the window like this.
Absolutely, I agree with the both of you - Kryptik and Matt - hence my suggestion of using CSS; it's just I've not had a chance to add the additional information yet.
|
1

https://jsfiddle.net/t3ktjez2/

You want to do something like this where you bind the event to each input so they handle themselves individually rather than looping over them on all on key press of the window.

var inputs = document.getElementsByClassName('C1');
for (var i = 0; i < inputs.length; i++) {
    inputs[i].addEventListener("keyup", function(e) {
        var input = e.target;
        // Check for number
        if (e.target.value) {
            input.style.borderColor= "green";
        } else {
             input.style.borderColor= "red";
        }
    });   
}

Edit: In fact the most performant way to do this would be to let the keyup event bubble up to the #nav element and then do the check there.

https://jsfiddle.net/t3ktjez2/1/

This means you do not have to bind many events to each input and instead, have the one which checks to see which element caused the keyup event.

var nav = document.getElementById('nav');
nav.addEventListener("keyup", function(e) {
    var el = e.target;
    if (el.className === 'C1') {
        // The element that trigger this event is an input so do check
        if (el.value) { // Do a real value check here
            el.style.borderColor = "green";
        } else {
            el.style.borderColor = "red";
        }   
    }
});   

Comments

1

You should think about your whole approach, because in my opinion, looping through all your input elements onkeyup is unnecessary. Maybe use a focus out event or something similar.

But to answer your question, since you check the value of the input onkeyup, why not just manipulate the style of this element after you checked it:

window.onkeyup = function(e) {
     var formValid = true;

     var inputs = document.getElementsByClassName("C1");
     for (i = 0; i < inputs.length; i++) {
         inputsVal = inputs[i].value;
         if (!inputsVal || inputsVal == "" || inputsVal == " ") {
              formValid = false;
              inputs[i].style.borderColor= "red";
         } else {
              inputs[i].style.borderColor= "green";
         }
     }

     document.getElementById('enterSign').style.display = "block";
     document.getElementById('spacer').style.display = "none";
     if (event.which == 13) {
         saveitem();
     }

     return formValid;
};

But this will change the color of all your inputs at once, after every keypress.

Here is an example fiddle: https://jsfiddle.net/hu2amocq/

Another thing to mention: Since you're using input type="number" most browsers will add control buttons to the form element as you can see in the fiddle. Using these to increase / decrease the number won't trigger your window.onkeyup event.


Better approach

As i said at the beginning, you should think about your concept and only evaluate the input when necessary and only the one input which changed.

Take a look at http://www.w3schools.com/jsref/dom_obj_event.asp and use one of the events listed there. For example like this:

<input type="number" onkeyup="validate('speed')" id="speed" />

validate = function(id) {
      var input = document.getElementById(id);
      var value = input.value.trim();
      if (value && value.length > 0) {
          document.getElementById(id).style.borderColor = "green";
      } else {
          document.getElementById(id).style.borderColor = "red";
     }
 }

2 Comments

well, this is a good start. :) I'm sure i can figure out how to play with this further. I'm going to wait before i accept, but i do like this one.
i could change the red to just black. that would give it a good feel to it to compete with my loop. But i will try to learn a better way to approach this as well.
1

First of all I would bind the events to the input fields instead of the window. This way you would not listen for every onkeyup event fired. Then you will have to get the min and max values, compare these and make the color (css/styling) changes.

Here is an incomplete example for you. By incomplete I mean that it does not take care of all scenarios - as an example using the up and down buttons and writing text instead of numbers. But it will give you an idea of how this could be implemented. It of course also does what you ask :)

Note: I created two css classes invalid and valid. I believe that styling should be done by css, and not javascript - classes are great for this.

var inputs = document.getElementsByClassName('C1');

for (var j = 0; j < inputs.length; j++){
  var input = inputs[j];
  input.addEventListener('keyup', function(e){
    var ele = e.target;
    var inputsVal = ele.value;

    var min = parseInt(ele.getAttribute("min"));
    var max = parseInt(ele.getAttribute("max"));

    console.log(ele, inputsVal, min, max);
    if (inputsVal && inputsVal <= max && inputsVal >= min) {
      ele.classList.add('valid');
      ele.classList.remove('invalid');
    }else {
      ele.classList.add('invalid');
      ele.classList.remove('valid');
    }
  });
}

var inputs = document.getElementsByClassName('C1');

for (var j = 0; j < inputs.length; j++){
  var input = inputs[j];
  input.addEventListener('keyup', function(e){
    var ele = e.target;
    var inputsVal = ele.value;
    
    var min = parseInt(ele.getAttribute("min"));
    var max = parseInt(ele.getAttribute("max"));
    
    console.log(ele, inputsVal, min, max);
    if (inputsVal && inputsVal <= max && inputsVal >= min) {
      ele.classList.add('valid');
      ele.classList.remove('invalid');
    }else {
      ele.classList.add('invalid');
      ele.classList.remove('valid');
    }
  });
}
.invalid {
 border-color:red; 
  }

.valid{
  border-color:green;
  }
<div id='nav'>
  <label for='speed'>&nbsp;Speed
    <br />
    <input type='number' class='C1' id='speed' placeholder='1-10' max='10' min='1' />
  </label>
  <br />
  <label for='Iron'>&nbsp;Iron
    <br />
    <input type='number' class='C1' id='Iron' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Ice'>&nbsp;Ice
    <br />
    <input type='number' class='C1' id='Ice' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Mass'>&nbsp;Mass
    <br />
    <input type='number' class='C1' id='Mass' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <label for='Diameter'>&nbsp;Diameter
    <br />
    <input type='number' class='C1' id='Diameter' placeholder='1-5' max='5' min='1' />
  </label>
  <br />
  <center>
    <p id='spacer'>&nbsp;</p>
    <p id='enterSign' onclick='saveitem()'>Press Enter</p>
  </center>
  <center>
    <button id='btnReset' onclick='resetPage()'>Reset</button>
  </center>
</div>

Comments

0

u can use jQuery and listen for blur on inputs

Fiddle

jQuery('input[type=number]').on('blur',function(){
var min = $(this).attr('min');
var max = $(this).attr('max');
if(min <= $(this).val() && $(this).val() <= max ) $(this).css('border','1px solid green');
});

This is just suggestion :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.