35

The datepicker in jQueryUI renders with a dynamic position. It renders according to its css if there's enough room for it, but if there isn't enough window space it tries to render on screen. I need it to stay put and render in the same place every time, independent of the screen position or other circumstances. How can this be done with the jQueryUI datepicker? Other implementations of jQuery datepicker seem to have ways of doing this, but I don't see a way of doing it for the UI version.

The answer doesn't seem to be just modifying the css:

.ui-datepicker { width: 17em; padding: .2em .2em 0; (trying top/margin-top/position:relative, etc. here...)}

...since when the datepicker is created it dynamically creates top and left in element style. Haven't found a way around this yet. One approach I saw is to give something like this in the beforeShow option:

beforeShow: function(input,inst){
                                inst.dpDiv.css({ 
                                   'top': input.offsetHeight+ 'px', 
                                   'left':(input.offsetWidth - input.width)+ 'px'
                                               });
                                }

This has some effect but the top and left properties are still being dynamically set after this is run when the datepicker renders. It's still trying to render on screen. How do I get it to always render in the same spot? My next step is probably to go into the datepicker guts and start pulling things out. Any ideas?

Note that the answer (for the UI version) is not in:

Community
  • 1
  • 1
Purrell
  • 12,461
  • 16
  • 58
  • 70

14 Answers14

66

Posting this in hopes that it will help others. At least as of v1.8.1 of datepicker, using 'window.DP_jQuery.datepicker' no longer works, because the pointer(right term?) now is named with a timestamp of its creation - so for example it would now be 'window.DP_jQuery_1273700460448'. So now, rather than using the pointer to the datepicker object, refer to it directly like this:

$.extend($.datepicker,{_checkOffset:function(inst,offset,isFixed){return offset}});

Many thanks for the answer below for getting me what I needed.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
JaredC
  • 1,360
  • 14
  • 11
  • No prob - I'm happy somebody else got something out of this, I remember spending an hour or two figuring this out. – Purrell Jun 02 '10 at 18:49
  • 1
    Nice one, really helped me! For anyone who needs it you can then change the margins of the datepicker like this: .ui-datepicker { margin-left: -361px; } – bbeckford Oct 06 '10 at 14:20
  • eeehh, maybe a stupid question, but how to use this? In beforeshow? – Jilco Tigchelaar Jan 25 '13 at 21:16
  • 1
    Works like a charm. @JilcoTigchelaar I'm just making the function returning a fixed offset. `$.extend($.datepicker,{_checkOffset:function(inst,offset,isFixed){offset.top = 80; offset.left=180; return offset;}});` – Felix Wessendorf Jan 28 '13 at 14:09
  • Sorry for ask this but how do I use it? To be specific, where should I put that line? – mutanic Feb 06 '13 at 02:43
  • 2
    @mutanic - $(document).ready(function () {put the line here}); – Adi Apr 02 '14 at 09:00
10

Edit: JaredC gave an updated answer for later versions of jQuery above. Switching accepted answer to that.

Alright, this is a matter of monkeypatching the feature it has of attempting to always render in the viewport since there's no option provided to enable/disable the feature. Luckily it's uncoupled and isolated in one function so we can just go in and override it. The following code completely disables that feature only:

$.extend(window.DP_jQuery.datepicker,{_checkOffset:function(inst,offset,isFixed){return offset}});

_checkOffset is called when it's opening and it does calculations on the offset and returns a new offset if it would otherwise be outside of the view port. This replaces the function body to just pass the original offset right through. Then you can use the beforeShow setting hack and/or the .ui-datepicker css class to put it wherever you want.

Purrell
  • 12,461
  • 16
  • 58
  • 70
6

