Custom watermark text input using custom binding

I am trying to create a custom bindHandler that I can use to give watermark behavior to text input fields.

In watermarkI mean: add default values ​​to text fields that are deleted when focusing, and replace with blur if the text field is still empty

I managed to get this to work, as shown in this jsfiddle: http://jsfiddle.net/rpallas/nvxuw/

I have 3 questions about this solution:

  • Is there a way to change it, so I only need to declare the watermark value once? Currently, I have to place it in the place where I declare the binding, and I also need to initialize the observable with the same value in viewModel, because otherwise it does not have an initial value.
  • Is there a better way to get to the underlying observable that the value of the element is bound to. I am currently grabbing it with allBindingsAccessor, but it seems wrong to me. Initially, I just set the value using jquery $(element).val(''), but that is also not the case. Which is better, or is there a better way?
  • Does anyone know or know of an existing solution to this problem? Am I reinventing the wheel?
+5
source share
2 answers

I think you are using allbindings, this is not necessary. Actually, I don’t think that a watermark should be aware of what is being observed at all, since it is that a watermark usually has an attribute placeholder.

Will this work for you?

ko.bindingHandlers.watermark = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor(), allBindings = allBindingsAccessor();
        var defaultWatermark = ko.utils.unwrapObservable(value);
        var $element = $(element);

        setTimeout(function() {
            $element.val(defaultWatermark);}, 0);

        $element.focus(
            function () {
                if ($element.val() === defaultWatermark) {
                    $element.val("");
                }
            }).blur(function () {
                if ($element.val() === '') {
                    $element.val(defaultWatermark)
                }
            });
    }
};

http://jsfiddle.net/madcapnmckay/Q5yME/1/

Hope this helps.

+14
source

The previous approach is good, as long as your application logic is really simple, keep in mind that the solution is messing with the values ​​of your view model, these values ​​can be observable and they can have associated subscriptions or calculations, so by changing the value you change the view model. Here is another solution without updating the View Model

ko.bindingHandlers.fakePlaceHolderWhenNeedIt = {
    init: function (element, valueAccessor, allBindings, vm) {
     if (!Modernizr.input.placeholder) {
        var placeHolderVal = $(element).attr("placeholder");

        if (placeHolderVal != null || placeHolderVal != '') {

            var $element = $(element);
            var value = valueAccessor()
            var valueUnwrapped = ko.utils.unwrapObservable(value);


            $element.keyup(function () {
                var inputValue = $(this).val();
                var $watermark = $(this).prev('.ie-placeholder');
                if (inputValue == null || inputValue == '') {
                    $watermark.show();
                }
                else {
                    $watermark.hide();
                }
            });

            var display = valueUnwrapped != null || valueUnwrapped != '' ? "block" : "none";
            var left = $element.position().left;
            var top = $element.position().top;
            var paddingLeft = $element.css('padding-left');
            var paddingRight = $element.css('padding-right');
            var paddingTop = $element.css('padding-top');
            var paddingBottom = $element.css('padding-bottom');

            var height = $element.css('height');
            var placeHolder = '<div class="ie-placeholder" style="position:absolute;left:' + left + ';top:' + top + ';padding-top: ' + paddingTop + ';padding-bottom: ' + paddingBottom + ';padding-left: ' + paddingLeft + ';padding-right: ' + paddingRight + ';height: ' + height + ';line-height:' + height + ';display:' + display + ';">' + placeHolderVal + '</div>';

            $(placeHolder).click(function () { $element.focus(); }).insertBefore(element);
        }
    }
},
update: function (element, valueAccessor, allBindings, vm) {
    if (!Modernizr.input.placeholder) {
        var placeHolderVal = $(element).attr("placeholder");

        if (placeHolderVal != null || placeHolderVal != '') {
            var $element = $(element);
            var value = valueAccessor()
            var valueUnwrapped = ko.utils.unwrapObservable(value);

            var $watermark = $element.prev('.ie-placeholder');
            if (valueUnwrapped == null || valueUnwrapped == '') {
                $watermark.show();
            }
            else {
                $watermark.hide();
            }
        }
    }
}
+1
source

All Articles