Como alterar a posição pop-up do controle jQuery DatePicker


109

Alguma ideia de como fazer com que o DatePicker apareça no final da caixa de texto associada, em vez de diretamente abaixo dela? O que tende a acontecer é que a caixa de texto fica na parte inferior da página e o DatePicker muda para cima para dar conta disso e cobre totalmente a caixa de texto. Se o usuário quiser digitar a data em vez de selecioná-la, não pode. Prefiro que apareça logo após a caixa de texto, para que não importe como se ajusta verticalmente.

Alguma ideia de como controlar o posicionamento? Não vi nenhuma configuração para o widget e não tive sorte ao ajustar as configurações de CSS, mas poderia facilmente estar faltando alguma coisa.


1
+1 para não apenas "abordar" a reclamação, que sua solução não é a solução para D_N.
Leônidas

Respostas:


20

A resposta aceita para esta pergunta, na verdade, não é para o jQuery UI Datepicker. Para alterar a posição do jQuery UI Datepicker, basta modificar .ui-datepicker no arquivo css. O tamanho do Datepicker também pode ser alterado desta forma, basta ajustar o tamanho da fonte.


4
Você poderia me dizer quais alterações você fez?
eddy

1
@gfrizzle - sim, aquela minha resposta foi tão errada. Finalmente consegui colocá-lo no lixo. Não tenho ideia do que eu estava pensando naquela época: /
Kev

64
Não tenho ideia de por que essa é a resposta aceita, pois é quase totalmente inútil.
Engenheiro de Software

10
A resposta dada é completamente inútil, pois o datepicker modificará dinamicamente o css do .ui-datepicker antes de abrir o widget.
Whaefelinger

4
Esta resposta não fornece detalhes e não deve ser aceita.
chapeljuice

120

Aqui está o que estou usando:

$('input.date').datepicker({
    beforeShow: function(input, inst)
    {
        inst.dpDiv.css({marginTop: -input.offsetHeight + 'px', marginLeft: input.offsetWidth + 'px'});
    }
});

Você também pode querer adicionar um pouco mais à margem esquerda para que não fique exatamente no campo de entrada.


2
Bom truque. O problema é que nos casos em que o datepicker é mostrado além das bordas da tela, o _showDatepicker tentará realocá-lo, portanto, top e left serão diferentes e marginTop, marginLeft pode precisar de ajuste.
Monzonj

1
Eu usei uma abordagem semelhante - entendi sua ideia de usar beforeShow, mas minha função beforeShow usa o efeito de posição do JQueryUI: $(this).position(my:'left top', at:'left bottom', of:'#altField')(já que estou usando a altFieldopção do
selecionador de data

Isso pode funcionar para definir as margens, mas falha para os atributos "left", "top", "z-index" e "position".
aaronbauman

Pelo que entendi, isso também não funciona, porque jquery redefine a posição após o beforeShow ser chamado.
Engenheiro de Software

A resposta dada está errada porque datepicker irá zerar a posição após o retorno de beforeShow ().
Whaefelinger

40

Eu faço isso diretamente no CSS:

.ui-datepicker { 
  margin-left: 100px;
  z-index: 1000;
}

Meus campos de entrada de data têm 100 px de largura. Também adicionei o Z-index para que o calendário também apareça acima dos pop-ups AJAX.

Eu não modifico o arquivo CSS jquery-ui; Eu sobrecarrego a classe em meu arquivo CSS principal, então posso alterar o tema ou atualizar o widget sem ter que inserir novamente meus mods específicos.


Mesmo problema do outro truque css: só funciona se você agora estiver exatamente onde o selecionador de data deveria estar no CSS. Você não pode usar este método para colocar dinamicamente o selecionador de data. Tive que usar um hack, vinculando em inst.input.focus ()
aaronbauman

Funcionou para mim usando Bootstrap :), mas eu apenas defini a propriedade z-index.
Francisco Goldenstein,

Inútil para qualquer coisa que não seja implementações triviais, pois você não saberá onde o campo de entrada está na página.
Engenheiro de Software

35

Aqui está minha variação de alinhamento do calendário Datepicker.

Eu acho que é muito bom, porque você pode controlar o posicionamento através do jQuery UI Position util.

Uma restrição: jquery.ui.position.js obrigatório.

Código:

$('input[name=date]').datepicker({
    beforeShow: function(input, inst) {
        // Handle calendar position before showing it.
        // It's not supported by Datepicker itself (for now) so we need to use its internal variables.
        var calendar = inst.dpDiv;

        // Dirty hack, but we can't do anything without it (for now, in jQuery UI 1.8.20)
        setTimeout(function() {
            calendar.position({
                my: 'right top',
                at: 'right bottom',
                collision: 'none',
                of: input
            });
        }, 1);
    }
})