bind focusin after using datepicker change css of datepicker`s widget wish help

$('input.date').datepicker();
$('input.date').focusin(function(){
    $('input.date').datepicker('widget').css({left:"-=127"});
});
anubiskong
  • 1,733
  • 1
  • 13
  • 6
  • when you already see an answer accepted, whats the point in answering again. Anyway, Welcome to SO! :) – LGAP Feb 22 '13 at 09:11
  • Showing way how to do it without a 'hack' is always a goot thing. Accepted solution **(overriding internal 'private' logic of datepicker)** may broke that fix with each new version of jquery.ui. Looking at accepted answer it actually seems to me like that is the case... – CodeLama Apr 29 '16 at 11:23
4

The problem I was having is that the datepicker is positioned incorrectly inside fixed elements, such as fancy/lightboxes. For some reason, the datepicker switches into "fixed" positioning mode when this happens, and then the offset calculation becomes incorrect, when absolute positioning would have worked just fine.

However, we can get the correct fixed position with this hack:

const checkOffset = $.datepicker._checkOffset;

$.extend($.datepicker, {
    _checkOffset: function(inst, offset, isFixed) {
        if(!isFixed) {
            return checkOffset.apply(this, arguments);
        }

        let isRTL = this._get(inst, "isRTL");
        let obj = inst.input[0];

        while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
            obj = obj[isRTL ? "previousSibling" : "nextSibling"];
        }

        let rect = obj.getBoundingClientRect();

        return {
            top: rect.top,
            left: rect.left,
        };
    }
});
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Interesting. But how can I use this on specific datepickers only? It seems not to work with standard DPs, only with the ones included in a dialog in my case. – JonSnow Apr 02 '19 at 15:45
  • @SchweizerSchoggi Not sure. If you can obtain an instance of the datepicker object, you might be able to overwrite `_checkOffset` for that instance only. Oh, but this code does check if the datepicker is "fixed" or not, maybe you could just tweak that logic to suit your needs. – mpen Apr 02 '19 at 18:56
  • I have used your solution but added + inst.input[0].clientHeight to the rect.top return value!. thanks – celerno Jul 23 '21 at 17:56
4

with jQuery:

beforeShow : function(input,inst){
    var offset = $(input).offset();
    var height = $(input).height();
    window.setTimeout(function () {
        $(inst.dpDiv).css({ top: (offset.top + height) + 'px', left:offset.left + 'px' })
    }, 1);
}
1

In your css file, for example:

#ui-datepicker-div {
  position: absolute !important;
  top: auto !important;
  left: auto !important;
}

Your important settings, whatever they are, will override the inline defaults.

Joe Lalgee
  • 971
  • 7
  • 5
  • 2
    @hcharge actually, they dont, if you use !important inside the CSS style. Only inline styles having !important themselves can then overwrite the !important coming from CSS - http://jsfiddle.net/NTCP7/ – Katai Feb 23 '13 at 12:31
  • While `!important` is a bit of a CSS hack of last resort, this solution works well and is the easiest to implement. – Neil Laslett Sep 18 '13 at 01:33
  • 1
    This answer is correct to the very specific problem of positioning the datepicker calendar widget at an absolute position. The answer is not correct in regard to the more general title "How to control positioning of jQueryUI datepicker?" – wh81752 Jul 16 '15 at 10:13
1

earlier i tried giving top, left in beforeShow event of datepicker.js, that's get override by _showDatePicker method of jquery-ui-custom.js . But after timeout the window its working fine. Below is the code

beforeShow : function(input,inst) {
  var offset = js.select("#" + dpId).offset();
                        var height = js.select("#" + dpId).height();
                        var width = js.select("#" + dpId).width();
                        window.setTimeout(function () {
                              js.select(inst.dpDiv).css({ top: (offset.top + height - 185) + 'px', left: (offset.left + width + 50) + 'px' })
                        }, 1);
}
Sudeep
  • 31
  • 1
  • 6
  • 1
    This solution is nothing more than a hack. Although it works in general, you may notice a kind of "jump" on the datepicker widget, especially if the position generated by datepicker is far away from your "hacked" position. This is of course only secondary, cause your solution may not work at all on "slow" computers. – wh81752 Jul 16 '15 at 10:05
1

This is quite an old question however I recently ran into an issue similar to this with the jQuery UI Datepicker. We were already using the solution posted by @JaredC above (specifically this code snippet: $.extend($.datepicker,{_checkOffset:function(inst,offset,isFixed){return offset}});) however it would not work for a modal that had an input in which we needed to render a dropdown.

This issue would occur because when scrolling down the page the offset of the input in the modal changes relative to the top of the scrolled page. The resulting behavior would mean that the datepicker would render in a different vertical position depending on how far you scrolled down the page (note: while visible and scrolling the datepicker was already fixed). The solution to this issue ("datepicker input nested within a modal") is to instead calculate the vertical positioning of the input relative to the view screen and then add the height of the input (allowing the "top" css property of the datepicker to be right below that of the input).

The following snippet is in coffeescript. Instead of returning the regular offset as per @JaredC's solution we instead obtain the elementId of the input from the 'inst' object and then access the object via jquery in order to use it to calculate the input's distance from the top of the screen relative to the viewport.

# CoffeeScript
$.extend( $.datepicker, { _checkOffset: (inst,offset,isFixed) ->
        offset.top = $("##{inst.id}").offset().top - $(window).scrollTop() + $("##{inst.id}")[0].getBoundingClientRect().height
        offset
    }
)

// JavaScript
$.extend($.datepicker, {
    _checkOffset: function(inst, offset, isFixed) {
        offset.top = $("#" + inst.id).offset().top - $(window).scrollTop() + $("#" + inst.id)[0].getBoundingClientRect().height;
        return offset;
    }
});
mpen
  • 272,448
  • 266
  • 850
  • 1,236
Eric Walsh
  • 3,035
  • 1
  • 16
  • 21
  • 1
    Your coffee is riddled with syntax errors. Would you mind fixing them? Or better yet, transpiling it to JS for us? **Edit:** Nvm, SO just screwed up the spacing. This is why I hate whitespace-dependent languages. I'm amending your answer with the JS, hope that's OK. – mpen Jun 02 '16 at 17:51
1

The accepted answer works very well overall.

However, using jQuery UI v1.11.4, I had an issue where the datepicker would position itself away from the input in a modal window (fixed positioning) when the browser window has been scrolled down. I was able to fix this problem editing the accepted answer as follows:

const checkOffset = $.datepicker._checkOffset;

$.extend($.datepicker, {
  _checkOffset: function(inst, offset, isFixed) {
    if(isFixed) {
      return checkOffset.apply(this, arguments);
    } else {
      return offset;
    }
  }
});
sebsonic2o
  • 356
  • 2
  • 7
1
$("first-selector").datepicker().bind('click',function () {
      $("#ui-datepicker-div").appendTo("other-selector");
});

<style>
#ui-datepicker-div {
        position: absolute !important;
        top: auto !important;
        left: auto !important;
        z-index: 99999999 !important;
}
</style>
Pardeep Goyal
  • 164
  • 1
  • 5
1

This is how it worked out for me:

$( "input[name='birthdate']" ).datepicker({
    beforeShow : function(input,inst){
    window.setTimeout(function () {
        $("#ui-datepicker-div").position({ my: "left top", at: "left bottom", of: input });
    }, 1);
    }
});

To get it positioned when the screen gets resized:

$(window).resize(function(){ $("#ui-datepicker-div").position({ my: "left top", at: "left bottom", of: "input[name='birthdate']" }); });
obeliksz
  • 468
  • 9
  • 24
1

I am creating a dashboard with dynamic information created with jquery-ajax and none of the solutions here worked, so I needed to create my own solution, when you create dynamic content maybe you need to run the code several times (When the document is already loaded or when you create new date pickers dinamically), so, this prevents to create double events on the elements that you already configured:

    function fixDatePicker (){
   $( ".datePicker:not(.datePicker.datePickerClickEnabled)").each(function (event){
    var dateP = $(this);
    dateP.addClass("datePickerClickEnabled");
    dateP.datepicker();
    dateP.on("click", function (e){
        var tPos = $(e.target).position();
        $(e.target).parent().append ($("#ui-datepicker-div"));
        $("#ui-datepicker-div").css({'top':tPos.top,'left':tPos.left});
        $(e.target).datepicker("show");
    });

});
    }

 

You may find two problems with this solution, that are: The first one is that the parent must have a relative position, and the second one is: because the picker changes from parent, styles may be damaged, the only thing that you need to do to force these styles with !important or with better css-especificity, like this:

div#ui-datepicker-div{
  font-size: 12px;
}

Because I am not a fan of using !important, please consider this:

https://www.w3schools.com/css/css_specificity.asp

-1

here is a more simple solution

var $picker = $( ".myspanclass" ).datepicker(); // span has display: inline-block
$picker.find('.ui-datepicker').css('margin-left', '-50px');
Benjamin
  • 961
  • 1
  • 11
  • 23
  • This answer is wrong cause the thread started asked quite specifically for a static render location. Your answer simply shifts the location 50px to the left. – wh81752 Jul 16 '15 at 10:00
-1

By default the calendar of the date picker was display to the upper-right of my input box, so the top of it was hidden by my menu bar. Here's how I positioned the widget (jquery-ui version 1.11.4) very simply (note that 'ui-datepicker' is the class name given by jquery-ui):

$(document).ready(function() {
$(".ui-datepicker").css('margin-left', '-50px', 'margin-top', '-50px');
//... 
}
Eva M
  • 625
  • 6
  • 11
  • Your problem misses a correlation with the stated problem of this thread. Second, why applying css properties via JS and not via CSS stylesheets? – wh81752 Jul 16 '15 at 10:08