Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datepicker years go off the top of the page when scrollposition is not at the top #6388

Open
aidan-doherty opened this issue Jun 12, 2019 · 15 comments

Comments

@aidan-doherty
Copy link

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
@seibtph
Copy link

seibtph commented Oct 14, 2019

Any idea how can this fixed?

@pwcreative
Copy link

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?

@aidan-doherty
Copy link
Author

@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.

@aloksoni11
Copy link

1+

@cunneen
Copy link

cunneen commented May 11, 2020

@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/

@cunneen
Copy link

cunneen commented May 11, 2020

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/

@doughballs
Copy link

doughballs commented May 11, 2020

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; }

  1. 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 here jQuery('.dropdown-content').css('top', topPosition); is not applying, but works if we paste into the console.

  1. 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.

@cunneen
Copy link

cunneen commented May 11, 2020

@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?

@cunneen
Copy link

cunneen commented May 11, 2020

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
Copy link

@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?!)

@doughballs
Copy link

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!

@cunneen
Copy link

cunneen commented May 11, 2020

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;
}

@cunneen
Copy link

cunneen commented May 11, 2020

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;
}

@doughballs
Copy link

Good work!

@yugendran-G
Copy link

how to implement the same without any modal pop-up view?
container | Element | null | Specify a DOM element to render the calendar in, by default it will be placed before the input.
this is not working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants