0
\$\begingroup\$

I'm making a down payment form where the user enters a purchase price and can then enter the amount they want to pay as a down payment as either a dollar amount, a percent down or by using a range slider. Modifying one field will modify the others as they type.

// $ Down and % Down are readonly until purchase price entered
$('.editable input').prop('readonly', true);
$('.purchase-price input').bind('blur', function () {
    if( $(this).val() ) {
        $('.editable input').prop('readonly', false);
    } else {
        $('.editable input').prop('readonly', true);
    }

    // Remove 0 value
    $('.editable input').val('');  
    
});

// Add commas as you type
$('.currency input').keyup(function (e) {

    // skip for arrow keys
    if (e.which >= 37 && e.which <= 40) return;

    // format number
    $(this).val(function (index, value) {
        return value
            .replace(/\D/g, "")
            .replace(/\B(?=(\d{3})+(?!\d))/g, ",")
            ;
    });
});  

// Convert variables with commas into numbers
function stringToNumber(number) {
    var numberNoCommas = number.replace(/,/g, '');
    return parseFloat(numberNoCommas);
}    

// Only allow numbers in fields with the numbers class
$(".numbers").bind("keypress", function (e) {
    var keyCode = e.which ? e.which : e.keyCode
    if (!(keyCode >= 48 && keyCode <= 57)) {
        return false;
    }
});  

// $ Amount Down
$('.amount-down input').on('input', function(){
    var purchasePrice = stringToNumber( $('.purchase-price input').val() );

    // Amount down can't exceed purchase price
    if ($(this).val() > purchasePrice) {
        $(this).val((purchasePrice));
    }

    var amountDown = stringToNumber( $(this).val() );
    var howMuchDown = ( amountDown / purchasePrice ) * 100;         

    // Update % Down text field
    $('.percent-down input').val( howMuchDown.toFixed(0) );

    // Update Range slider
    $('.down-payment input').val( amountDown ).trigger('change');

});    

// % Amount down
$('.percent-down input').on('input', function(){
    var purchasePrice = stringToNumber( $('.purchase-price input').val() );

    // Percent down can't exceed 100%
    if ( $(this).val() > 100 ) {
        $(this).val(100);
    } 

    var percentDown = $(this).val();
    var howMuchDown = ( purchasePrice * stringToNumber(percentDown) ) / 100;          

    // Update $ Down text field
    $('.amount-down input').val( Number((howMuchDown).toFixed(2)) );

    // Update Range slider
    $('.down-payment input').val( howMuchDown ).trigger('change');
});      

// Range Slider
$('.down-payment input').on('input', function(){
    var purchasePrice = stringToNumber( $('.purchase-price input').val() );
    var rangeValue = $(this).val();
    var howMuchDown = ( ( rangeValue * 100) / purchasePrice).toFixed(0);

    // Amount Down
    $('.amount-down input').val( Number(rangeValue).toLocaleString('en') );

    // % Down
    if(isNaN(howMuchDown)) {
        howMuchDown = 0;
    }

    // Percent Down
    $('.percent-down input').val(howMuchDown);
});   
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="field_12_45" class="purchase-price hide-instructions currency numbers"><label>What's the purchase price of the property?<input id="input_12_45" type="text" value="" class=""></div>
<br><br>
<div id="field_12_48" class="amount-down editable currency numbers"><label>$</label><input id="input_12_48" type="text" value="" class="" placeholder="0"></div>
<br><br>
<div id="field_12_47" class="percent-down editable numbers"><label>%</label><input id="input_12_47" type="text" value="" class="" placeholder="0"></div>
<br><br>
<div id="field_12_42" class="down-payment currency"><input id="input_12_42" type="range" step="1" min="0" value="" class="" max="500000"></div>

I would like to see if the code can be optimized further and I am concerned about performance and accuracy.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Regarding hypertext markup:

  • it’s considered bad practice to use <br /> elements for styling purposes, instead use CSS for styling. Same goes for inline style definitions; better to have them all in one place.
  • the id of elements are supposed to be unique across the whole document for it to be valid. When used in forms, it would be more helpful to give them semantic meaning (compare eg. document.forms.downpay.percent versus document.forms[0].input_12_47).
  • consider using form labels consistently, adding container elements for styling needs if needed: <label><span>Purchase price</span><input id="price" ... /></label>, allowing specific targeting in CSS: label span { ... }
  • I would consider using type="number" on the other fields, too: step="any" for the price input, perhaps, and step="0.01" for the "percent" input. Although, type="range" makes most sense to me for a percentage. If using type="text", then perhaps consider using hints such as inputmode="numeric" and pattern="\d*".

Regarding JS:

  • consider attaching the listener for the input event on the form itself, and branch using switch(event.target.id) to see which input element is getting the user input. I’m not saying that is the “correct” way, but it’s… neat. At least in smaller forms with co-dependent fields.
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.