jQuery

Little jQuery.extend gotcha

jQuery.extend() is a very powerful function for merging two objects or for extending jQuery with custom plugins.

There is but one little gotcha to think about: as in most function calls the order of parameters matters 🙂

Following two fragments of code yields different objects

var a = {
  aa : '1'
  , bb : '2'
  , cc : '3'
};

var b = {
  aa : '11'
  , bb : '22'
  , dd: 3
};

var result_a = $.extend({}, true, a, b);
var result_b = $.extend({}, true, b, a);

The result will be this:

Notice that I use $.extend({}, true, a, b) instead of $.extend(a, b). The first parameter (which is optional) is the target which should receive merged properties. If none is given than the first object is extended automatically.

jQuery datepicker improved

I like jQuery’s datepicker a lot, but I miss two things – one is some kind of mask on the input field so I can write date directly without worrying about the correct date format, and the second is customisable trigger (the thing you click on to toggle the calendar widget 🙂 )

Masked input

This is a screenshot taken directly from jqueryui.com. As you see you can write anything to the input field.  Then you have to validate it, display error message, prevent form from submit… etc. It’s not nice.  Luckily there is a nice solution, a jQuery plugin called maskedinput. (and yes, I use it in durationPicker plugin). I definitely recommend you to use it, it’s very handy, saves lot of work.

I typed a date, right? ... or didn't I?

This is what I wanted!

So, that’s the first thing. Masked input is great, but there is a conflict with the new jQueryUI datepicker. Both of them listen on keypress event. Masked input in order to position entered digit in front of or behind given delimiter and datepicker listens in order to automatically update selected date in a widget as you type.  It works fine until you type the first digit indicating year – maskedinput will insert delimiter in front of your digit, which is perfectly fine, but datepicker expects number, and as a result you get year 1900 (or something else same crazy).

Custom trigger

To prevent this conflict you need to toggle datepicker not when user enters input field, which is the default behaviour, but when she clicks on some trigger, in most cases icon or a button.  Datepicker has an option for it

$( "#datepicker" ).datepicker({
   showOn: "button",
   buttonImage: "images/calendar.gif",
   buttonImageOnly: true
});

Do you see the path to image file embedded in js code? It should not be there, it’s not flexible and makes it very difficult to style.  Not mentioning that you can’t use themeroller. Wouldn’t it be nice if you could use any DOM element as an trigger with themeroller-ready classes, and not just an image? Well…. You can 🙂 Use something like this:

var el = $('.datepicker');
el.parent().addClass('hideCalImage calImgWrapper'); //add class to hide 'native' image triggers
//add themeroller ui classes
	var calImgController = $('<div class="calImg ui-icon-calendar ui-icon"></div>').click(function () {
	if (el.attr('data-hasDPVisible') == 'true') {
		el.attr('data-hasDPVisible', 'false');
		el.datepicker('hide');
	}
	else {
		el.attr('data-hasDPVisible', 'true');
		el.datepicker('show');
	}
}).appendTo(el.parent());

I add some css class to hide the <img>, and then replace it with my own <div> element with attached click listeners.  Notice the use of data- attribute hasDpVisible. I have to use some kind of indication whether datepicker is on or off to be able to show / close it as needed.  And that’s basically it. For the full source code see the DEMO.