6
\$\begingroup\$

I created a JS script for a friend's website to allow the visitors to choose the date and time of their delivery.

I'm new in web-development and I will appreciate a feedback from professionals like you.

The script doesn't have an UI at the moment ,so for settings just need to change the shippingDateTime.

The script automatically add to an html form the elements needed so I think should be usable on every project

I will post the code below , for a live demo you can see here or a Github for a full code

Thanks

"use strict";
const container = document.querySelector(".cart");
const checkoutButton = document.querySelector(".btn.cart__checkout");
const defaultOption = "-Choose an option-";
const disableCheckoutButton = true;
const daysOffRisp = "No delivery this date";
const shippingDateTime = [
  [
    "Delivery",
    {
      delayD: 0,
      timeSlots: [
        [12, 13],
        [13, 15],
        [18.15, 19],
        [22, 23],
        [23, 24],
      ],
      lastOrder: 19,
      daysOff: ["2020-12-31"],
      delayH: 1,
    },
  ],
  [
    "Pick-up",
    {
      delayD: 0,
      delayH: 1,
      timeSlots: 3,
      startTime: 12.0,
      endTime: 23.0,
      lastOrder: 22,
      daysOff: [2, 3],
    },
  ],
];

////////   End settings

let dateValid = undefined;
let timeValid = undefined;
let deliveryMetValid = undefined;
let deliveryMethods = "";
checkoutButton.insertAdjacentHTML(
  "beforebegin",
  `<div class="btn newbutton" style="display:none">${checkoutButton.innerHTML}</div>`
);
const newButton = document.querySelector(".newbutton");

//if disableCheckoutButton is set to true
function f_disabledCheckout() {
  function sostituteButton(b) {
    if (b === "newB") {
      newButton.style.display = "inline-block";
      checkoutButton.style.display = "none";
    } else if (b === "oldB") {
      newButton.style.display = "none";
      checkoutButton.style.display = "inline-block";
    }
  }

  if (!disableCheckoutButton) {
    return;
  } else if (disableCheckoutButton && dateValid && timeValid) {
    sostituteButton("oldB");
  } else {
    sostituteButton("newB");

    newButton.addEventListener("click", function () {
      function animate(element) {
        element.classList.add("attention");
        element.addEventListener("animationend", (e) =>
          e.target.classList.remove("attention")
        );
      }

      if (dateValid != true) animate(datePicker);
      if (timeValid != true) animate(input);
      if (deliveryMetValid != true) animate(instancesDeliveryMethods);
    });
  }
}
f_disabledCheckout();

//generate one shipping button foreach shipping method available
for (const methods of shippingDateTime)
  deliveryMethods += `<label class="radiobutton deliverymethods" for="${methods[0]}">
       <input class="radio" type="radio" id="${methods[0]}" name="Method" value="${methods[0]}">${methods[0]}</label>`;

//attach all the elements to the main div
container.insertAdjacentHTML(
  "afterbegin",
  ` <div class="ext">
      <label class="caption">Delivery method</label>
      <div class="container_deliverymethods">
      ${deliveryMethods}
      </div>
    </div>
    <div class="ext ext-datepicker" style="visibility:hidden">
      <label class="caption">Delivery date</label>
      <div><input type="date" class="datepicker-btn" min="" required ><span class="validate"></span></div>
      <input type="text" class="converteddate" name="Date" style="display:none">
    </div>
    <div class="ext ext-input" style="visibility:hidden">
      <label class="caption">Delivery time</label>
      <input list="" id="input-datalist" autocomplete="off" name="Time" value="${defaultOption}">
    </div>
    <datalist id="datalist">
    </datalist>
    `
);

//reset hidden elemnts
function f_reset(el) {
  if (el === "datepick") {
    extInput.style.visibility = "hidden";
    validator.className = "validate";
    datePicker.value = "";
    dateValid = false;
    f_disabledCheckout();
  }
  if (el === "datepick" || el === "timeslot") {
    extInput.style.visibility = "hidden";
    timeValid = false;
    f_disabledCheckout();
  }
  if (el === "datepick" || el === "timeslot" || el === "datalist") {
    datalist.style.display = "none";
    f_disabledCheckout();
  }
}

