a better date range picker

I was unhappy with the range-datepickers I had seen so I made my own. I needed a date range picker that could select ranges of years, quarters or months, and I couldn’t find one that worked in this way. I had been using the jquery datepicker, but it didn’t have a “quarter” option; although it may have been possible to extend the datepicker to allow for this. at any rate, this is still a little rough and a little long in the code, but it works the way I wanted it to; the available months/quarters and years change in logical ways depending on the current selection in each of the other select menus. It also preserves the selected date through aggregate mode changes; a setting of March is still there when switched to “quarterly mode” and back to “monthly mode”.

date-range-picker
the date-range-picker in Google Chrome

Here’s the date-range-picker in chrome; appearance will vary depending on the browser and the implemented formating. This may be a reason to switch over to hidden select elements mirrored by styled divs in order to have cross-browser consistency and for increased visual adaptability.


Here is the html:

<div id="report-dates" class="report-date">
<span class="rdate">
<label>start date</label>
        <select id="from-sub-inc" class="sub-inc">
        </select>
        <select id="from-year-inc" class="year-inc">
        </select>
</span>
        
<span class="rdate">
<label>end date</label>
        <select id="to-sub-inc" class="sub-inc">
        </select>
        <select id="to-year-inc" class="year-inc">
        </select>
</span> 
</div>
                                                        
<div id="aggregate">
        <label>aggregate data: </label>
        <span class="aggregate">
        <input type="radio" name="aggr" id="monthly" value="monthly" checked>monthly</span>
        <span class="aggregate">
        <input type="radio" name="aggr" id="quarterly" value="quarterly">quarterly</span>
        <span class="aggregate">
        <input type="radio" name="aggr" id="annually" value="annually">annually</span>
        <span class="aggregate">
        <input type="radio" name="aggr" id="sum" value="sum">sum</span>
</div>

and this is the javascript:

var $j = jQuery.noConflict();

