Stephen R
Stephen R

Reputation: 3897

HTML "number" input not allowing JavaScript to display commas

NOTE: I know this is similar to other questions, but for semantic and other reasons (e.g. ease of input on iOS) I specifically want the HTML input to be type="number". This is where the problem comes in....

I'm trying to set up an HTML form so that number fields show thousands commas -- e.g. "10,000,000" instead of "10000000". I want to set it so that the field displays the commas, but when it gets focus for actual editing the commas go away.

I can manually add commas to the value without any issue (I'm testing mainly in Firefox ~59); but any time I try to have JavaScript add the commas, the field is blanked out instead. Does anyone know how to make this work?

(Note, I'm using Numerals.js for formatting here... http://numeraljs.com/ )

Here is what I have:

$(document).ready(function(){
    var numberFields = $("input[type=number]");
    numberFields.each( function(){
        $(this).val( numeral( $(this).val() ).format('0') );
    });
    numberFields.focus( function(){
        $(this).val( numeral( $(this).val() ).format('0') );
    });
    numberFields.blur( function(){
        $(this).val( numeral( $(this).val() ).format('0,0') );
    });
});

Example HTML input:

<input name="myField" value="0" type="number">

(Incidentally -- and conveniently, I've confirmed that submitting a number with commas to the HTML form processing script just drops the commas and puts the unformatted number into the DB. Sweet!)

Upvotes: 0

Views: 1882

Answers (3)

Stephen R
Stephen R

Reputation: 3897

My final function, based on Dave's answer. Dave's was not fully functional (though an effective proof of concept). This handles the name attribute (which is necessary for Form submit), and positions the input overlay relative to the original input, rather than the page (which prevents things going catywampus if the window is resized).

IMPORTANT: When using this, you must use <label>...</label><input>, NOT <label>...<input></label> for your number fields Fixed!:

$(document).ready(function() {
      format_integers();
    });

    /**
     * Any form inputs with type=number and data-format=integer will display contents with commas when input not in focus.
     * @param container string to be used as jQuery search. Defaults to 'body'; but can be any specific element
     */
    function format_integers(container) {
      container = (typeof container !== 'undefined') ? container : 'body';
      var $wrapper = $('<span>').css({
        position: 'relative',
        display: 'inline-block',
        padding: 0
      });
      $(container + " input[type='number'][data-format=integer]").each(function() {
        var $clone = $(this).clone();
        var $parentLabel = $(this).parent('label');
        if( $parentLabel.length !== 0 ) {
            $parentLabel.css( 'display', 'inline-flex' );
            $clone.css( 'order', 1 );
            $(this).css( 'order', 2 );
        }
        $(this).wrapAll($wrapper).css({
            position: 'absolute',
            top: 0,
            left: 0,
            opacity: 0
          })
          .attr('pattern', '[0-9]*');
        $clone.removeAttr('name class id pattern')
          .attr('type', 'text')
          .attr('tabindex', -1)
          .css('width', $(this).width)
          .val($(this).val() ? parseInt($(this).val()).toLocaleString() : '');
        $(this).before($clone);
        $(this).on('focus', function() {
          $(this).css('opacity', '1');
        });
        $(this).on('blur', function() {
          $(this).css('opacity', '0');
          $clone.val($(this).val() ? parseInt($(this).val()).toLocaleString() : '');
        });
      });
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
  <label>Integer Input:&nbsp; <input type="number" data-format="integer" name="test" id="num_input" value="123456789" /></label><br>
  <label for="num_input2">Integer Input: </label>
  <input type="number" data-format="integer" name="test2" id="num_input2" value="10000000" /><br>
  <label>Textarea <textarea>Something to focus other than the number fields</textarea></label>
</form>

Upvotes: 0

Stephen R
Stephen R

Reputation: 3897

NOTE: I'm leaving this original answer in case something here is useful down the road; but please see my other answer, which is a complete solution to the original question.

I've found no way to quite do what I want. Futzing with input "type" is too inconsistent in different browsers; and doesn't work anyway on iOS (which was 90% of the point). However, I did get "pretty close" working smoothly with the following. (NOTE this uses the numeral.js library for formatting):

JavaScript:

<script>
$(document).ready(function(){
    var intFields = $("input[data-format=integer]");
    intFields.each( function(){
        $(this).attr( "pattern", "[0-9,]*" );
        $(this).val( numeral( $(this).val() ).format('0,0') );
    });
    intFields.focus( function(){
        $(this).val( numeral( $(this).val() ).format('0') );
    });
    intFields.blur( function(){
        $(this).val( numeral( $(this).val() ).format('0,0') );
    });
    $("form#myForm").on( "submit", function(){
        intFields.each( function() {
            $(this).val( numeral( $(this).val() ).format('0') );
        } );
        return true;
    } );
});
</script>

HTML:

<input type="text" data-format="integer" name="some_number" value="12345678>">

Overall, WAAAY too much work for such a common use-case. I hope browsers makers come up with a solution to this soon! There needs to be a simple, standard way to display numbers in fields.

Upvotes: 0

dave
dave

Reputation: 64657

I'm not familiar with numeral.js, but if I were doing it, I would just save the numeric value as a data attribute, and then format with .toLocaleString, keeping in mind that you have switch between text and number types so that you can display your commas:

Seeing the issues with iOS, I believe the following will work. You can clone the element, THEN set the original to be a text input. Then, get the position of the original, and set the new element to be absolutely positioned over the original. Now, set the number input to opacity: 0, this way you won't see it, but when they click, it will click your clone. When the clone is clicked, set it to opacity: 1, and when it is blurred, set the original input to the cloned input's value, but using toLocaleString. I checked that it works in firefox, in theory it should work on iOS as well.

$(document).ready(function(){
        var $clones = [];
        var numberFields = $("input[type='number']");
        numberFields.each(function(i){
            var $clone = $(this).clone();
            $(this).attr('type', 'text');
            var destination = $(this).offset();
            var width = $(this).width();
            $clones.push($clone.css({
                position: 'absolute',
                top: destination.top,
                left: destination.left,
                opacity: '0',
                width: width
            }));
            $(this).after($clone);
            var that = this;
            $clone.on('focus', function() {
                $(this).css('opacity', '1');
            });
            $clone.on('blur', function() {
                $(this).css('opacity', '0');
                $(that).val('' + $(this).val() ? parseInt($(this).val()).toLocaleString() : '');
            });
        });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" pattern="[0-9,]*" />

Upvotes: 3

Related Questions