// convert in ms
function f_ms(d = 0, h = 0, m = 0) {
  h += Number((d % 1).toFixed(4).substring(2));
  m += Number((h % 1).toFixed(4).substring(2));

  const result = d * 24 * 60 * 60 * 1000 + h * 60 * 60 * 1000 + m * 60 * 1000;

  return result;
}

//listen for click on delivery methos
const instancesDeliveryMethods = document.querySelector(
  ".container_deliverymethods"
);
const validator = document.querySelector(".validate");
let choosenDeliveryM = "";

function checkDeliveryMethods(e) {
  validator.className = "validate";
  deliveryMetValid = true;
  choosenDeliveryM = e.target.value;
  f_reset("datepick");
  showDatePicker(e.target.value);
}
instancesDeliveryMethods.addEventListener("change", checkDeliveryMethods);

//calculate first availabe date
let addNDay = 0;

function showDatePicker(deliveryvalue) {
  addNDay = 0;
  for (const dv of shippingDateTime) {
    if (dv[0] === deliveryvalue) {
      const deliveryOptions = dv[1];
      const timeslot = deliveryOptions.timeSlots;
      const delayH = f_ms(0, deliveryOptions.delayH);

      if (
        typeof timeslot == "number" &&
        f_ms(0, deliveryOptions.endTime) < now + delayH + f_ms(0, timeslot)
      ) {
        addNDay = 1;
      }
      if (
        typeof timeslot == "object" &&
        f_ms(0, timeslot[timeslot.length - 1][0]) < now + delayH
      ) {
        addNDay = 1;
      }

      now > f_ms(0, deliveryOptions.lastOrder) ? (addNDay = 2) : "";

      deliveryOptions.delayD ? (addNDay = deliveryOptions.delayD) : "";

      dateMin(addNDay);
      return;
    }
  }
}
const datePicker = document.querySelector(".datepicker-btn");
const extDatePicker = document.querySelector(".ext-datepicker");
const day = new Date();
day.setHours(0, 0, 0, 0);
const today = day.getTime();

console.log(today);

const nowraw = new Date();
const hours = nowraw.getHours();
const minutes = nowraw.getMinutes();
const now = f_ms(0, hours) + f_ms(0, 0, minutes);

//set min value of the datePicker
let min = new Date();
function dateMin(minDelay) {
  f_reset("datepick");

  min.setTime(today + f_ms(minDelay));

  if (jqui === true) {
    $(".datepicker-btn").datepicker("option", "minDate", minDelay);
  } else {
    datePicker.min = min.toISOString().slice(0, 10);
  }

  extDatePicker.style.visibility = "visible";
}