function init() {   
    var dateSelection = {
        SelectionMode: 'monthly',
        StartSubInc: $j("#from-sub-inc"),
        StartYearInc: $j("#from-year-inc"),
        EndSubInc: $j("#to-sub-inc"),
        EndYearInc: $j("#to-year-inc"),
        Months: new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"),
        Quarters: new Array("Q1", "Q2", "Q3", "Q4"),
        StartDate: new Date(Date.now()),
        EndDate: new Date(Date.now()),
        StartSetYears: function(){
                
                var YearUL = dateSelection.EndDate.getFullYear();
                if (dateSelection.StartSubInc.val() > dateSelection.EndSubInc.val())
                        YearUL = dateSelection.EndDate.getFullYear() -1;
                var options = '';
                for (var y = YearUL; y >= 1950; --y){
                        options += '<option value="' + y + '">' + y + '</option>';
                }
                dateSelection.StartYearInc.append( options );
                $j("#from-year-inc option[value='"+ dateSelection.StartDate.getFullYear() +"']" ).attr("selected", true);
        },
        StartSetSub: function() {
                
                var MonthUL = 11 // if start and end are same year, restrict available months
                if (dateSelection.StartDate.getFullYear() >= dateSelection.EndDate.getFullYear())
                        MonthUL = dateSelection.EndDate.getMonth(); 
                
                var options = "";
                if (dateSelection.SelectionMode == "monthly" || dateSelection.SelectionMode == "sum") {
                        for (var i=0; i <= MonthUL; ++i){
                                options += '<option value="' + (i) +'">'+ dateSelection.Months[i] + '</option>';
                        }
                        dateSelection.StartSubInc.prop("disabled", false);
                                dateSelection.StartSubInc.append ( options );
                                $j("#from-sub-inc option[value='" + dateSelection.StartDate.getMonth() +"']").attr("selected", true);
                }
                if (dateSelection.SelectionMode == "quarterly") {
                        for (var i=0; i <= MonthUL; i+=3){
                                options += '<option value="' + (i)  +'"';
                                if (i <= dateSelection.StartDate.getMonth() && i+3 > dateSelection.StartDate.getMonth() ){
                                        options += ' selected="selected" ';
                                }
                                options += '>'+ dateSelection.Quarters[parseInt(i/3)] + '</option>';
                        }
                        dateSelection.StartSubInc.prop("disabled", false);
                                dateSelection.StartSubInc.append ( options );
                }
                if (dateSelection.SelectionMode == "annually") {
                        dateSelection.StartSubInc.prop("disabled", true);
                }
        },
        EndSetYears: function(){
                var YearLL = dateSelection.StartDate.getFullYear();
                if ( dateSelection.StartSubInc.val() > dateSelection.EndSubInc.val())
                        YearLL = dateSelection.StartDate.getFullYear() +1;              
                var options = '';
                for (var y = new Date(Date.now()).getFullYear(); y >= YearLL; y--){
                        options += '<option value="' + y + '">' + y + '</option>';
                }
                dateSelection.EndYearInc.append( options );
                $j("#to-year-inc option[value='"+ dateSelection.EndDate.getFullYear() +"']" ).attr("selected", true);
        },
        EndSetSub: function() { 
                
                var MonthUL = 11; 
                var MonthLL = 0;
                if (dateSelection.EndDate.getFullYear() == new Date(Date.now()).getFullYear() ){
                        MonthUL = new Date(Date.now()).getMonth();
                }
                
                if ( dateSelection.StartDate.getFullYear() == dateSelection.EndDate.getFullYear() ){
                        MonthLL = dateSelection.StartDate.getMonth();
                }
                        
                var options = "";

                if (dateSelection.SelectionMode == "monthly" || dateSelection.SelectionMode == "sum") {
                        for (var i= MonthLL; MonthUL >= i; ++i){
                                options += '<option value="' + i +'">'+ dateSelection.Months[i] + '</option>';
                        }
                        dateSelection.EndSubInc.prop("disabled", false);
                                dateSelection.EndSubInc.append ( options );
                                $j("#to-sub-inc option[value='" + dateSelection.EndDate.getMonth() +"']").attr("selected", true);
                }
                if (dateSelection.SelectionMode == "quarterly") {
                        for (var i=MonthLL; i < MonthUL; i+=3){

                                options += '<option value="' + i  +'"'; 
                                if (i <= dateSelection.EndDate.getMonth() && i+3 > dateSelection.EndDate.getMonth() ){
                                        options += ' selected="selected" ';
                                }
                                options += '>' + dateSelection.Quarters[parseInt(i/3)] + '</option>';
                                        
                        }
                        dateSelection.EndSubInc.prop("disabled", false);
                                dateSelection.EndSubInc.append ( options );
                                
                }
                if (dateSelection.SelectionMode == "annually") {
                        dateSelection.EndSubInc.prop("disabled", true);
                }
                
        }
    }
    // initialize sub-inc and year-inc lists
    updateOptions();
    
    // change selected dates and  date limits
    dateSelection.StartSubInc.on("change", function(){
        dateSelection.StartDate.setMonth(dateSelection.StartSubInc.val());
        dateSelection.StartDate.setDate(1);
        updateOptions();
    });
    
    dateSelection.EndSubInc.on("change", function(){
        dateSelection.EndDate.setMonth(dateSelection.EndSubInc.val());
        dateSelection.EndDate.setDate(27); // smallest data inc is monthly 
        updateOptions();
    });
    
    dateSelection.StartYearInc.on("change", function() {
        dateSelection.StartDate.setYear(dateSelection.StartYearInc.val() );
        updateOptions();
    });
    
    dateSelection.EndYearInc.on("change", function() {
        dateSelection.EndDate.setYear(dateSelection.EndYearInc.val() );
        updateOptions();
    });
    
    // change sub-inc options depending on data time aggregation
    $j("input:radio[name='aggr']").on("click", function (e){
        dateSelection.SelectionMode = $j("input:radio[name='aggr']:checked").val();
        dateSelection.StartSubInc.html("");
        dateSelection.StartSetSub();
        dateSelection.EndSubInc.html("");
        dateSelection.EndSetSub();
    });
    function updateOptions(){
        dateSelection.EndSubInc.html("");
        dateSelection.EndSetSub();
        dateSelection.StartSubInc.html("");
        dateSelection.StartSetSub();
        dateSelection.EndYearInc.html("");
        dateSelection.EndSetYears();
        dateSelection.StartYearInc.html("");
        dateSelection.StartSetYears();
        
    }   

}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s