I have a select with 3 options and two datepickers, a start and an end date. Based on the option selected, the date end date can either be any future date or limited to a 30 day date range. In the jsFiddle provided below, when Option 1 is selected, the date range is restricted to 30 days. If either Option 2 or Option 3 are selected, the end date can be any future date.

Here is a complete working jsFiddle with the complete code.

I haven't run into any issues with the code below. I'm just wondering if anyone has any suggestions on how I could go about reducing it as it just seems too "bulky".

$('#startDate, #endDate').datepicker({
    beforeShow: setDateRange,
    dateFormat: "mm/dd/yy",
    firstDay: 1,
    changeFirstDay: false,
    onChange: function() { $(this).valid(); },
    onSelect: function() {
        if (this.id == 'startDate') {
            var date = $('#startDate').datepicker('getDate');
            if (date) { date.setDate(date.getDate() + 1); }
            $('#endDate').datepicker('option', 'minDate', date);
        }
    }
});

function setDateRange(input) {
    var min = null, dateMin = min, dateMax = null, dayRange = 30;
    var opt = $('#select1'), start = $('#startDate'), end = $('#endDate');
    if (opt.val() == '1') {
        if (input.id == 'startDate') {
            if ($('#endDate').datepicker('getDate') != null) {
                dateMax = $('#endDate').datepicker('getDate');
                dateMin = $('#endDate').datepicker('getDate');
                dateMin.setDate(dateMin.getDate() - dayRange);
                if (dateMin < min) { dateMin = min; }
            }
        } else if (input.id == 'endDate') {
            dateMin = $('#startDate').datepicker('getDate');
            dateMax = new Date(dateMin.getFullYear(), 
                dateMin.getMonth(), dateMin.getDate() + 30);
            if ($('#startDate').datepicker('getDate') != null) {
                var rangeMax = new Date(dateMin.getFullYear(), 
                    dateMin.getMonth(), dateMin.getDate() + dayRange);
                if (rangeMax < dateMax) { dateMax = rangeMax; }
            }
        }
    } else if (opt.val() != '1') {
        if (input.id == 'startDate') {
            if ($('#endDate').datepicker('getDate') != null) {
                dateMin = null;
            }
        } else if (input.id == 'endDate') {
            dateMin = $('#startDate').datepicker('getDate');
            dateMax = null;
            if ($('#startDate').datepicker('getDate') != null) { dateMax = null; }
        }
    }
    return {
        minDate: dateMin,
        maxDate: dateMax
    };
}

I posted this on Stack Overflow a couple months back when I was initially trying to figure out how to get it working. The post goes into a little more detail on the option selections but I have since added on to it some. For example, the code above now includes an onSelect when the datepickers are initialized.

If anyone has any suggestions on how I could go about improving this, please let me know.

up vote 3 down vote accepted

From a once over;

  • This code does not do what the tin says:

    // If selected start date is later than currently selected
    // end date, set end date to start date + 1 day
    var date = $('#startDate').datepicker('getDate');
    if (date) { date.setDate(date.getDate() + 1); }
    

    what this actually does is always set the first possible date to startdate + 1

  • Not sure about this :)

    <option value="2">Any Future Date</option>
    <option value="3">Any Future Date</option>
    
  • The continued checks for input.id are a good indication that perhaps you need 2 distinct functions
  • The id's 1 and 2 are rather meaningless, they could be meaningful strings, or even better they could be function names that need to be executed.
  • There a number of non-sensical assignments like here:

    dateMax = null;
    if ($('#startDate').datepicker('getDate') != null) { dateMax = null; }
    

    if you set dateMax already to null, then why would you set dateMax again to null?

  • Your code dies when the user tries to select an end date without first selecting first a starting date in 30 days mode with Cannot read property 'getFullYear' of null because you are indeed trying to calculate on an empty field..
  • I am not sure why in 30 day mode the start date is impacted by the end date, especially since clearing out the end date opens up the calendar again.

All in all, I would go with something like this:

$('#startDate, #endDate').datepicker({
    beforeShow: setDateRange,
    dateFormat: "mm/dd/yy",
    firstDay: 1,
    changeFirstDay: false,
    onChange: function () {
        $(this).valid();
    }
});

var dateLogic = {

    addDays: function addDays(initialDate, dayCount) {
        var newDate = new Date(initialDate);
        newDate.setDate(newDate.getDate() + dayCount);
        return newDate;
    },
    openWide: {
        minDate: null,
        maxDate: null
    },
    futureDays: {
        startDate: function startDateFutureDays(currentStart, currentEnd) {
            return !currentEnd ? dateLogic.openWide : {
                minDate: null,
                maxDate: dateLogic.addDays(currentEnd, -1)
            }
        },
        endDate: function endDateFutureDays(currentStart) {
            return {
                minDate: currentStart,
                maxDate: null
            }
        }
    },
    thirtyDays: {
        startDate: function startDateThirtyDays(currentStart, currentEnd) {
            return !currentEnd ? dateLogic.openWide : {
                maxDate: currentEnd,
                minDate: dateLogic.addDays(currentEnd, -30)
            };
        },
        endDate: function endDateThirtyDays(currentStart) {
            return !currentStart ? dateLogic.openWide : {
                minDate: currentStart,
                maxDate: dateLogic.addDays(currentStart, 30)
            }
        }
    }
};

function setDateRange(input) {

    var opt = $('#select1'),
        startDate = $('#startDate').datepicker('getDate'),
        endDate = $('#endDate').datepicker('getDate');

    return dateLogic[opt.val()][input.id](startDate, endDate);
}

$('#select1').change(function () {
    $('#startDate, #endDate').val('');
    $('#startDate, #endDate').removeAttr('disabled');
});

You can play with this on http://jsfiddle.net/konijn_gmail_com/rc0kr718/

  • Could not make the snippet work :\ The insert button seems to do nothing.. – konijn Oct 3 '14 at 19:54

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.