// listen for change in date picker
function setDeliverSlots() {
  const dateFromDPraw = new Date(datePicker.value);
  dateFromDPraw.setHours(0, 0, 0, 0);
  const dateFromDP = dateFromDPraw.getTime();

  let optiontimeSlotsValues = [];
  input.value = defaultOption;

  if (dateFromDP >= min) {
    validator.className = "validate y";
    document.querySelector(".converteddate").value =
      dateFromDPraw.getDate() +
      "-" +
      (dateFromDPraw.getMonth() + 1) +
      "-" +
      dateFromDPraw.getFullYear();
    dateValid = true;

    for (const deliveryMetAv of shippingDateTime) {
      const deliveryMetName = deliveryMetAv[0];
      const deliveryMetSlots = deliveryMetAv[1].timeSlots;
      const delayD = f_ms(deliveryMetAv[1].delayD);
      const delayH = f_ms(0, deliveryMetAv[1].delayH);
      const daysOff = deliveryMetAv[1].daysOff;
      const deliveryMetEndTime = f_ms(0, deliveryMetAv[1].endTime);
      const deliveryMetStartTime = f_ms(0, deliveryMetAv[1].startTime);

      if (deliveryMetName === choosenDeliveryM) {
        if (daysOff.includes(7)) {
          daysOff.splice(daysOff.indexOf(7), 1, 0);
        }

        if (
          daysOff.includes(dateFromDPraw.getDay()) ||
          daysOff.includes(datePicker.value)
        ) {
          input.value = daysOffRisp;
          extInput.style.visibility = "visible";
          dateValid = false;
          datalist.innerHTML = "";
          validator.className = "validate n";
          f_disabledCheckout();
          return;
        }

        if (
          typeof deliveryMetSlots == "object" //if timeslot is an array
        ) {
          for (const slot of deliveryMetSlots) {
            const startS = f_ms(0, slot[0]);
            const endS = f_ms(0, slot[1]);

            if (
              (dateFromDP >= min && dateFromDP != today) ||
              (dateFromDP == today && now < endS + delayH)
            ) {
              optiontimeSlotsValues.push(slot);
            }
          }
        } else if (
          typeof deliveryMetSlots == "number" //if timeslot is a number
        ) {
          const slotDuration = f_ms(0, deliveryMetAv[1].timeSlots);
          let currentSlot = deliveryMetStartTime;

          while (deliveryMetEndTime >= currentSlot + delayH) {
            if (dateFromDP == today && currentSlot >= now + delayH) {
              optiontimeSlotsValues.push([
                currentSlot / 60 / 60 / 1000,
                (currentSlot + slotDuration) / 60 / 60 / 1000,
              ]);
            } else if (dateFromDP !== today && dateFromDP >= min) {
              optiontimeSlotsValues.push([
                currentSlot / 60 / 60 / 1000,
                (currentSlot + slotDuration) / 60 / 60 / 1000,
              ]);
            }
            currentSlot += slotDuration;
          }
        }
      }

      if (optiontimeSlotsValues != "") {
        let optionstimeSlots = ``;

        optiontimeSlotsValues.forEach(function (element) {
          let [el1, el2] = [...element];
          function convert(el) {
            el = el.toString();
            if (el.includes(".")) {
              el = el.replace(".", ":").padEnd(5, "0");
            } else {
              el += ":00";
            }
            return el;
          }

          element = convert(el1) + "-" + convert(el2);

          optionstimeSlots += `<option class="btn datalist-options" value="${element}">${element}</option>`;
        });

        datalist.innerHTML = optionstimeSlots;
        extInput.style.visibility = "visible";
      }
    }
  } else {
    validator.className = "validate n";
    dateValid = false;
    f_reset("timeslot");
  }
  f_disabledCheckout();
}
datePicker.addEventListener("input", setDeliverSlots);

//datalist
const input = document.querySelector("#input-datalist");
const extInput = document.querySelector(".ext-input");
const datalist = document.querySelector("#datalist");
input.addEventListener("focus", () => (datalist.style.display = "block"));

datalist.addEventListener("click", function (e) {
  input.value = e.target.value;
  f_reset("datalist");
  checkTime();
});

datalist.style.width = input.offsetWidth + "px";
datalist.style.left = input.offsetLeft + "px";
datalist.style.top = input.offsetTop + input.offsetHeight + "px";

// validate time
function checkTime() {
  timeValid = false;
  const datalistOptions = document.querySelectorAll(".datalist-options");
  datalistOptions.forEach((element) => {
    if (element.value === input.value && element.value != "") {
      timeValid = true;
    }
  });
  f_disabledCheckout();
}
input.addEventListener("change", checkTime);

//close datalist if clicked somewhere outside the datalist
document.querySelector("body").addEventListener("click", (e) => {
  if (e.target != input && e.target.className != "datalist-options")
    f_reset("datalist");
});

//if browser not support input date load jquery data picker
let jqui;
if (datePicker.type === "text") {
  jqui = true;

  const jq = document.createElement("script");
  jq.src = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.5.1.min.js";
  const jui = document.createElement("script");
  jui.src = "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.4/jquery-ui.min.js";
  const css = document.createElement("link");
  css.href =
    "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.4/themes/ui-lightness/jquery-ui.css";
  css.type = "text/css";
  css.rel = "stylesheet";
  const head = document.head || document.getElementsByTagName("head")[0];

  head.appendChild(jq);
  jq.addEventListener("load", function () {
    head.appendChild(jui);
    jui.addEventListener("load", function () {
      head.appendChild(css);
      css.addEventListener("load", function () {
        if (window.jQuery) {
          $(".datepicker-btn").datepicker({
            altField: ".converteddate",
            altFormat: "dd-mm-yy",
          });
          $(".datepicker-btn").on("change", setDeliverSlots);
        } else {
          alert(
            "Something went wrong ,please try reloading the browser or try with a different one "
          );
        }
      });
    });
  });
}