Obrigado @kottenator, isso é realmente útil (especialmente com position.js)!
Sebastian

Essa resposta mostra nada mais do que os méritos de position.js.
Whaefelinger

Isso ainda funciona na v1.11.4, mas é realmente um hack sujo, uma pena que a API jQuery não nos permite fazer isso em um afterShowmétodo.
Skoua 01 de

29

Aqui está outra variação que funciona bem para mim, ajuste rect.top + 40, rect.left + 0 para atender às suas necessidades:

$(".datepicker").datepicker({
    changeMonth: true,
    changeYear: true,
    dateFormat: 'mm/dd/yy',
    beforeShow: function (input, inst) {
        var rect = input.getBoundingClientRect();
        setTimeout(function () {
	        inst.dpDiv.css({ top: rect.top + 40, left: rect.left + 0 });
        }, 0);
    }
});
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<form>
<input class="datepicker" name="date1" type="text">
<input class="datepicker" name="date2" type="text">
</form>


Uau, funciona. Usando "inst.dpDiv.css (" top "," 40px! Important ");" não funciona!
emeraldhieu

Longe de ser perfeito para superar um problema de programação introduzindo um tempo limite.
Whaefelinger

Obrigado, funcionou para mim, gostaria de saber se algum dia haverá um evento afterShow, isso seria ótimo !! Mas, por enquanto, isso me ajuda a continuar de qualquer maneira: D
Rob Branaghan

16

