Search code examples
javascripthtmlcssdatepickerflatpickr

How to change the color of the deselected date in flatpickr


I am using flatpickr inside a project of mine. My scenario is that I have range date picker that uses flatpickr. After I select a range of dates, I have another date picker where all the selected dates get selected and turn green. Now, whenever I want to deselect a date from the selected dates, the date should turn red. If I select the deselected one, it should turn green again.

enter image description here

Here is the html code for the date pickers:

<div class="column-2 w-100 w-100 d-flex justify-content-between">
  <p class="w-25 fs-6 fw-bold">Operational calendar</p>
  <div class="w-75 d-flex gap-3">
    <input type="text" name="" class="form-control bg-light" id="dateRangePicker" placeholder="Select date" />
    {{-- <input type="date" class="form-control" /> --}}
  </div>
</div>

<div class="column-2 w-100 w-100 d-flex justify-content-between">
  <p class="w-25 fs-6 fw-bold">Opening and closing dates</p>
  <div class="w-75 d-flex gap-3">
    <input type="hidden" name="" />
    <div id="dateRangePicker2" class="w-100"></div>
    {{-- <input type="date" class="form-control" /> --}}
  </div>
</div>

Here is the JavaScript code:

document.addEventListener("DOMContentLoaded", function () {
  var dateRangePicker = document.getElementById("dateRangePicker");
  var dateRangePicker2 = document.getElementById("dateRangePicker2");

  var picker = flatpickr(dateRangePicker, {
    mode: "range",
    dateFormat: "d-m-Y",
    onChange: function (selectedDates) {
      updateMultiDatePicker(selectedDates);
    },
  });

  var picker2 = flatpickr(dateRangePicker2, {
    mode: "multiple",
    dateFormat: "Y-m-d",
    open: true,
    inline: true,
    allowInput: true,
    onChange: function (selectedDates) {
      updateDeselectedDates(selectedDates);
    },
  });

  function updateMultiDatePicker(selectedDates) {
    const allDates = document.querySelectorAll("#dateRangePicker2 .flatpickr-day");
    allDates.forEach(function (el) {
      el.classList.add("blabla");
    });
    // console.log(allDates)
    if (selectedDates.length === 2) {
      var startDate = selectedDates[0];
      var endDate = selectedDates[1];
      var datesToUpdate = [];

      var currentDate = new Date(startDate);
      while (currentDate <= endDate) {
        datesToUpdate.push(new Date(currentDate));
        currentDate.setDate(currentDate.getDate() + 1);
      }

      picker2.setDate(datesToUpdate);
    }
  }

  function updateDeselectedDates(selectedDates) {
    const allSelectedDates = document.querySelectorAll(".flatpickr-day.selected");
    // allSelectedDates.addClass

    allSelectedDates.forEach(function (dateElement) {
      dateElement.classList.add("blabla");

      if (!dateElement.classList.contains("blabla")) {
        dateElement.addEventListener("click", function () {
          dateElement.classList.add("deselected");
        });
      }
      const dateString = dateElement.getAttribute("aria-label");
      const isSelected = selectedDates.includes(dateString);

      // if (isSelected) {
      //     if (!dateElement.classList.contains('selected')) {
      //         dateElement.classList.add('deselected');
      //     } else {
      //         dateElement.classList.remove('deselected');
      //     }
      // }
    });
  }
});

So as you can see for the green color dates, when they are deselected, the should turn red. This is what I want and this far I've got.