//collect the data (optional)
const datalog = document.querySelector(".datalog");
checkoutButton.addEventListener("click", (e) => {
  //   e.preventDefault();
  let selectedValue;
  function check() {
    const rbs = document.querySelectorAll('input[name="Method"]');
    for (const rb of rbs) {
      if (rb.checked) {
        selectedValue = rb.value;
        break;
      }
    }
  }

  check();
  datalog.value = `Method:${selectedValue} | Date${
    document.querySelector(".converteddate").value
  } | Time${input.value}`;
  f_reset("datepick");
});
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

User friendly

Some points on user friendly forms

Ambiguity

When selecting "Pick-up" the date and time field labels show "Delivery date" and "Delivery time" respectively. This will confuse most users and the service provider will get a pile of emails concerning the ambiguity.

Ensure that all labels and information is as clear as possible and there should be absolutely no ambiguity.

Conformation

When the checkout is clicked there is no confirmation asking the user if the details for pickup or delivery are correct. Users can often click the wrong button you must always give them a way to back out of a mistake.

Remember

After selecting a date and time a user may change their mind in regard to pickup or delivery. If the shipping type is changed the existing fields are reset, this will frustrate some users. Best to keep entered data rather than delete it.

Vetting

Ensure you vet all dates and times.

I was able to set a delivery and pickup time to earlier than my systems clock. eg Checkout text "Method:Delivery | Date29-11-2020 | Time12:00-13:00" even though my clock was set to 2pm 29-11-2020.

The date's year field allows up to 6 numbers, so I do hope that my delivery is not late in the year 275760. "Method:Delivery | Date31-3-275760 | Time12:00-13:00"

Be informative

Be as verbose as you can in regards to displaying selected options.

The date picker has day names when selecting the date, however that is lost once the date has been selected. Show the day and month names next to the selected date.

Use 12 hour time, eg 13:00 is 1pm

What does 12:00-13:00 mean, yes most will know, but some may take it to mean 12:00 or 13:00. The delivery is between 12:00 and 13:00. Is that local time?

Don't be sloppy

The checkout report is sloppy with no spaces after "Method:", "Date" and "Time" and no separator ":" after "Date" and "time". Use commas ",", never use "|"

The checkout report

"Method:Delivery | Date30-11-2020 | Time12:00-13:00"

could be

"Method: Delivery, Date: Monday 30th November 2020, Time: Between 1pm and 3pm local time"

\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much for the opinion , I wasn't expecting such detailed feedback. I really appreciate it . I suppose you are based in America . I'm based in uk and i developed this code for a small local shop . I didn't think about use outside uk . And the output was basically just demonstrative as the data are suppose to be processed with the method defined by your form . But anyway I see your points and I will update the code . Thanks \$\endgroup\$ Commented Nov 30, 2020 at 21:45
-1
\$\begingroup\$

You can Shorten the script a fair bit by creating a few helper functions at the begining of the file, shortening a few variable names & removing unecessary redeclarations of var, let and const.. Javascript allows you to chain these as long as they have a comma after each one and the last one still has a semi-colon i've edited the script you provided and included it below. the idea of the helper functions is basically anywhere where your writing out document.getElementById or querySelector() on multiple lines you can just replace this with the shortened function name
such as qS(), qSAll(), getId(), getTag(), getClass() or createElem(); also shortened input to just inp & the animation function name to anime().

"use strict";
 let UN = undefined;
 let qS=(s)=>{
   return document.querySelector(s)
 },
 getTag=(tg)=>{
   return document.getElementsByTagName(tg)
 },
 createElem=(elem)=>{
   return document.createElement(elem)
 };

const container = qS(".cart"),
      checkoutButton = qS(".btn.cart__checkout"),
      defaultOption = "-Choose an option-",
      disableCheckoutButton = true,
      daysOffRisp = "No delivery this date",
      shippingDateTime = [
  [
    "Delivery",
    {
      delayD: 0,
      timeSlots: [
        [12, 13],
        [13, 15],
        [18.15, 19],
        [22, 23],
        [23, 24],
      ],
      lastOrder: 19,
      daysOff: ["2020-12-31"],
      delayH: 1,
    },
  ],
  [
    "Pick-up",
    {
      delayD: 0,
      delayH: 1,
      timeSlots: 3,
      startTime: 12.0,
      endTime: 23.0,
      lastOrder: 22,
      daysOff: [2, 3],
    },
  ],
];

