27

I am using jQuery UI datepicker and I want to display additional text inside date cells next to the date. This is the desired behavior:

jQuery UI datepicker with custom text inside cells

Unfortunately, manipulating date cells using .text() and .html() functions breaks the datepicker functionality. See following demo and try to use the datepicker. Notice that when you select a date (i) the custom text is gone (ii) changing months destroys the calendar and lands you on "undefined NaN" month:

https://jsfiddle.net/salman/aLdx4L0y/

Is there a solution?

1
  • If you don't mind to trigger addCustomInformation function a lot you can do something like this Commented Mar 5, 2016 at 8:37

4 Answers 4

23
+250

Well, you can monkey-patch jQuery UI and risk breaking the code with newer versions or you can use documented callbacks to add custom data-* attributes to datepicker and display them using CSS pseudo elements:

$(function() {
  $("#datepicker").datepicker({
    beforeShow: addCustomInformation,
    //---^----------- if closed by default (when you're using <input>)
    beforeShowDay: function(date) {
      return [true, date.getDay() === 5 || date.getDay() === 6 ? "weekend" : "weekday"];
    },
    onChangeMonthYear: addCustomInformation,
    onSelect: addCustomInformation
  });
  addCustomInformation(); // if open by default (when you're using <div>)
});

function addCustomInformation() {
  setTimeout(function() {
    $(".ui-datepicker-calendar td").filter(function() {
      var date = $(this).text();
      return /\d/.test(date);
    }).find("a").attr('data-custom', 110); // Add custom data here
  }, 0)
}
.ui-datepicker .weekend .ui-state-default {
  background: #FEA;
}
.ui-datepicker-calendar td a[data-custom] {
  position: relative;
  padding-bottom: 10px;
}
.ui-datepicker-calendar td a[data-custom]::after {
  /*STYLE THE CUSTOME DATA HERE*/
  content: '$' attr(data-custom);
  display: block;
  font-size: small;
}
<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
<link href="//code.jquery.com/ui/1.9.2/themes/smoothness/jquery-ui.css" rel="stylesheet" />
<script src="//code.jquery.com/ui/1.9.2/jquery-ui.min.js"></script>
<input id="datepicker">

Here's an updated JSFiddle


Side note: I'm not sure which functionality is broke in your demo, but since this solution is using documented callbacks and CSS, I believe it's less likely to break anything in future than monkey patching

Sign up to request clarification or add additional context in comments.

4 Comments

Nice approach! (My patched version has practically zero chance of breaking the calendar widget itself, but of course it might stop working if they change something internally. Not unlike this, really - it silently assumes that there always will be an a inside a td, and I would bet this is not documented anywhere. ;) )
@Tomalak practically - yes that is true, jQuery UI doesn't really get that much update (especially breaking ones) it seems, the complete interaction re-write with touch support (2.0) is taking forever and today I saw a 3 year old bug and it's version was 1.10~, after 3 years stable version is just 1.11~ !
True, it has collected more than a little dust in some areas.
getDay returns 0 for sunday and 6 for saturday, not 5
14

There is a much simpler solution based on the accepted answer:

The beforeShowDay function allows us to set the "title" attribute for each date. We can display the attribute using CSS pseudo elements.

$(function() {
  var dayrates = [100, 150, 150, 150, 150, 250, 250];

  $("#datepicker").datepicker({
    beforeShowDay: function(date) {
      var selectable = true;
      var classname = "";
      var title = "\u20AC" + dayrates[date.getDay()];
      return [selectable, classname, title];
    }
  });
});
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");

.ui-datepicker td span,
.ui-datepicker td a {
  padding-bottom: 1em;
}

.ui-datepicker td[title]::after {
  content: attr(title);
  display: block;
  position: relative;
  font-size: .8em;
  height: 1.25em;
  margin-top: -1.25em;
  text-align: right;
  padding-right: .25em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<div id="datepicker"></div>

3 Comments

Very nice, this is a beautiful solution. Thumbs up.
Thinking about it, this approach depends on a) You only ever want to insert plain text, never additional functionality (like a second link, for example) and b) No other .ui-datepicker td will ever have a title. The second can be overcome by using .ui-datepicker-calendar td[title], though. The first is probably limiting.
Nice answer. Here's a Codepen link.
10

Since the jQuery UI datapicker is pretty monolithic it is difficult to enhance cleanly.

I would go with monkey-patching one of its internal functions, namely _generateHTML.

$.datepicker._generateHTML = (function () {
    var realGenerateHtml = $.datepicker._generateHTML;
    return function (instance) {
        var html = realGenerateHtml.apply(this, arguments), $temp;
        
        if ( instance.input.is(".datepicker-price") ) {
            $temp = $("<table></table>").append(html);
            $temp.find(".ui-datepicker-calendar td a").each(function () {
                var yy = $(this).parent().data("year"),
                    mm = $(this).parent().data("month"),
                    dd = +$(this).text();

                $(this).append("<br><small class='price'>$100</small>");
            });
            html = $temp[0].innerHTML;
        }
        return html;
    };
})();

$(function() {
    $("#datepicker").datepicker();
});
.ui-datepicker .weekend .ui-state-default {
  background: #FEA;
}
.price {
    color: blue;
}
<link href="https://code.jquery.com/ui/1.9.2/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.9.2/jquery-ui.min.js"></script>

<div id="datepicker" class="datepicker-price"></div>

4 Comments

Monkey patch the _generateHTML function... why didn't I think of that?
Does this actually work completely? When I click a date and click the right or left arrow, it shows "undefined NaN" and the dates go away. If I don't click the date box and just click the arrows, everything is fine and the month changes.
Yes, it does. I've tested it in three browsers. Not sure where it would show "undefined NaN" for you? Please make a jsFiddle that demonstrates the issue.
Again. Make a jsFiddle that demonstrates the issue and post the link.
7

Add this code into your JS...

$('#DatePicker').datepicker({
changeMonth: true,
changeYear: true,
minDate: 0,
onSelect: function (date, dp) {
    updateDatePickerCells();
},
onChangeMonthYear: function(month, year, dp) {
    updateDatePickerCells();
},
beforeShow: function(elem, dp) { 
    updateDatePickerCells();
}});
updateDatePickerCells();
function updateDatePickerCells(dp) {

setTimeout(function () {

    var cellContents = {1: '20', 15: '60', 28: '$99.99'};


    $('.ui-datepicker td > *').each(function (idx, elem) {
        var value = '$125';//cellContents[idx + 1] || 0;


        var className = 'datepicker-content-' + CryptoJS.MD5(value).toString();

        if(value == 0)
            addCSSRule('.ui-datepicker td a.' + className + ':after {content: "\\a0";}'); //&nbsp;
        else
            addCSSRule('.ui-datepicker td a.' + className + ':after {content: "' + value + '";}');

        $(this).addClass(className);
    });
}, 0);
}
var dynamicCSSRules = [];
function addCSSRule(rule) {
if ($.inArray(rule, dynamicCSSRules) == -1) {
    $('head').append('<style>' + rule + '</style>');
    dynamicCSSRules.push(rule);
}
}

Demo link... http://jsfiddle.net/pratikgavas/e3uu9/131/

1 Comment

If you want text only for selected date then you can declare it in cellContents array... e.g. var cellContents = {1: '20', 15: '60', 28: '$99.99'}; where 1, 15, 28 are dates and 20, 60, 99.99 are values

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.