Solution

  • It seems like flatpickr doesn't provide an API to add custom classes to the date elements within the calendar, but we can select and style the deselected dates using their aria-label values.

    1. Create a constructed stylesheet. This is where we will hold are CSS rules for the red deselected dates:
      const disableStyles = new CSSStyleSheet();
      document.adoptedStyleSheets.push(disableStyles);
      
    2. Get the list of aria-labels on the dates we are interested in updateMultiDatePicker():
      function updateMultiDatePicker(selectedDates) {
        // …
        let ariaLabels = [];
        // …
        while (currentDate <= endDate) {
          // …
          ariaLabels = ariaLabels.concat(
            picker2.formatDate(
              new Date(currentDate),
              picker2.config.ariaDateFormat,
            ),
          );
          // …
        }
      
    3. Convert this list of aria-label values to CSS selectors:
      const selectors = ariaLabels
        .map((label) => `[aria-label="${label}"]`)
        .join(",");
      
    4. Add the CSS rule to make these date cells in the calendar red:
      disableStyles.replace(
        `#dateRangePicker2 + .flatpickr-calendar :is(${selectors}):not(.selected) { background-color: red }`,
      );
      

    const disableStyles = new CSSStyleSheet();
    document.adoptedStyleSheets.push(disableStyles);
    
    document.addEventListener("DOMContentLoaded", function () {
      var dateRangePicker = document.getElementById("dateRangePicker");
      var dateRangePicker2 = document.getElementById("dateRangePicker2");
    
      var picker = flatpickr(dateRangePicker, {
        mode: "range",
        dateFormat: "d-m-Y",
        onChange: function (selectedDates) {
          updateMultiDatePicker(selectedDates);
        },
      });
    
      var picker2 = flatpickr(dateRangePicker2, {
        mode: "multiple",
        dateFormat: "Y-m-d",
        open: true,
        inline: true,
        allowInput: true,
        onChange: function (selectedDates) {
          updateDeselectedDates(selectedDates);
        },
      });
    
      function updateMultiDatePicker(selectedDates) {
        const allDates = document.querySelectorAll(
          "#dateRangePicker2 .flatpickr-day",
        );
        allDates.forEach(function (el) {
          el.classList.add("blabla");
        });
    
        // console.log(allDates)
        if (selectedDates.length === 2) {
          var startDate = selectedDates[0];
          var endDate = selectedDates[1];
          var datesToUpdate = [];
          let ariaLabels = [];
    
          var currentDate = new Date(startDate);
          while (currentDate <= endDate) {
            datesToUpdate.push(new Date(currentDate));
            ariaLabels = ariaLabels.concat(
              picker2.formatDate(
                new Date(currentDate),
                picker2.config.ariaDateFormat,
              ),
            );
            currentDate.setDate(currentDate.getDate() + 1);
          }
    
          picker2.setDate(datesToUpdate);
    
          const selectors = ariaLabels
            .map((label) => `[aria-label="${label}"]`)
            .join(",");
          disableStyles.replace(
            `#dateRangePicker2 + .flatpickr-calendar :is(${selectors}):not(.selected) { background-color: red }`,
          );
        }
      }
    
      function updateDeselectedDates(selectedDates) {
        const allSelectedDates = document.querySelectorAll(
          ".flatpickr-day.selected",
        );
        // allSelectedDates.addClass
    
        allSelectedDates.forEach(function (dateElement) {
          dateElement.classList.add("blabla");
    
          if (!dateElement.classList.contains("blabla")) {
            dateElement.addEventListener("click", function () {
              dateElement.classList.add("deselected");
            });
          }
          const dateString = dateElement.getAttribute("aria-label");
          const isSelected = selectedDates.includes(dateString);
    
          // if (isSelected) {
          //     if (!dateElement.classList.contains('selected')) {
          //         dateElement.classList.add('deselected');
          //     } else {
          //         dateElement.classList.remove('deselected');
          //     }
          // }
        });
      }
    });
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css" integrity="sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.css" integrity="sha512-MQXduO8IQnJVq1qmySpN87QQkiR1bZHtorbJBD0tzy7/0U9+YIC93QWHeGTEoojMVHWWNkoCp8V6OzVSYrX0oQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.js" integrity="sha512-K/oyQtMXpxI4+K0W7H25UopjM8pzq0yrVdFdG21Fh5dBe91I40pDd9A4lzNlHPHBIP2cwZuoxaUSX0GJSObvGA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <div class="column-2 w-100 w-100 d-flex justify-content-between">
      <p class="w-25 fs-6 fw-bold">Operational calendar</p>
      <div class="w-75 d-flex gap-3">
        <input
          type="text"
          name=""
          class="form-control bg-light"
          id="dateRangePicker"
          placeholder="Select date"
        />
      </div>
    </div>
    
    <div class="column-2 w-100 w-100 d-flex justify-content-between">
      <p class="w-25 fs-6 fw-bold">Opening and closing dates</p>
      <div class="w-75 d-flex gap-3">
        <input type="hidden" name="" />
        <div id="dateRangePicker2" class="w-100"></div>
      </div>
    </div>