////////   End settings

let dateValid = UN,
    timeValid = UN,
    deliveryMetValid = UN,
    deliveryMethods = "";
checkoutButton.insertAdjacentHTML(
  "beforebegin",
  `<div class="btn newbutton" style="display:none">${checkoutButton.innerHTML}</div>`
);
const newButton = qS(".newbutton");

//if disableCheckoutButton is set to true
function f_disabledCheckout() {
  function sostituteButton(b) {
    if (b === "newB") {
      newButton.style.display = "inline-block";
      checkoutButton.style.display = "none";
    } else if (b === "oldB") {
      newButton.style.display = "none";
      checkoutButton.style.display = "inline-block";
    }
  }

  if (!disableCheckoutButton) {
    return;
  } else if (disableCheckoutButton && dateValid && timeValid) {
    sostituteButton("oldB");
  } else {
    sostituteButton("newB");

    newButton.addEventListener("click", function () {
      function anime(el) {
        el.classList.add("attention");
        el.addEventListener("animationend", (e) =>
          e.target.classList.remove("attention")
        );
      }

      if (dateValid != true) anime(datePicker);
      if (timeValid != true) anime(inp);
      if (deliveryMetValid != true) anime(instancesDeliveryMethods);
    });
  }
}
f_disabledCheckout();

//generate one shipping button foreach shipping method available
for (const methods of shippingDateTime)
  deliveryMethods += `<label class="radiobutton deliverymethods" for="${methods[0]}">
       <input class="radio" type="radio" id="${methods[0]}" name="Method" value="${methods[0]}">${methods[0]}</label>`;

//attach all the elements to the main div
container.insertAdjacentHTML(
  "afterbegin",
  ` <div class="ext">
      <label class="caption">Delivery method</label>
      <div class="container_deliverymethods">
      ${deliveryMethods}
      </div>
    </div>
    <div class="ext ext-datepicker" style="visibility:hidden">
      <label class="caption">Delivery date</label>
      <div><input type="date" class="datepicker-btn" min="" required ><span class="validate"></span></div>
      <input type="text" class="converteddate" name="Date" style="display:none">
    </div>
    <div class="ext ext-input" style="visibility:hidden">
      <label class="caption">Delivery time</label>
      <input list="" id="input-datalist" autocomplete="off" name="Time" value="${defaultOption}">
    </div>
    <datalist id="datalist">
    </datalist>
    `
);

//reset hidden elemnts
function f_reset(el) {
  if (el === "datepick") {
    extInput.style.visibility = "hidden";
    validator.className = "validate";
    datePicker.value = "";
    dateValid = false;
    f_disabledCheckout();
  }
  if (el === "datepick" || el === "timeslot") {
    extInput.style.visibility = "hidden";
    timeValid = false;
    f_disabledCheckout();
  }
  if (el === "datepick" || el === "timeslot" || el === "datalist") {
    datalist.style.display = "none";
    f_disabledCheckout();
  }
}

// convert in ms
function f_ms(d = 0, h = 0, m = 0) {
  h += Number((d % 1).toFixed(4).substring(2));
  m += Number((h % 1).toFixed(4).substring(2));

  const result = d * 24 * 60 * 60 * 1000 + h * 60 * 60 * 1000 + m * 60 * 1000;

  return result;
}

//listen for click on delivery methos
const instancesDeliveryMethods = qS(
  ".container_deliverymethods"
);
const validator = qS(".validate");
let choosenDeliveryM = "";

function checkDeliveryMethods(e) {
  validator.className = "validate";
  deliveryMetValid = true;
  choosenDeliveryM = e.target.value;
  f_reset("datepick");
  showDatePicker(e.target.value);
}
instancesDeliveryMethods.addEventListener("change", checkDeliveryMethods);

//calculate first availabe date
let addNDay = 0;

