chrisdavies / tiny-date-picker

A small, modern, dependency-free date picker
https://chrisdavies.github.io/tiny-date-picker/
415 stars 88 forks source link

Clicked date-picker element is outside of its DOM tree in event.target property causing "click-outside" events to not work as expected #132

Open jarmo opened 2 years ago

jarmo commented 2 years ago

I'm using TinyDatePicker on a Bootstrap Offcanvas element which is basically like a modal dialog appearing from the edge of the screen. It has a sensible default behavior of closing itself when user clicks outside of the Offcanvas element.

For some reason, when clicking on any TinyDatePicker element then event.target node does not seem to be in a DOM tree anymore. Offcanvas performs offcanvasEl.contains(event.target) (Node#contains) on each click event and since it returns false by the reason explained above then offcanvas element will be always closed on any click on TinyDatePicker.

I've also used appendTo option for TinyDatePicker and I can see that the created picker is inside of my offcanvas element.

This is how I'm creating TinyDatePicker:

var offcanvas = document.querySelector(".offcanvas")
var datepicker = TinyDatePicker(offcanvas.querySelector("#date"), {
    appendTo: offcanvas,
    mode: "dp-below"
})

And this is how I can verify that TinyDatePicker is within my offcanvas element:

datepicker.on("show", (event) => {
  var node = event.target
  while (node) {console.error(node); node = node.parentElement}
})

Opening TinyDatePicker element results in the output of printing out DOM tree up to html element including the expected offcanvas element.

And this is how I can verify that TinyDatePicker clicked target is not within my offcanvas element anymore:

offcanvas.addEventListener("click", (event) => {
  var node = event.target
  while (node) {console.error(node); node = node.parentElement}
})

Clicking on any TinyDatePicker element results with the following output:

<button class="dp-day undefined" tabindex="-1" type="button" data-date="1658350800000">
<div class="dp-days">
<div class="dp-cal">

Clicking on any other element on offcanvas element prints out the whole tree up to html element.

I have a suspicion that TinyDatePicker will remove itself from DOM too soon - it should do it in the next JavaScript event loop cycle (via setTimeout(0) for example) to avoid this kind of problems. Just to clarify that this problem is not offcanvas specific, but any component specific, which wants to check where click exactly happened.

This is the ugly hack how I can solve this currently if anyone else happens to stumble on the same problem:

offcanvas.addEventListener("click", (event) => {
  var isDatePickerClick = Array.from(event.target.classList).some((clazz) => clazz.startsWith("dp-"))
  if (!isDatePickerClick) return

  event.stopPropagation()
})