Payment Amount Selector
Recently, I've received a couple opportunities to create payment forms where an amount is selected by tapping on it.
One was for how many items the person is ordering. The other was for how much money the person is contributing to the website.
Both use similar code. The amount being contributed contains currency formatting code that the number of items selection does not.
I'll provide code for selecting a contribution amount.
Here is an illustration. (When implementing for currencies other than USD, AUD, or other Dollar currency, replace "$" with the correct currency symbol.)
Select Amount
Payment amount: $
Tapping on an amount will fill in the "Payment amount" field below the selection boxes. The field is read-only so it can't be changed by the user.
Tapping the "Other" selection box allows an amount to be typed in. This is what gives the selector more than just a simple "tap to select" functionality.
As the "Other" selection box is first tapped, the "Payment amount" field is filled with "(tap to update)".
After the user types in an amount and taps on "(tap to update)" (or taps anywhere else on the page), the "Payment amount" field updates with whatever amount greater than zero is specified in the "Other" selection box.
Implementing the Amount Selector
The code that enables the illustration is provided here.
It is not simple. But I'll try to explain it well enough so it seems that way.
Let's first do the CSS, selection boxes, and "Payment amount" field. Later, we'll do the JavaScript.
The source code is below. Implementation notes follow.
<!-- CSS declarations area. --> <style> .amount-box { outline:none; float:left; width:150px; padding:10px 0 8px 0; font-size:3em; border:1px solid blue; border-radius:5px; text-align:center; margin:0 6px 6px 0; background-color:skyblue; cursor:pointer; } </style> <!-- Amount functionality container. --> <div style="max-width:470px; margin:0 auto;"> <!-- The selection boxes area. --> <h3 style="margin-bottom:1px;">Select Amount</h3> <div onclick="IDtapped('amount10')" id="amount10" class="amount-box" style="outline:none;">$10</div> <div onclick="IDtapped('amount20')" id="amount20" class="amount-box" style="outline:none;">$20</div> <div onclick="IDtapped('amount50')" id="amount50" class="amount-box" style="outline:none;">$50</div> <div onclick="IDtapped('amount100')" id="amount100" class="amount-box" style="outline:none;">$100</div> <div onclick="IDtapped('amount250')" id="amount250" class="amount-box" style="outline:none;">$250</div> <div onclick="IDtapped('amount500')" id="amount500" class="amount-box" style="outline:none;">$500</div> <div onclick="IDtapped('amount1000')" id="amount1000" class="amount-box" style="outline:none;">$1000</div> <div onclick="IDtapped('amountOther')" id="amountOther" class="amount-box" style="outline:none;" onblur="UpdateAmount()" onkeyup="ConformAmount(true)" contenteditable="true">Other</div> <div style="clear:left;"></div> <!-- "Payment amount" area. --> <p> Payment amount: $<input type="text" id="payment-amount" name="amount" readonly="readonly" style="cursor:pointer; width:125px; border:none;"> </p> </div> <!-- End: Amount functionality container. -->
Implementation Notes —
CSS Declarations for the Selection Boxes
At the top of the above code is the CSS rule for the selection boxes. They may be modified as needed for your design preferences. It is recommended to keep the cursor:pointer;
declaration so desktop/laptop computers see the pointer mouse cursor when the mouse hovers over a selection box.
The green and red declarations are referred to in the The Selection Boxes section below.
You'll see that the CSS layout is float:left;
. I did it that way because it is one of the easiest width-responsive layouts and works for many web page designs. You may wish to do a grid or use flex
declarations. Or some other method.
In other words, use whatever layout is desired for your implementation and update the CSS declarations accordingly.
The Selection Boxes
Each selection box has onclick
, id
, class
, and style
attributes. The "Other" selection box, where a different amount can be typed in, also has onblur
and contenteditable
attributes. The attributes are addressed below.
The onclick
and id
attributes:
The onclick
attribute has a parameter. The parameter needs to be identical to the id
attribute value. In the code above, the parameter and id value are colored blue.
The class
attribute:
The class
attribute has the same name as the CSS rule in the CSS declarations area near the top of the illustration source code. In the code above and in the CSS style declaration, the amount-box
.
The style
attribute:
The style
attribute must contain outline:none;
as part or all of its value. Additional inline styles may be added so long as outline:none;
remains as part of the style
attribute value.
The onblur
attribute:
Only the "Other" selection box contains an onblur
attribute. It is not color coded in the above code, but it is bolded. The entire attribute is onblur="UpdateAmount()"
and causes the JavaScript function UpdateAmount() to run when the box goes out of focus.
The onkeyup
attribute:
Only the "Other" selection box contains an onkeyup
attribute. It is not color coded in the above code, but it is bolded. The entire attribute is onkeyup="ConformAmount(true)"
and causes the JavaScript function ConformAmount() to run when a key is released.
The contenteditable
attribute:
Only the "Other" selection box contains a contenteditable
attribute. It is not color coded in the above code, but it is bolded. The entire attribute is contenteditable="true"
and is the attribute that allows you to edit the content of a div.
Modifying the Selection Boxes
Selection boxes may be added and removed.
Selection box id values may be modified with corresponding modifications for the UpdateAmount() function call parameters at the onclick
attribute. The class name may be changed if it is also changed for the CSS rule where the CSS declaration is declared.
The outline:none;
value is required in the style, although other CSS declarations may be added.
For the "Other" selection box, the onblur
, onkeyup
, and contenteditable
attributes are required.
Other than those considerations, modify the section boxes and their content as you wish.
The "Payment amount" Field
After someone selects an amount or types an amount into the "Other" selection box, the "Payment amount" field is filled in with the selected or typed amount.
The "Payment amount" field is a regular form field made so it can't be edited. It has a number of special attributes and is initially assigned a special value.
The id
attribute:
The JavaScript requires the id
attribute so it knows where to publish the amount that was selected. If the payment-amount
is changed to something else, a corresponding change needs to be made in customization section of the JavaScript.
The name
attribute:
The code contains a name
attribute as a placeholder for you to assign the form field name you need for your implementation. The name
attribute's presence or absence doesn't affect the live illustration on this page.
The readonly
attribute:
The readonly="readonly"
attribute is required to make the "Payment amount" field unchangeable for the user.
In HTML5, readonly
(no value) is preferred but readonly="readonly"
is allowed. Other HTML versions require a value for the readonly
attribute, so readonly="readonly"
is used in the above source code.
The style
attribute:
The style
attribute is optional. A cursor:pointer;
definition may make it more intuitive when the mouse cursor changes to a pointer. (The "(tap to update)" text appears when the "Other" selection box is tapped on.)
Modifying the "Payment amount" Field
For the "Payment amount" Field, the id
and readonly
attributes are required.
Other than those considerations, modify the "Payment amount" field as you wish.
Implementing the JavaScript
The JavaScript needs to be put anywhere below the amount selection area. Above the cancel </body>
tag should work.
The source code is below. Implementation notes follow.
<script> /* Amount Selector Version 1.0 August 20, 2018 Will Bontrager Software LLC https://www.willmaster.com/ Copyright 2018 Will Bontrager Software LLC This software is provided "AS IS," without any warranty of any kind, without even any implied warranty such as merchantability or fitness for a particular purpose. */ /* Customizations */ // Four places. // Place 1. // Specify styling differences for the amount area that is selected. // Must always at least specify an outline declaration -- color may be transparent. var SelectedArea = "outline:1px solid transparent; background-color:white; color:#0099cc"; // Place 2. // Specify styling differences for the amount area that is not selected. // Must always at least specify outline:none. var NotSelectedArea = "outline:none; background-color:#87ceeb; color:black;"; // Place 3. // Within the Array parameters, specify the id values of each amount selection box -- including the id value of the "Other" selection box. var IDvalues = Array( "amount10", "amount20", "amount50", "amount100", "amount250", "amount500", "amount1000", "amountOther" ); // Place 4. // Specify the id value of the editable selection box (should also be listed in the IDvalues Array() parameters). var IDofEditableField = "amountOther"; // Place 5. // Optionally, specify a currency symbol to be prepended and/or appended to the amount typed into the editable field. var PrependSymbolToEditableAmount = "$"; var AppendSymbolToEditableAmount = ""; // Place 6. // Specify the id value of the "Payment amount" field where the selected amount is published. var AmountFieldID = "payment-amount"; /* End of customization. */ function PowerhouseStyleChange(idvalue,style) { var id = document.getElementById(idvalue); var ta = style.split(/\s*;\s*/); for( var i=0; i<ta.length; i++ ) { var tta = ta[i].split(':',2); var ttaa = tta[0].split('-'); if( ttaa.length > 1 ) { for( var ii=1; ii<ttaa.length; ii++ ) { ttaa[ii] = ttaa[ii].charAt(0).toUpperCase() + ttaa[ii].slice(1); } } tta[0] = ttaa.join(''); id.style[tta[0]] = tta[1]; } } // function PowerhouseStyleChange() function DoStyleChange(id,style) { var styles = style.split(/; */); for( var i=0; i< styles.length; i++ ) { if( styles[i].match(/:/) ) { PowerhouseStyleChange(id,styles[i]); } } } // function DoStyleChange() function AttachCurrencySymbols(n) { if( PrependSymbolToEditableAmount.length ) { n = PrependSymbolToEditableAmount + n; } if( AppendSymbolToEditableAmount.length ) { n = n + AppendSymbolToEditableAmount; } return n; } // function AttachCurrencySymbols() function ConformActualAmountInDiv(d) { d.innerHTML = d.innerHTML.replace(/[^\.\,\d]/g,""); if( d.innerHTML.length < 1 ) { d.innerHTML = ' '; } } // function ConformActualAmountInDiv() function ConformAmount(removeAmountInField) { var editdiv = document.getElementById(IDofEditableField); ConformActualAmountInDiv(editdiv); var sel = window.getSelection(); var range = sel.getRangeAt(0); range.setStart(editdiv,1); range.collapse(false); sel.removeAllRanges(); sel.addRange(range); if( removeAmountInField ) { document.getElementById(AmountFieldID).value = '(tap to update)'; } } // function ConformAmount() function IDtapped(amount) { for( var i=0; i<IDvalues.length; i++ ) { DoStyleChange(IDvalues[i],NotSelectedArea); } DoStyleChange(amount,SelectedArea); var d = document.getElementById(IDofEditableField); if( amount == IDofEditableField ) { d.innerHTML = " "; } else { d.innerHTML = "Other"; } UpdateAmount(); } // function IDtapped() function UpdateAmount() { var amount = 0.0; var hasamount = false; var field = document.getElementById(AmountFieldID); var editdiv = document.getElementById(IDofEditableField); for( var i=0; i<IDvalues.length; i++ ) { var d = document.getElementById(IDvalues[i]); if( ! d.style.outline.match(/none/) ) { hasamount = true; amount = d.innerHTML; } if( hasamount ) { amount = amount.replace(/[^\.\d\-]/g,''); } if( hasamount && amount > 0.0 ) { field.value = CurrencyFormat(amount); } else { if( editdiv.style.outline.match(/none/) ) { field.value = '(select above)'; } else { field.value = '(tap to update)'; } } if( hasamount && amount > 0.0 && IDvalues[i] == IDofEditableField ) { ConformActualAmountInDiv(editdiv); if( editdiv.innerHTML.match(/\d/) ) { var amd = CurrencyFormat(editdiv.innerHTML); amd = amd.replace(/\.00$/,""); editdiv.innerHTML = AttachCurrencySymbols(amd); } else { editdiv.innerHTML = 'Other'; } } } } // function UpdateAmount() function CurrencyFormat(number) { // To customize currency formatting in this function, see https://www.willmaster.com/library/tools/currency-formatting.php var decimalplaces = 2; var decimalcharacter = "."; var thousandseparater = ","; number = parseFloat(number); var sign = number < 0 ? "-" : ""; var formatted = new String(number.toFixed(decimalplaces)); if( decimalcharacter.length && decimalcharacter != "." ) { formatted = formatted.replace(/\./,decimalcharacter); } var integer = ""; var fraction = ""; var strnumber = new String(formatted); var dotpos = decimalcharacter.length ? strnumber.indexOf(decimalcharacter) : -1; if( dotpos > -1 ) { if( dotpos ) { integer = strnumber.substr(0,dotpos); } fraction = strnumber.substr(dotpos+1); } else { integer = strnumber; } if( integer ) { integer = String(Math.abs(integer)); } while( fraction.length < decimalplaces ) { fraction += "0"; } temparray = new Array(); while( integer.length > 3 ) { temparray.unshift(integer.substr(-3)); integer = integer.substr(0,integer.length-3); } temparray.unshift(integer); integer = temparray.join(thousandseparater); return sign + integer + decimalcharacter + fraction; } // function CurrencyFormat() function DelayedConformAmount() { ConformAmount(false); UpdateAmount(); } // function DelayedConformAmount() window.addEventListener('keydown', function(e) { if(e.keyCode==13 || e.keyCode==10) { ConformAmount(false); setTimeout(DelayedConformAmount,500); } }); UpdateAmount(); </script>
Implementation Notes —
The SelectedArea
Variable
Specify the selection box style that is different when it is selected than when it is not selected. There must always be an outline
specification (color may be transparent
). Any other styles are optional.
The NotSelectedArea
Variable
Specify the selection box style that is different when it is not selected than when it is selected. There must always be have an outline:none;
specification. Any other styles are optional.
The IDvalues
Variable
Within the Array parameters, specify the id values of each amount selection box — including the id value of the "Other" selection box.
The IDofEditableField
Variable
Specify the id value of the "Other" selection box.
The PrependSymbolToEditableAmount
Variable and the AppendSymbolToEditableAmount
Variable
When the "Other" section box is edited with an amount, a currency symbol or other character or series of characters may be prepended or appended to the typed amount. Specify symbols or characters you need. Otherwise, leave the values blank.
The AmountFieldID
Variable
Specify the id value of the "Payment amount" field where the selected amount is published.
Using It
I realize there is a lot of techy stuff involved for implementing the amount selector. My intent was to make it as clear as I could.
The "Payment amount" field is the form field where your payment processing software can pick up the amount that was selected or typed in. Change the field's name
attribute as necessary.
If you wish to use this JavaScript for a number-of-items selection boxes instead of payment-amount, remove calls to the JavaScript CurrencyFormat() function. There are likely to be some other tweaking things, depending on exactly what you want to do, but removing calls to CurrencyFormat() is likely to be the largest conversion step.
(This article first appeared with an issue of the Possibilities newsletter.)
Will Bontrager