function showDatePicker(deliveryvalue) {
  addNDay = 0;
  for (const dv of shippingDateTime) {
    if (dv[0] === deliveryvalue) {
      const deliveryOptions = dv[1],
            timeslot = deliveryOptions.timeSlots,
            delayH = f_ms(0, deliveryOptions.delayH);

      if (
        typeof timeslot == "number" &&
        f_ms(0, deliveryOptions.endTime) < now + delayH + f_ms(0, timeslot)
      ) {
        addNDay = 1;
      }
      if (
        typeof timeslot == "object" &&
        f_ms(0, timeslot[timeslot.length - 1][0]) < now + delayH
      ) {
        addNDay = 1;
      }

      now > f_ms(0, deliveryOptions.lastOrder) ? (addNDay = 2) : "";

      deliveryOptions.delayD ? (addNDay = deliveryOptions.delayD) : "";

      dateMin(addNDay);
      return;
    }
  }
}
const datePicker = qS(".datepicker-btn"),
      extDatePicker = qS(".ext-datepicker"),
      day = new Date();
day.setHours(0, 0, 0, 0);
const today = day.getTime();

console.log(today);

const nowraw = new Date(),
      hours = nowraw.getHours();
      minutes = nowraw.getMinutes();
      now = f_ms(0, hours) + f_ms(0, 0, minutes);
   // ^^ These should really be declared as lets as they will likely change.
//set min value of the datePicker
let min = new Date();
function dateMin(minDelay) {
  f_reset("datepick");

  min.setTime(today + f_ms(minDelay));

  if (jqui === true) {
    $(".datepicker-btn").datepicker("option", "minDate", minDelay);
  } else {
    datePicker.min = min.toISOString().slice(0, 10);
  }

  extDatePicker.style.visibility = "visible";
}

// listen for change in date picker
function setDeliverSlots() {
  const dateFromDPraw = new Date(datePicker.value);
  dateFromDPraw.setHours(0, 0, 0, 0);
  const dateFromDP = dateFromDPraw.getTime();

  let optiontimeSlotsValues = [];
  inp.value = defaultOption;

  if (dateFromDP >= min) {
    validator.className = "validate y";
    qS(".converteddate").value =
      dateFromDPraw.getDate() +
      "-" +
      (dateFromDPraw.getMonth() + 1) +
      "-" +
      dateFromDPraw.getFullYear();
    dateValid = true;

    for (const deliveryMetAv of shippingDateTime) {
      const deliveryMetName = deliveryMetAv[0],
            deliveryMetSlots = deliveryMetAv[1].timeSlots,
            delayD = f_ms(deliveryMetAv[1].delayD),
            delayH = f_ms(0, deliveryMetAv[1].delayH),
            daysOff = deliveryMetAv[1].daysOff,
            deliveryMetEndTime = f_ms(0, deliveryMetAv[1].endTime),
            deliveryMetStartTime = f_ms(0, deliveryMetAv[1].startTime);

      if (deliveryMetName === choosenDeliveryM) {
        if (daysOff.includes(7)) {
          daysOff.splice(daysOff.indexOf(7), 1, 0);
        }

        if (
          daysOff.includes(dateFromDPraw.getDay()) ||
          daysOff.includes(datePicker.value)
        ) {
          input.value = daysOffRisp;
          extInput.style.visibility = "visible";
          dateValid = false;
          datalist.innerHTML = "";
          validator.className = "validate n";
          f_disabledCheckout();
          return;
        }

        if (
          typeof deliveryMetSlots == "object" //if timeslot is an array
        ) {
          for (const slot of deliveryMetSlots) {
            const startS = f_ms(0, slot[0]),
                  endS = f_ms(0, slot[1]);

            if (
              (dateFromDP >= min && dateFromDP != today) ||
              (dateFromDP == today && now < endS + delayH)
            ) {
              optiontimeSlotsValues.push(slot);
            }
          }
        } else if (
          typeof deliveryMetSlots == "number" //if timeslot is a number
        ) {
          const slotDuration = f_ms(0, deliveryMetAv[1].timeSlots);
          let currentSlot = deliveryMetStartTime;

          while (deliveryMetEndTime >= currentSlot + delayH) {
            if (dateFromDP == today && currentSlot >= now + delayH) {
              optiontimeSlotsValues.push([
                currentSlot / 60 / 60 / 1000,
                (currentSlot + slotDuration) / 60 / 60 / 1000,
              ]);
            } else if (dateFromDP !== today && dateFromDP >= min) {
              optiontimeSlotsValues.push([
                currentSlot / 60 / 60 / 1000,
                (currentSlot + slotDuration) / 60 / 60 / 1000,
              ]);
            }
            currentSlot += slotDuration;
          }
        }
      }

      if (optiontimeSlotsValues != "") {
        let optionstimeSlots = ``;

        optiontimeSlotsValues.forEach(function (elems) {
          let [el1, el2] = [...elems];
          function convert(el) {
            el = el.toString();
            if (el.includes(".")) {
              el = el.replace(".", ":").padEnd(5, "0");
            } else {
              el += ":00";
            }
            return el;
          }

          elems = convert(el1) + "-" + convert(el2);

          optionstimeSlots += `<option class="btn datalist-options" value="${elems}">${elems}</option>`;
        });

        datalist.innerHTML = optionstimeSlots;
        extInput.style.visibility = "visible";
      }
    }
  } else {
    validator.className = "validate n";
    dateValid = false;
    f_reset("timeslot");
  }
  f_disabledCheckout();
}
datePicker.addEventListener("inp", setDeliverSlots);

