Eljas Eljas - 1 month ago 14
Javascript Question

Flatpickr onDayCreate add class

I'm struggling with a Flatpickr's (https://chmln.github.io/flatpickr/) onDayCreate event. Is here anyone who knows better how to check if picker's date object matches any date in my array of dates?

I have an array (or array of objects, not really sure how to call this) with dates going like this:

dates: {
"20161029": 3,
"20161030": 0,
"20161031": 0,
"20161101": 4,
"20161102": 4,
"20161103": 4,
"20161104": 5,
"20161105": 1,
"20161106": 0,
"20161107": 4,
"20161108": 3,
"20161109": 3,
"20161110": 4
}


And I would need to check if value is 0, >3 or 5 and add class to that date. Flatpickr has an example but it is using math function to randomize which dates should have new span element (example). But I can't configure my if else to addClass.

Answer

I created a dictionary of the classes just for convenience. You can use the keys of your object as a way to retrieve the number associated to a day when the flatpickr is triggering the onCreateDay callback. With the value associated to a day, you can get the class from the dictionary and, if it's not empty, add it to the element.

I've added some explanations to the code in order to highlight some things I consider relevant.

You can check it running the script in this page (full screen if don't see it) or you can check it in this fiddle.

Hope it helps.

var dates = {
    20161029: 3,
    20161030: 0,
    20161031: 0,
    20161101: 4,
    20161102: 4,
    20161103: 4,
    20161104: 5,
    20161105: 1,
    20161106: 0,
    20161107: 4,
    20161108: 3,
    20161109: 3,
    20161110: 4
  },
  classDict = {
    0: 'redClass',
    1: 'greenClass',
    3: 'blueClass',
    4: 'greyClass',
    5: 'orangeClass'
  };

// Better always use a two digit format in your dates obj
function get2DigitFmt(val) {
  return ('0' + val).slice(-2);
}

// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var date = dayElem.dateObj,
      // Note the + 1 in the month.
      key = date.getFullYear() + get2DigitFmt(date.getMonth() + 1) + get2DigitFmt(date.getDate()),
      value = classDict[dates[key]];
    if (value) {
      dayElem.className += ' ' + value;
    }
  }
});
.redClass {
  background-color: red !important;
}
.greenClass {
  background-color: green !important;
}
.blueClass {
  background-color: blue !important;
}
.greyClass {
  background-color: grey !important;
}
.orangeClass {
  background-color: orange !important;
}
<link rel="stylesheet" href="https://unpkg.com/flatpickr/dist/flatpickr.min.css">
<script src="https://unpkg.com/flatpickr"></script>
<input id="dayCreate" type="text" placeholder="Select Date..">

UPDATE

The idea of the dictionary was to simplify adding/removing classes and avoid ugly switches or long ifs. However, you could easily modify the code in order to filter by value (only values greater than 3 get class) and add any class you want when the condition is met.

For instance (fiddle):

function getClass(value) {
  // Here you could put any logic you want. Ifs, add the value to a string, whatever...
    return value === 4 ? 'redClass' : 'blueClass';
}
// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var date = dayElem.dateObj,
      // Note the + 1 in the month.
      key = date.getFullYear() + get2DigitFmt(date.getMonth() + 1) + get2DigitFmt(date.getDate()),
      value = dates[key];
    if (value > 3) {
      dayElem.className += ' ' + getClass(value);
    }
  }
});

As you can see in the solutions I provided, there's no need to loop over the object all the time in order to get the value associated to a date, you can get it in constant time composing the key of the date from the date that flatpickr provides while constructing the day (onCreateDay callback).

UPDATE

According to the documentation (or so it seems), in order to get the date of a ccurrent day inside of the onDayCreate callback, you must use properties of fp (currentYear and currentMonth) and dayElem (textContent).

However, currentMonth returns always the month that the flatpicker is currently showing, not the month of the day (the calendar can be showing november, but the day can be in october or december) so a bit of tinkering is needed to avoid marking incorrect dates.

In this fiddle you can find a solution that doesn't use dateObj and works more like the documentation says.

And here's the code:

// Better always use a two digit format in your dates obj
function get2DigitFmt(val) {
  return ('0' + val).slice(-2);
}

function getClass(value) {
  // Here you could put any logic you want. Ifs, add the value to a string, whatever...
  return value === 4 ? 'redClass' : 'blueClass';
}

// Adjust month depending on the month's day
function getMonth(currentMonth, dayClass) {
  return currentMonth + (dayClass.contains('prevMonthDay') ? 0 : (1 + Number(dayClass.contains('nextMonthDay'))));
}

function getDateKey(year, month, day) {
  return year + get2DigitFmt(month) + get2DigitFmt(day);
}

// onDayCreate event, add class to day if date has a class
flatpickr("#dayCreate", {
  onDayCreate: function(dObj, dStr, fp, dayElem) {
    var key = getDateKey(fp.currentYear, getMonth(fp.currentMonth, dayElem.className), dayElem.textContent),
      value = dates[key];
    if (value > 3) {
      dayElem.className += ' ' + getClass(value);
    }
  }
});