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
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']" }); });
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.
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,
};
}
});
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.
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);
}
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;
}
});