Materialize: Datepicker years go off the top of the page when scrollposition is not at the top

Created on 12 Jun 2019  路  14Comments  路  Source: Dogfalo/materialize

Expected Behavior


The issue i am bringing up can be replicated from the documentation

When i open the datepicker with the scroll position at the top of the page and select years it displays as expected showing the years going down the way.
datepicker_how_it_should_always_be

Current Behavior

if i scroll down the page and do the same thing as in the expected behavior the year options go up off the screen.
datepicker_example

Possible Solution



Add an option so the select options always go down from the input?

Steps to Reproduce (for bugs)


  1. add datepicker to a scrollable page
  2. Scroll down the page
  3. Open datepicker
  4. Select years input

Context


Your Environment


  • Version used: Testing from materialize site
  • Browser Name and version: Firefox 67.0
  • Operating System and version (desktop or mobile): Linux mint
  • Link to your project (if appropriate): Not my project but here

Most helpful comment

OK this solution is working perfectly for me, thanks for the pointers @doughballs .

(edit: almost perfectly... if you open a dropdown a second time, it regresses... :sigh: )
(edit #2: fixed the above issue with another dodgy hack)

jsfiddle: https://jsfiddle.net/t9uzf3xn/

 // a hack to work around https://github.com/Dogfalo/materialize/issues/6388
  const datePickerSelectAddClass = function() {
    const self = this;
    setTimeout(function() {
      let selector = self.el;
      if (!selector) {
        selector = ".datepicker"
      }
      $(selector).siblings(".datepicker-modal") // modals are generated alongside each date picker
        .find(".select-dropdown.dropdown-trigger") // the .dropdown-trigger element is the trigger for the <selects> to appear
        .each((index, item) => {
          const dateDropdownID = $(item).attr("data-target"); // now we've got the ID of one of the generated <ul> elements
          const dropdownULJQ = $(`#${dateDropdownID}`);
          // add an 'onclick' event handler
          dropdownULJQ.children("li").on("click",()=>{
            datePickerSelectAddClass(); // yeah for some reason our UL elements get destroyed and recreated so we have to do this again.
          });
          dropdownULJQ.addClass("dropdown-content-datepicker") // add our own className to it so we can target it.
        });
    }, 500);
  };

  $('.datepicker').datepicker({
    onOpen: datePickerSelectAddClass
  });
.dropdown-content-datepicker {
  position: fixed;
  height:300px !important;
  top: 10% !important;
}

All 14 comments

Any idea how can this fixed?

I'm unable to replicate this. I'm not sure how it's possible to perform the 'current behaviour' step in the test:

"if i scroll down the page and do the same thing as in the expected behavior the year options go up off the screen."

Pretty vague instructions. How does one click the date picker if it's not visible on the page?

@pwcreative It is still visible on the page as you can see in the example image where it is broken. Another way ive managed to replicate it is by trying the same thing using responsive design mode in the browser. It seems to only open that way when scrolled to a certain position.

1+

@pwcreative here's a jsfiddle to replicate. It seems to depend upon the y-coordinate of the input field to which the date picker is attached i.e. if the input field is near the top of the visible viewport.

https://jsfiddle.net/jnk21do6/6/

I've had some success by forcing the date input to scroll into the centre of the screen when it receives focus:

   $(document).ready(function() {
     // initialize the datepicker
     $('.datepicker').datepicker()
     // when it gets focus, scroll it to the centre
     .on("focus", function() {
       var center = $(window).height() / 2;
       var top = $(this).offset().top;
       if (top > center) {
         $('html, body').animate({
           scrollTop: top - center
         }, 'fast');
       }
       // now open the date picker
       $(this).datepicker("open");
     });
   });

Here's a jsfiddle with the above : https://jsfiddle.net/jnk21do6/18/

I was just playing around with a pen but not quite there yet. I did think about the scroll thing but not quite working for me. Here's my thoughts on a workaround:

1) We need to limit the height of the dropdown, so that it is never taller than the modal. It looks weird to me hanging off the modal. This is an easy fix with css:

.dropdown-content { max-height: 300px; }

2) It seems like the top position of the dropdown is the problem. I'm part way through a fix that will get the top position of the modal, and use that as the top position of the dropdown. The modal is position:fixed, with a top value of 10%, whereas the dropdown is position:absolute, and in this case, the relative may be causing the positioning issue:

 $('.datepicker').datepicker({
      onOpen: function() {
        setTimeout(function(){
          var topPosition = jQuery('.datepicker-modal').offset().top ;

          console.log( topPosition )

          jQuery('.dropdown-content').css('top', topPosition);

        },500)
      }
    });