//datalist
const inp      = qS("#input-datalist"),
      extInput = qS(".ext-input"),
      datalist = qS("#datalist");
inp.addEventListener("focus", () => (datalist.style.display = "block"));

datalist.addEventListener("click", function (e) {
  inp.value = e.target.value;
  f_reset("datalist");
  checkTime();
});

datalist.style.width = inp.offsetWidth + "px";
datalist.style.left  = inp.offsetLeft + "px";
datalist.style.top   = inp.offsetTop + inp.offsetHeight + "px";

// validate time
function checkTime() {
  timeValid = false;
  const datalistOptions = qS(".datalist-options");
  datalistOptions.forEach((element) => {
    if (element.value === inp.value && element.value != "") {
      timeValid = true;
    }
  });
  f_disabledCheckout();
}
inp.addEventListener("change", checkTime);

//close datalist if clicked somewhere outside the datalist
qS("body").addEventListener("click", (e) => {
  if (e.target != inp && e.target.className != "datalist-options")
    f_reset("datalist");
});

//if browser not support input date load jquery data picker
let jqui;
if (datePicker.type === "text") {
  jqui = true;

  const jq = createElem("script");
  jq.src = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.5.1.min.js";
  const jui = createElem("script");
  jui.src = "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.4/jquery-ui.min.js";
  const css = createElem("link");
  css.href =
    "https://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.4/themes/ui-lightness/jquery-ui.css";
  css.type = "text/css";
  css.rel = "stylesheet";
  const head = document.head || getTag("head")[0];

  head.appendChild(jq);
  jq.addEventListener("load", function () {
    head.appendChild(jui);
    jui.addEventListener("load", function () {
      head.appendChild(css);
      css.addEventListener("load", function () {
        if (window.jQuery) {
          $(".datepicker-btn").datepicker({
            altField: ".converteddate",
            altFormat: "dd-mm-yy",
          });
          $(".datepicker-btn").on("change", setDeliverSlots);
        } else {
          alert(
            "Something went wrong ,please try reloading the browser or try with a different one "
          );
        }
      });
    });
  });
}

//collect the data (optional)
const datalog = qS(".datalog");
checkoutButton.addEventListener("click", (e) => {
  //   e.preventDefault();
  let selectedValue;
  function check() {
    const rbs = qS('inp[name="Method"]');
    for (const rb of rbs) {
      if (rb.checked) {
        selectedValue = rb.value;
        break;
      }
    }
  }
  check();
  datalog.value = `Method:${selectedValue} | Date${
    qS(".converteddate").value
  } | Time${inp.value}`;
  f_reset("datepick");
});

You could also shorten it a bit further by using bitly URL shortener, for the pre-built script librarys that your importing and maybe creating another helper function for addEventListener & appendChild functions. if the animate() function was part of your JQuery then you might need to put these back to the full name.

\$\endgroup\$
1
  • \$\begingroup\$ I'm sorry, but this is bad advice. Shorter code is not better code. Your suggestions hurt readability, which is the top priority for good code. \$\endgroup\$ Commented Nov 28, 2020 at 11:53

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.