Datepicker Range inputs on dynamic elements

I’m using the jquery date range picker and the example on that page uses ids rather than classes to identify the from and to dates. I needed to convert this to classes and also to make it work on dynamically added elements.

Eventually (one day later) I manage to get it working but the code seems to verbose to me as it is duplicated but I can’t seem to merge it into a simpler routine without breaking the functionality.

Here’s a codepen of the working version:

The part I need help with is this (this bit is working as I want):

//range is ok with this
$(function() {
    $('body').on('focus', ".from", function() {


        $('.from').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').next('label').find('.to');
                $(theOtherDate).datepicker("option", "minDate", selectedDate);
                // $(this).valid();
            }
        });
        $('.to').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').prev('label').find('.from');
                $(theOtherDate).datepicker("option", "maxDate", selectedDate);
                //$(this).valid();
            }
        });
    })

    $('body').on('focus', ".to", function() {
        $('.from').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').next('label').find('.to');
                $(theOtherDate).datepicker("option", "minDate", selectedDate);
               
            }
        });
        $('.to').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').prev('label').find('.from');
                $(theOtherDate).datepicker("option", "maxDate", selectedDate);
               
            }
        });


    })
});

As you can see its basically 2 sections of the same code. I tried reducing it to this:

$(function() {
    $('body').on('focus', ".from", ".to", function() {
      
         
            $('.from').datepicker({
                defaultDate: "+1w",
                changeMonth: true,
                dateFormat: 'dd-mm-yy',
                numberOfMonths: 2,
                onClose: function(selectedDate) {
                    var theOtherDate = $(this).closest('label').next('label').find('.to');
                    $(theOtherDate).datepicker("option", "minDate", selectedDate);
                 
                }
            });
       
       
           
            $('.to').datepicker({
                defaultDate: "+1w",
                changeMonth: true,
                dateFormat: 'dd-mm-yy',
                numberOfMonths: 2,
                onClose: function(selectedDate) {
                    var theOtherDate = $(this).closest('label').prev('label').find('.from');
                    $(theOtherDate).datepicker("option", "maxDate", selectedDate);
                    
                }
            });

       
    })
});

Now that appears to work but if you select the ‘to’ date first it becomes clear that the range is not working as in the previous example because you don’t get the range limit on the ‘from’ date.

Also in other examples I’ve seen around people are destroying the datepicker before calling it again but this again breaks the function in both versions.

Lastly this needs to work without ids or any unique name attributes.

So to recap the first version (as in the codepen) appears to be working properly but seems overly verbose to me.

Hi Paul,

I see no CodePen

Strange its embedded fine for me!

Here’s a direct link anyway.

Ignore that I posted the code containing .destroy as it still doesn’t work if I remove destroy().

Oops, sorry, blocked by a browser extension.

Anyways, unless I’m missing something obvious, you can just do:

$('body').on('focus', ".from, .to", function() {

This seems to work or me and the upper date range is respected if you select the to date first.

1 Like

Thanks for the reply.

That doesn’t seem to be working for me (unless I made an error). I’ve updated the codepen to that non working version now:

In the first fieldset select the ‘To’ input and enter a date such as 22 Feb and then go to the ‘From’ input and you will see that the dates above 22 feb are not faded out.

Now try the same thing in the second fieldset that uses ID and you will see the difference.

My original (verbose script) works exactly like the ids but there must be a way to shorten it I would think.

Ignore the above it seems to be working thanks. Must have been an issue in codepen. :slight_smile:

Can you clarify the difference between what I tried.

Yours:

$('body').on('focus', ".from, .to", function() {

as opposed to mine:

$('body').on('focus', ".from", ".to", function() {

I can see you merged the from and to into the same quotes but I thought that was basically the same as what I was doing but in separate items.

In my tests, this:

$(‘body’).on(‘focus’, “.from”, “.to”, function() {

meant that if you clicked into the “to” field first, then the datepicker wasn’t shown.

Whereas specifying:

$('body').on('focus', ".from, .to", function() {

seemed to work (plus the fact that this is how I learned to target multiple selectors).

It may well have been some CodePen weirdness :smile:

Glad you got it fixed.

If you can post what you have (just post the code here), I will look if it can be DRYed up any more.

I think that what I was using was me mis-reading the rules for .on(). I assumed the correct way to do it was to add separate comma separated values in separate quotes but I should have used the method I normally use for selectors like you have shown.

The code for that particular function is now this:

$(function() {
    $('body').on('focus', ".from, .to", function() {
        $('.from').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').next('label').find('.to');
                $(theOtherDate).datepicker("option", "minDate", selectedDate);
                $(this).valid();// for validate plugin
            }
        });
        $('.to').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                var theOtherDate = $(this).closest('label').prev('label').find('.from');
                $(theOtherDate).datepicker("option", "maxDate", selectedDate);
                 $(this).valid();// for validate plugin
            }
        });
    })
});

I’m assuming that now its working I can merge that into one routine using:

$(‘.from,.to’).datepicker({

and then putting an if else structure inside the ‘onClose: function(selectedDate) {’ function and test for ‘from’ or 'to" classname.

Thanks for your help :smile:

Finally down to this now :smile:

$(function() {
    $('body').on('focus', ".from, .to", function() {
        $('.from,.to').datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            dateFormat: 'dd-mm-yy',
            numberOfMonths: 2,
            onClose: function(selectedDate) {
                if ($(this).hasClass('from')){
                                 var theOtherDate = $(this).closest('label').next('label').find('.to');
                                 $(theOtherDate).datepicker("option", "minDate", selectedDate);
                                } else {
                                 var theOtherDate = $(this).closest('label').prev('label').find('.from');
                $(theOtherDate).datepicker("option", "maxDate", selectedDate);    
                                }
               // $(this).valid();// for validate plugin
            }
        });
     })
});

Do you see any issues with that approach?

No, that looks good.

I would point out that If you have multiple instances of the datepicker, you can stick any shared configuration in an object literal and pass that in. It might also be easier to manage if you put the onClose handler in a separate function.

e.g.

function updateRange(selectedDate) {
  if ($(this).hasClass('from')){
    var theOtherDate = $(this).closest('label').next('label').find('.to');
    $(theOtherDate).datepicker("option", "minDate", selectedDate);
  } else {
    var theOtherDate = $(this).closest('label').prev('label').find('.from');
    $(theOtherDate).datepicker("option", "maxDate", selectedDate);    
  }
}

var configOpts = {
  defaultDate: "+1w",
  changeMonth: true,
  dateFormat: 'dd-mm-yy',
  numberOfMonths: 2,
  onClose: updateRange(selectedDate)
}

$('.from,.to').datepicker(configOpts);

But that’s a matter of taste, I guess :slight_smile:

Thanks Pullo.

I still have a lot to learn :smile:

Just wondering, I don’t need this anymore do I.

$(theOtherDate).datepicker

Could it just be:

theOtherDate.datepicker

It’s assigned with this:

var theOtherDate = $(this).closest(‘label’).next(‘label’).find(‘.to’);

So I should be able to just use ‘theOtherDate’.

Or does it not really matter?

The datepicker method is expecting to be initialised on a jQuery object.
I normally append any variable holding jQuery objects with a dollar sign:

var $theOtherDate = $(this).closest('label').next('label').find('.to');
$theOtherDate.datepicker();

In the grand scheme of things, it doesn’t really matter much.
This way just helps you (and others) to remember / recognize what is saved where.

Ah ok, thanks again. :wink:

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.