richj richj - 1 month ago 8
Ajax Question

How to bind jQuery Datepicker after Ajax refresh?

I've got a jQuery datepicker that is working great for me, except when I fresh the content via ajax, I lose the datepicker. From what I can tell I should be using jQuery on() to bind it to the input, but I can't seem to find the right event to bind it to.

Works the first time, but not on subsequent refreshes:

$("[id^=startPicker]").datetimepicker({
showOn: "button",
buttonImage: "/img/calendar_icon.png",
buttonImageOnly: true,
dateFormat: 'mm/dd/yy',
timeFormat: 'hh:mm tt',
stepMinute: 1,
onClose: function (dateText, inst) {

var selectedDate = $(this).datepicker("getDate"); //Date object

$.ajax({
url: "/url",
dataType: "json",
method: 'post',
data: {
value: selectedDate.toDateString() + ' ' + selectedDate.toTimeString()
},
beforeSend: function () {
$("#loading").fadeIn();
},
success: function (data, textStatus) {
$("#content").html(data);
},
complete: function () {
$("#loading").fadeOut();
}
});
}
});


Doesn't bind first time or on subsequent refreshes:

$('#content').on('ready', "[id^=startPicker]", function () {
$(this).datetimepicker({
showOn: "button",
buttonImage: "/img/calendar_icon.png",
buttonImageOnly: true,
dateFormat: 'mm/dd/yy',
timeFormat: 'hh:mm tt',
stepMinute: 1,
onClose: function (dateText, inst) {

var selectedDate = $(this).datepicker("getDate"); //Date object

$.ajax({
url: "/url",
dataType: "json",
method: 'post',
data: {
value: selectedDate.toDateString() + ' ' + selectedDate.toTimeString()
},
beforeSend: function () {
$("#loading").fadeIn();
},
success: function (data, textStatus) {
$("#content").html(data);
},
complete: function () {
$("#loading").fadeOut();
}
});
}
});
});

Answer

The jQuery .on() function is for deferred event handling, but it doesn't work for plugin initialization.

It works for events because the DOM model propagates events from DOM elements to their parent elements, all the way to the top. So when you click on a link, you're also clicking on whatever contains that link (a div for example), whatever contains that container, and so on all the way up to the body tag and eventually the document itself. .on() takes advantage of this by binding to the click event of a common parent element instead of the dynamic child elements, so child elements can be added/removed without losing the event handler on the parent.

Plugin initialization doesn't work this way, though. In order to initialize a plugin on a target element, it must be done on the element itself which means the element needs to be in the DOM at the time. Thus, when you add new elements dynamically, you need to target those new elements and initialize the plugin on them. So your success function from the AJAX call would need to do that.

Note that since your AJAX call is itself inside of your initialization, you're going to need to split some of these out into separate functions for re-use. Otherwise this re-initialization would nest inside itself indefinitely.

Maybe something like this:

var datePickerClose = function (dateText, inst) {

    var selectedDate = $(this).datepicker("getDate"); //Date object

    $.ajax({
        url: "/url",
        dataType: "json",
        method: 'post',
        data: {
            value: selectedDate.toDateString() + ' ' + selectedDate.toTimeString()
        },
        beforeSend: function () {
            $("#loading").fadeIn();
        },
        success: function (data, textStatus) {
            $("#content").html(data);
            $("#content").find("[id^=startPicker]").each(function () {
                bindDatePicker(this);
            });
        },
        complete: function () {
            $("#loading").fadeOut();
        }
    });
};

var bindDatePicker = function(element) {
    $(element).datetimepicker({
        showOn: "button",
        buttonImage: "/img/calendar_icon.png",
        buttonImageOnly: true,
        dateFormat: 'mm/dd/yy',
        timeFormat: 'hh:mm tt',
        stepMinute: 1,
        onClose: datePickerClose
    });
};

$("[id^=startPicker]").each(function() {
    bindDatePicker(this);
});

This is, of course, free-hand untested code just to demonstrate the concept. It may require a little tweaking, and there may be a more elegant way to accomplish the same logic.

Comments