(function ($) { var cleanInput = function (value) { return parseFloat(value.replace(/[^0-9.-]/, '')); } var formatInput = function (value) { toks = value.toFixed(2).replace('-', '').split('.'); var display = '$' + $.map(toks[0].split('').reverse(), function (elm, i) { return [(i % 3 == 0 && i > 0 ? ',' : ''), elm]; }).reverse().join('') + '.' + toks[1]; return value < 0 ? '(' + display + ')' : display; } ko.bindingHandlers.money = { init: function (elm, valueAccessor) { $(elm).change(function () { valueAccessor()(cleanInput(elm.value)); }).addClass('money'); }, update: function (elm, valueAccessor, allBindingsAccessor) { var value =ko.utils.unwrapObservable(valueAccessor()) $elm = $(elm), method = $elm.is(":input") ? "val" : "html"; $elm[method](formatInput(value)).toggleClass('negative', value < 0); } }; })(jQuery); //Not part of the custom binding, just wiring the viewModel up to the bindings. $(function(){ var viewModel={ Cash:ko.observable(-1234.56) }; ko.applyBindings(viewModel); });
<!DOCTYPE html> <html> <head> </head> <body class='ui-widget'> <header class='ui-widget-header'> <h1>Give me all of your money</h1> </header> <div class='ui-widget-content'> <p> <label>How Much?</label><input data-bind="money:Cash" /></p> <p> <label>Non inputs too!</label><span data-bind="money:Cash" /></p> <p> <label>Normal binding:</label><span data-bind="text:Cash" /></p> </div> </body> </html>