Isso funciona para mim:

 beforeShow: function(input, inst) {
     var cal = inst.dpDiv;
     var top  = $(this).offset().top + $(this).outerHeight();
     var left = $(this).offset().left;
     setTimeout(function() {
        cal.css({
            'top' : top,
            'left': left
        });
     }, 10);

6

Primeiro, acho que deve haver um afterShowingmétodo no objeto datepicker, onde você pode alterar a posição depois que o jquery tiver feito todo o seu vodu no _showDatepickermétodo. Além disso, um parâmetro chamadopreferedPosition também seria desejável, então você poderia defini-lo e jquery modificá-lo caso a caixa de diálogo seja renderizada fora da janela de exibição.

Existe um "truque" para fazer essa última coisa. Se você estudar o _showDatepickermétodo, verá o uso de uma variável privada$.datepikcer._pos . Essa variável será configurada se ninguém a tiver configurado antes. Se você modificar essa variável antes de mostrar a caixa de diálogo, o Jquery a pegará e tentará alocar a caixa de diálogo nessa posição, e se renderizar fora da tela, ele a ajustará para ter certeza de que está visível. Parece bom, hein?

O problema é; _posé privado, mas se você não se importa. Você pode:

$('input.date').datepicker({
    beforeShow: function(input, inst)
    {
        $.datepicker._pos = $.datepicker._findPos(input); //this is the default position
        $.datepicker._pos[0] = whatever; //left
        $.datepicker._pos[1] = whatever; //top
    }
});

Mas tome cuidado com as atualizações do Jquery-ui, porque uma mudança na implementação interna do _showDatepickerpode corromper seu código.


1
Eles deveriam ter adicionado isso no jQuery UI como um item configurável.
Aleksej Komarov

3

Eu precisava posicionar o selecionador de data de acordo com um div pai dentro do qual residia meu controle de entrada sem borda. Para fazer isso, usei o utilitário "position" incluído no núcleo da IU do jquery. Eu fiz isso no evento beforeShow. Como outros comentaram acima, você não pode definir a posição diretamente em beforeShow, pois o código datepicker irá redefinir o local após terminar a função beforeShow. Para contornar isso, basta definir a posição usando setInterval. O script atual será concluído, mostrando o selecionador de data e, em seguida, o código de reposicionamento será executado imediatamente após o selecionador de data estar visível. Embora isso nunca deva acontecer, se o selecionador de data não estiver visível após 0,5 segundos, o código tem um fail-safe para desistir e limpar o intervalo.

        beforeShow: function(a, b) {
            var cnt = 0;
            var interval = setInterval(function() {
                cnt++;
                if (b.dpDiv.is(":visible")) {
                    var parent = b.input.closest("div");
                    b.dpDiv.position({ my: "left top", at: "left bottom", of: parent });
                    clearInterval(interval);
                } else if (cnt > 50) {
                    clearInterval(interval);
                }
            }, 10);
        }

Solução muito feia. O maior problema seria um "salto" perceptível do widget do selecionador de data logo após ficar visível, onde de repente sua "realocação" entra em ação. Deve ser evitado a todo custo.
Whaefelinger

2

vincule o foco após usar o datepicker, altere o css do widget do datepicker para obter ajuda

$('input.date').datepicker();
$('input.date').focusin(function(){
    $('input.date').datepicker('widget').css({left:"-=127"});
});

2

eu consertei com meu código jquery personalizado.

  1. dei ao meu campo de entrada datepicker uma classe " .datepicker2 "

  2. encontre sua posição atual no topo e

  3. posicionou a caixa pop-up, cujo nome de classe é " .ui-datepicker "

aqui está o meu código.

$('.datepicker2').click(function(){
    var popup =$(this).offset();
    var popupTop = popup.top - 40;
    $('.ui-datepicker').css({
      'top' : popupTop
     });
});

aqui "40" é meu pixel esperado, você pode mudar com o seu.


1

corrigir problema de posição de exibição daterangepicker.jQuery.js

//Original Code
//show, hide, or toggle rangepicker
    function showRP() {
        if (rp.data('state') == 'closed') {
            rp.data('state', 'open');
            rp.fadeIn(300);
            options.onOpen();
        }
    }


//Fixed
//show, hide, or toggle rangepicker
    function showRP() {
        rp.parent().css('left', rangeInput.offset().left);
        rp.parent().css('top', rangeInput.offset().top + rangeInput.outerHeight());
        if (rp.data('state') == 'closed') {
            rp.data('state', 'open');
            rp.fadeIn(300);
            options.onOpen();
        }
    }

1

Uma função jquery muito simples.

$(".datepicker").focus(function(event){
                var dim = $(this).offset();
                $("#ui-datepicker-div").offset({
                    top     :   dim.top - 180,
                    left    :   dim.left + 150
                });
            });

1

Passei uma quantidade absurda de tempo tentando descobrir esse problema em várias páginas de um novo site que estou desenvolvendo e nada parecia funcionar (incluindo qualquer uma das várias soluções apresentadas acima que implementei. Acho que o jQuery acabou de mudar as coisas já começaram a crescer o suficiente desde que foram sugeridas que as soluções não funcionam mais. Não sei. Honestamente, não entendo por que não há algo simples implementado na interface do usuário do jQuery para configurar isso, como parece ser um problema bastante grande com o calendário sempre aparecendo em alguma posição consideravelmente distante na página da entrada à qual está anexado.

De qualquer forma, eu queria uma solução final genérica o suficiente para que pudesse ser usada em qualquer lugar e funcionasse. Parece que finalmente cheguei a uma conclusão que evita algumas das respostas mais complicadas e específicas do código jQuery acima:

jsFiddle: http://jsfiddle.net/mu27tLen/

HTML:

<input type="text" class="datePicker" />

JS:

positionDatePicker= function(cssToAdd) {
    /* Remove previous positioner */
    var oldScript= document.getElementById('datePickerPosition');
    if(oldScript) oldScript.parentNode.removeChild(oldScript);
    /* Create new positioner */
    var newStyle= document.createElement("style");
    newStyle.type= 'text/css';
    newStyle.setAttribute('id', 'datePickerPostion');
    if(newStyle.styleSheet) newStyle.styleSheet.cssText= cssToAdd;
    else newStyle.appendChild(document.createTextNode(cssToAdd));
    document.getElementsByTagName("head")[0].appendChild(newStyle);
}
jQuery(document).ready(function() {
    /* Initialize date picker elements */
    var dateInputs= jQuery('input.datePicker');
    dateInputs.datepicker();
    dateInputs.datepicker('option', {
        'dateFormat' : 'mm/dd/yy',
        'beforeShow' : function(input, inst) {
            var bodyRect= document.body.getBoundingClientRect();
            var rect= input.getBoundingClientRect();
            positionDatePicker('.page #ui-datepicker-div{z-index:100 !important;top:' + (rect.top - bodyRect.top + input.offsetHeight + 2) + 'px !important;}');
        }
    }).datepicker('setDate', new Date());
});

Essencialmente, anexei uma nova tag de estilo ao cabeçalho antes de cada evento de "show" do selecionador de data (excluindo o anterior, se houver). Esse método de fazer as coisas contorna a grande maioria dos problemas que encontrei durante o desenvolvimento.

Testado no Chrome, Firefox, Safari e IE> 8 (como o IE8 não funciona bem com jsFiddle e estou perdendo o gosto por me preocupar com

Eu sei que isso é uma mistura de contextos jQuery e javascript, mas neste ponto eu simplesmente não quero me esforçar para convertê-lo para jQuery. Seria simples, eu sei. Eu cansei dessa coisa agora. Espero que outra pessoa possa se beneficiar com esta solução.


1

Eles mudaram o nome da classe para ui-datepicker-trigger

Então, isso funciona em jquery 1.11.x

.ui-datepicker-trigger { 
margin-top:7px;
  margin-left: -30px;
  margin-bottom:0px;
  position:absolute;
  z-index:1000;
}


1

Ou você pode usar o evento de foco em seu objeto dateSelect e api de posição juntos. Você pode trocar superior e inferior e esquerda por direita ou centro (ou realmente qualquer coisa que você quiser da API de posição). Dessa forma, você não precisa de um intervalo ou de qualquer solução extremamente complexa e pode configurar o layout para atender às suas necessidades dependendo de onde a entrada está.

dateSelect.focus(function () {
    $("#ui-datepicker-div").position({
        my: "left top",
        at: "left bottom",
        of: $(this)
    });
});

A melhor / mais correta resposta !!
HirsuteJim

0

Também é importante notar que se o IE entrar no modo quirks, seus componentes de UI do jQuery e outros elementos serão posicionados incorretamente.

Para se certificar de que você não cairá no modo quirks, certifique-se de definir seu doctype corretamente para o HTML5 mais recente.

<!DOCTYPE html>

Usar o transicional confunde as coisas. Esperançosamente, isso economizará algum tempo no futuro.


0

Isso coloca a funcionalidade em um método denominado function, permitindo que seu código a encapsule ou que o método seja transformado em uma extensão jquery. Acabei de usar no meu código, funciona perfeitamente

var nOffsetTop  = /* whatever value, set from wherever */;
var nOffsetLeft = /* whatever value, set from wherever */;

$(input).datepicker
(
   beforeShow : function(oInput, oInst) 
   { 
      AlterPostion(oInput, oInst, nOffsetTop, nOffsetLeft); 
   }
);

/* can be converted to extension, or whatever*/
var AlterPosition = function(oInput, oItst, nOffsetTop, nOffsetLeft)
{
   var divContainer = oInst.dpDiv;
   var oElem        = $(this);
       oInput       = $(oInput);

   setTimeout(function() 
   { 
      divContainer.css
      ({ 
         top  : (nOffsetTop >= 0 ? "+=" + nOffsetTop : "-=" + (nOffsetTop * -1)), 
         left : (nOffsetTop >= 0 ? "+=" + nOffsetLeft : "-=" + (nOffsetLeft * -1))
      }); 
   }, 10);
}

0

No Jquery.UI, basta atualizar a função _checkOffset, de modo que viewHeight seja adicionado a offset.top, antes que o offset seja retornado.

_checkOffset: function(inst, offset, isFixed) {
    var dpWidth = inst.dpDiv.outerWidth(),
    dpHeight = inst.dpDiv.outerHeight(),
    inputWidth = inst.input ? inst.input.outerWidth() : 0,
    inputHeight = inst.input ? inst.input.outerHeight() : 0,
    viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
            viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : ($(document).scrollTop()||document.body.scrollTop));
        offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
        offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
        offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? ($(document).scrollTop()||document.body.scrollTop) : 0;

        // now check if datepicker is showing outside window viewport - move to a better place if so.
        offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
            Math.abs(offset.left + dpWidth - viewWidth) : 0);
        offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
            Math.abs(dpHeight + inputHeight) : 0);
**offset.top = offset.top + viewHeight;**
        return offset;
    },

Esta solução é essencialmente correta: injete sua própria função _checkOffset () via jQuery.extend () e retorne um objeto arbitrário com propriedades numéricas "top" e "left", ou seja, jQuery.extend (jQuery.datepicker, {_checkOffset: function (inst , deslocamento, isFixed) {return {top: mytop , left: myleft };}}); onde mytop e myleft são do seu agrado pessoal, por exemplo, derivado de inst .
Whaefelinger

0
.ui-datepicker {-ms-transform: translate(100px,100px); -webkit-transform: translate(100px,100px); transform: translate(100px,100px);}

porque o voteown? isso não resolve o problema? codepen.io/coconewty/pen/rajbNj
James

votado para baixo? Porque você estava apenas lendo o título da pergunta e não fez nenhum esforço para investigar o problema real. Simplesmente suponha que você tenha dois selecionadores de data anexados aos campos de entrada com várias larguras. Como sua solução garante que cada borda esquerda do widget selecionador de data toque a borda direita do campo de entradas? Por outro lado, você deve obter pelo menos um ponto pela originalidade de suas respostas.
Whaefelinger

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.