The last line herejQuery('.dropdown-content').css('top', topPosition); is not applying, but works if we paste into the console.

3) I don't know what the container option is (set to null be default) this may help us to create a container element that the dropdown will use as a parent?

EDIT:

This css seems to be working in my example:

.dropdown-content {
  height:300px !important;
  top:10% !important;
}

https://codepen.io/doughballs/pen/mdeKybx

Note, when you open the dropdown, the page scrolls behind it. Some funky stuff going on. I just tried those to lines of CSS on the docs page and it seems also to fix it, but note that it would apply to all dropdown content, so you may need to be more specific with the selector.

@doughballs thanks for sharing this I'll give it a try! Is the setTimeout to stop the jerkiness that I was seeing with my earlier hack?

Ah I see now: I'm guessing that the top is being overridden by some sort of onopen event handler for the <select>, which is why it later works in the console.

@doughballs thanks for sharing this I'll give it a try! Is the setTimeout to stop the jerkiness that I was seeing with my earlier hack?

Sorry, the setTimeout is to give the modal time to open and position itself - without it, we get a value of 0. What we really want is onOpenEnd, but inexplicably it's not available in the datepicker options (but is apparently available to timepicker?!)

Ah I see now: I'm guessing that the top is being overridden by some sort of onopen event handler for the <select>, which is why it later works in the console.

Yes, possibly, I think an !important flag would do it, or maybe a second function inside the dropdown that sets the top position.

These are all majorily hacky, as I don't fully understand the component!

Edit: This doesn't work properly, the classNames get reverted by some other event.

(previous comment:)

So with a bit of jQuery-fu I've discovered we can find the generated IDs of the month & year dropdowns in the date picker, and then add our own CSS class to the elements so we can target them specifically without affecting other select elements:

$(".datepicker") // get our date pickers
  .siblings(".datepicker-modal") // modals are generated alongside each date picker
  .find(".select-dropdown.dropdown-trigger") // the .dropdown-trigger element is the trigger for the <selects> to appear
  .each((index, item)=>{
    const dateDropdownID = $(item).attr("data-target"); // now we've got the ID of one of the generated <ul> elements
    $(`#${dateDropdownID}`).addClass("dropdown-content-datepicker") // add our own className to it so we can target it.
})

So then we can use a modified version of your CSS rule:

.dropdown-content-datepicker {
  height:300px !important;
  top:10% !important;
}

OK this solution is working perfectly for me, thanks for the pointers @doughballs .

(edit: almost perfectly... if you open a dropdown a second time, it regresses... :sigh: )
(edit #2: fixed the above issue with another dodgy hack)

jsfiddle: https://jsfiddle.net/t9uzf3xn/

 // a hack to work around https://github.com/Dogfalo/materialize/issues/6388
  const datePickerSelectAddClass = function() {
    const self = this;
    setTimeout(function() {
      let selector = self.el;
      if (!selector) {
        selector = ".datepicker"
      }
      $(selector).siblings(".datepicker-modal") // modals are generated alongside each date picker
        .find(".select-dropdown.dropdown-trigger") // the .dropdown-trigger element is the trigger for the <selects> to appear
        .each((index, item) => {
          const dateDropdownID = $(item).attr("data-target"); // now we've got the ID of one of the generated <ul> elements
          const dropdownULJQ = $(`#${dateDropdownID}`);
          // add an 'onclick' event handler
          dropdownULJQ.children("li").on("click",()=>{
            datePickerSelectAddClass(); // yeah for some reason our UL elements get destroyed and recreated so we have to do this again.
          });
          dropdownULJQ.addClass("dropdown-content-datepicker") // add our own className to it so we can target it.
        });
    }, 500);
  };

  $('.datepicker').datepicker({
    onOpen: datePickerSelectAddClass
  });
.dropdown-content-datepicker {
  position: fixed;
  height:300px !important;
  top: 10% !important;
}

Good work!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cope picture cope  路  3Comments

ericlormul picture ericlormul  路  3Comments

ruslandzhumaev picture ruslandzhumaev  路  3Comments

djensen47 picture djensen47  路  3Comments

heshamelmasry77 picture heshamelmasry77  路  3Comments