Obtendo o valor de select (suspenso) antes da alteração

237

O que eu quero alcançar é que, sempre que o <select>menu suspenso for alterado, desejo o valor do menu suspenso antes da alteração. Estou usando a versão 1.3.2 do jQuery e usando o evento change, mas o valor que estou recebendo lá é após a alteração.

<select name="test">
<option value="stack">Stack</option>
<option value="overflow">Overflow</option>
<option value="my">My</option>
<option value="question">Question</option>
</select>

Vamos dizer que atualmente a opção My está selecionada agora quando eu a altero para empilhar no evento onchange (ou seja, quando eu mudei para empilhar) eu quero o valor anterior, ou seja, o meu esperado neste caso.

Como isso pode ser alcançado?

Editar: no meu caso, estou tendo várias caixas de seleção na mesma página e quero que a mesma coisa seja aplicada a todas elas. Além disso, todos os meus select são inseridos após o carregamento da página através do ajax.

Alpesh
fonte
1
Você já verificou isso: stackoverflow.com/questions/1983535/…
Rajat

Respostas:

434

Combine o evento de foco com o evento de mudança para alcançar o que deseja:

(function () {
    var previous;

    $("select").on('focus', function () {
        // Store the current value on focus and on change
        previous = this.value;
    }).change(function() {
        // Do something with the previous value after the change
        alert(previous);

        // Make sure the previous value is updated
        previous = this.value;
    });
})();

Exemplo de trabalho: http://jsfiddle.net/x5PKf/766

Andy E
fonte
1
@Alpesh: Infelizmente não, já que você não está usando o jQuery 1.4.1 ou posterior, você não pode usar live () com o foco e os eventos de alteração . A única coisa que você pode fazer é ajustar seu script para vincular depois que os elementos forem inseridos na página.
Andy E
1
Obrigado, eu já vinculei o live e ele funcionou com sucesso. :)
Alpesh
4
Note que você também deve se concentrar para fora elemento select no seu manipulador de mudança, caso contrário não será disparado após a mudança do valor repetido
Alex
52
Eu não concordo com esta solução. Se você alterar o valor uma vez que funcione, mas se você fizer mais uma vez, não funcionará. Você precisa clicar em algum lugar para perder o foco e outro clique na lista suspensa. Eu sugiro: $ ("# dropdownId"). On ('change', function () {var ddl = $ (this); var previous = ddl.data ('previous'); ddl.data ('previous', ddl .val ());});
free4ride 16/01
5
@ free4ride, a correção que eu só chamar $(this).blur();no fim da função de mudança
chiliNUT
135

não use uma var global para isso - armazene o valor anterior nos dados. Aqui está um exemplo: http://jsbin.com/uqupu3/2/edit

o código para ref:

$(document).ready(function(){
  var sel = $("#sel");
  sel.data("prev",sel.val());

  sel.change(function(data){
     var jqThis = $(this);
     alert(jqThis.data("prev"));
     jqThis.data("prev",jqThis.val());
  });
});

acabei de ver que você tem muitas seleções na página - essa abordagem também funcionará para você, pois para cada seleção você armazenará o valor anterior nos dados da seleção

Avi Pinto
fonte
jQuery.data é uma boa solução aqui. na sua solução, você está criando um fechamento para cada seleção que tenha um impacto perfeito se houver muitos elementos selecionados
Avi Pinto
Minha preocupação não era com o desempenho e não estou convencido de que a criação de funções seja mais lenta do que jQuery.dataqualquer maneira.
August Lilleaas
9
então qual é a sua objeção ao usar o jQuery.data? tanto quanto eu sei a biblioteca jQuery também usa-lo internamente, e recomenda usá-lo
Avi Pinto
Definitivamente, concordo em usar o jQuery Data - obrigado por me lembrar que eu estava prestes a usar a var global. Não há nada errado com a var global, mas o uso de dados é mais conciso. Mas o foco encadeamento e mudança, então também é bom :)
Piotr Kula
4
@ppumkin: Uma coisa definitivamente errada com uma variável global: suporta apenas uma select! A resposta aceita vai quebrar como ele corresponde a todos selectmas apenas armazena o valor da primeira. data é a melhor maneira de fazer isso. +1 :)
Gone Coding
86

Eu vou para a solução de Avi Pinto que usa jquery.data()

Usar o foco não é uma solução válida. Funciona na primeira vez que você altera as opções, mas se você permanecer nesse elemento selecionado, pressione a tecla "para cima" ou "para baixo". Não passará pelo evento de foco novamente.

Portanto, a solução deve ser mais parecida com a seguinte,

//set the pre data, usually needed after you initialize the select element
$('mySelect').data('pre', $(this).val());

$('mySelect').change(function(e){
    var before_change = $(this).data('pre');//get the pre data
    //Do your work here
    $(this).data('pre', $(this).val());//update the pre data
})
user1310312
fonte
3
Ótima solução, muito melhor que a resposta selecionada!
TJL
2
A segunda parte funciona bem, mas a primeira parte não está configurando o atributo de dados. Alguma ideia?
Sinaesthetic
1
@ Sinestésico, eu tive o mesmo problema (e já votei esta resposta). Eu não acho que $ (this) sobreviva ao método .data (). A resposta de Avi Pinto remonta ao elemento select por seu valor, e isso funcionou para mim (então eu também votei em Avi).
goodeye 16/08
1
Isso não funcionará, pois isso! == $ ('mySelect'). Aqui está uma versão fixa. var $selectStatus = $('.my-select'); $selectStatus.on('change', function () { var newStatus = $(this).val(); var oldStatus = $(this).data('pre'); }).data('pre', $selectStatus.val());
Capy
2
@Sinaesthetic esta linha define um valor para todos os elementos "mySelect" $('mySelect').data('pre', $(this).val());Deve ser como: $('mySelect').each(function() { $(this).data('pre', $(this).val()); });Eu não tenho reputação suficiente para editar a resposta: /
Abderrahim
8

Acompanhe o valor manualmente.

var selects = jQuery("select.track_me");

selects.each(function (i, element) {
  var select = jQuery(element);
  var previousValue = select.val();
  select.bind("change", function () {
    var currentValue = select.val();

    // Use currentValue and previousValue
    // ...

    previousValue = currentValue;
  });
});
August Lilleaas
fonte
Não posso usar a abordagem acima porque estou tendo várias caixas de seleção na mesma página com as quais tenho que lidar.
Alpesh
1
Você não disse nada sobre vários botões de seleção na sua pergunta. Atualizei minha resposta para suportar várias caixas.
August Lilleaas
1
melhor resposta. Eu acho que esta solução é melhor do que o foco usign () e mudança () porque ele se baseia em jquery para raither compatibilidade do navegador, em seguida, a execução ef fluxo correto de ambos os métodos
coorasse
@coorasse: o fluxo correto de execução nunca poderia ser diferente. Um valor de caixas de seleção nunca pode ser alterado pelo usuário antes que o elemento entre em foco. Dito isto, não há nada de errado com esta abordagem, quer :-)
Andy E
Do ponto de vista do uso de 'fechamento', não tenho certeza se isso é melhor que a solução 'foco'.
James Poulose
8
 $("#dropdownId").on('focus', function () {
    var ddl = $(this);
    ddl.data('previous', ddl.val());
}).on('change', function () {
    var ddl = $(this);
    var previous = ddl.data('previous');
    ddl.data('previous', ddl.val());
});
free4ride
fonte
Isso funcionou para mim apenas ligeira correção está na dropdownId selector seus em vez de um, graças
malkoty
2
Alterar evento pode ocorrer sem interação do usuário, de modo que este não é um brainer
qdev
3

Estou usando o evento "ao vivo", minha solução é basicamente semelhante ao Dimitiar, mas, em vez de usar o "foco", meu valor anterior é armazenado quando o "clique" é acionado.

var previous = "initial prev value";
$("select").live('click', function () {
        //update previous value
        previous = $(this).val();
    }).change(function() {
        alert(previous); //I have previous value 
    });
Cica Gustiani
fonte
2
Eu não usaria isso. E se o usuário 'separar' o elemento selecionar e usar as setas do teclado para selecionar uma opção diferente?
Christian Lundahl
@ Perplexor sim, ele não funcionará com as teclas, caso contrário, ele armazenará cada valor sempre que você pressionar a tecla. Isso supõe apenas que o usuário clique no menu suspenso. O problema é que o evento 'ao vivo' não funcionará com 'foco', não conheço nenhuma solução melhor.
Cica Gustiani
Você pode armazenar o valor atual em foco em uma variável separada. Isso é apenas do topo da minha cabeça, então não tenho certeza se isso é ideal.
Christian Lundahl
Se o 'foco' puder funcionar com o evento 'ao vivo', será perfeito. Mas se você criar dinamicamente esses menus suspensos, poderá usar apenas 'ao vivo', é o que eu sei. Infelizmente, o 'foco' em 'ao vivo' não aciona o evento para armazenar os novos valores, mas eu não sei com o novo jquery
Cica Gustiani
live está morto nas novas versões do jQuery! [ stackoverflow.com/questions/14354040/…
remo 15/03
2

mantenha o valor suspenso atualmente selecionado com o jquery escolhido em uma variável global antes de escrever a função de ação suspensa 'on change'. Se você deseja definir o valor anterior na função, pode usar a variável global.

//global variable
var previousValue=$("#dropDownList").val();
$("#dropDownList").change(function () {
BootstrapDialog.confirm(' Are you sure you want to continue?',
  function (result) {
  if (result) {
     return true;
  } else {
      $("#dropDownList").val(previousValue).trigger('chosen:updated');  
     return false;
         }
  });
});
Deshani Tharaka
fonte
1

Que tal usar um evento jQuery personalizado com uma interface do tipo de relógio angular;

// adds a custom jQuery event which gives the previous and current values of an input on change
(function ($) {
    // new event type tl_change
    jQuery.event.special.tl_change = {
        add: function (handleObj) {
            // use mousedown and touchstart so that if you stay focused on the
            // element and keep changing it, it continues to update the prev val
            $(this)
                .on('mousedown.tl_change touchstart.tl_change', handleObj.selector, focusHandler)
                .on('change.tl_change', handleObj.selector, function (e) {
                // use an anonymous funciton here so we have access to the
                // original handle object to call the handler with our args
                var $el = $(this);
                // call our handle function, passing in the event, the previous and current vals
                // override the change event name to our name
                e.type = "tl_change";
                handleObj.handler.apply($el, [e, $el.data('tl-previous-val'), $el.val()]);
            });
        },
        remove: function (handleObj) {
            $(this)
                .off('mousedown.tl_change touchstart.tl_change', handleObj.selector, focusHandler)
                .off('change.tl_change', handleObj.selector)
                .removeData('tl-previous-val');
        }
    };

    // on focus lets set the previous value of the element to a data attr
    function focusHandler(e) {
        var $el = $(this);
        $el.data('tl-previous-val', $el.val());
    }
})(jQuery);

// usage
$('.some-element').on('tl_change', '.delegate-maybe', function (e, prev, current) {
    console.log(e);         // regular event object
    console.log(prev);      // previous value of input (before change)
    console.log(current);   // current value of input (after change)
    console.log(this);      // element
});
Nick M
fonte
1

Eu sei que esse é um tópico antigo, mas achei que poderia acrescentar um pouco mais. No meu caso, eu queria passar o texto, val e alguns outros dados attr. Nesse caso, é melhor armazenar toda a opção como um valor anterior, em vez de apenas o valor.

Exemplo de código abaixo:

var $sel = $('your select');
$sel.data("prevSel", $sel.clone());
$sel.on('change', function () {
    //grab previous select
    var prevSel = $(this).data("prevSel");

    //do what you want with the previous select
    var prevVal = prevSel.val();
    var prevText = prevSel.text();
    alert("option value - " + prevVal + " option text - " + prevText)

    //reset prev val        
    $(this).data("prevSel", $(this).clone());
});

EDITAR:

Esqueci de adicionar .clone () ao elemento. ao não fazer isso, quando você tenta recuperar os valores, acaba puxando a nova cópia do select em vez da anterior. O uso do método clone () armazena uma cópia do select em vez de uma instância dele.

JBW
fonte
0

Bem, por que você não armazena o valor selecionado atual e, quando o item selecionado é alterado, o valor antigo é armazenado? (e você pode atualizá-lo novamente como desejar)

Soufiane Hassou
fonte
Bem, eu não posso fazer isso, porque no meu caso, estou tendo várias caixas de seleção na mesma página. E salvar o valor inicial de todos no início será muito complicado.
Alpesh
0

Use o código a seguir, eu testei e seu funcionamento

var prev_val;
$('.dropdown').focus(function() {
    prev_val = $(this).val();
}).change(function(){
            $(this).unbind('focus');
            var conf = confirm('Are you sure want to change status ?');

            if(conf == true){
                //your code
            }
            else{
                $(this).val(prev_val);
                $(this).bind('focus');
                return false;
            }
});
Er.KT
fonte
0
(function() {

    var value = $('[name=request_status]').change(function() {
        if (confirm('You are about to update the status of this request, please confirm')) {
            $(this).closest('form').submit(); // submit the form
        }else {
            $(this).val(value); // set the value back
        }
    }).val();
})();
Bilal
fonte
0

Eu gostaria de contribuir com outra opção para resolver esse problema; uma vez que as soluções propostas acima não resolveram o meu cenário.

(function()
    {
      // Initialize the previous-attribute
      var selects = $('select');
      selects.data('previous', selects.val());

      // Listen on the body for changes to selects
      $('body').on('change', 'select',
        function()
        {
          $(this).data('previous', $(this).val());
        }
      );
    }
)();

Isso usa jQuery para que def. é uma dependência aqui, mas isso pode ser adaptado para funcionar em javascript puro. (Adicione um ouvinte ao corpo, verifique se o destino original era uma função de seleção, execução, ...).

Ao anexar o ouvinte de alterações ao corpo, você pode ter certeza de que isso será acionado após ouvintes específicos para as seleções; caso contrário, o valor de 'data-previous' será substituído antes que você possa lê-lo.

Obviamente, isso pressupõe que você prefere usar ouvintes separados para o valor definido anteriormente e o valor de verificação. Ele se encaixa perfeitamente no padrão de responsabilidade única.

Nota: Isso adiciona essa funcionalidade 'anterior' a todas as seleções; portanto, faça o ajuste fino dos seletores, se necessário.

thisisboris
fonte
0

Esta é uma melhoria na resposta @thisisboris. Ele adiciona um valor atual aos dados, para que o código possa controlar quando uma variável definida para o valor atual é alterada.

(function()
{
    // Initialize the previous-attribute
    var selects = $( 'select' );
    $.each( selects, function( index, myValue ) {
        $( myValue ).data( 'mgc-previous', myValue.value );
        $( myValue ).data( 'mgc-current', myValue.value );  
    });

    // Listen on the body for changes to selects
    $('body').on('change', 'select',
        function()
        {
            alert('I am a body alert');
            $(this).data('mgc-previous', $(this).data( 'mgc-current' ) );
            $(this).data('mgc-current', $(this).val() );
        }
    );
})();
ermSO
fonte
0

Melhor solução:

$('select').on('selectric-before-change', function (event, element, selectric) {
    var current = element.state.currValue; // index of current value before select a new one
    var selected = element.state.selectedIdx; // index of value that will be selected

    // choose what you need
    console.log(element.items[current].value);
    console.log(element.items[current].text);
    console.log(element.items[current].slug);
});
ViES
fonte
1
esta resposta é baseada na biblioteca js "Selectric" e não em jQuery ou javascript 'simples'. Eventhough é o melhor, ele não se adequar a pergunta original nem diz que o usuário precisa instalar outra coisa para que ele funcione
gadget00
0

Existem várias maneiras de alcançar o resultado desejado, esta é a minha humilde maneira de fazê-lo:

Deixe o elemento manter seu valor anterior, então adicione um atributo 'previousValue'.

<select id="mySelect" previousValue=""></select>

Depois de inicializado, 'previousValue' agora poderia ser usado como um atributo. Em JS, para acessar o valor anterior deste, selecione:

$("#mySelect").change(function() {console.log($(this).attr('previousValue'));.....; $(this).attr('previousValue', this.value);}

Depois de terminar de usar 'previousValue', atualize o atributo para o valor atual.

Rishi Alluri
fonte
0

Eu precisava revelar uma div diferente com base na seleção

É assim que você pode fazer isso com a sintaxe jquery e es6

HTML

<select class="reveal">
    <option disabled selected value>Select option</option>
    <option value="value1" data-target="#target-1" >Option 1</option>
    <option value="value2" data-target="#target-2" >Option 2</option>
</select>
<div id="target-1" style="display: none">
    option 1
</div>
<div id="target-2" style="display: none">
    option 2
</div>

JS

$('select.reveal').each((i, element)=>{
    //create reference variable 
    let $option = $('option:selected', element)
    $(element).on('change', event => {
        //get the current select element
        let selector = event.currentTarget
        //hide previously selected target
        if(typeof $option.data('target') !== 'undefined'){
            $($option.data('target')).hide()
        }
        //set new target id
        $option = $('option:selected', selector)
        //show new target
        if(typeof $option.data('target') !== 'undefined'){
            $($option.data('target')).show()
        }
    })
})
Dieter Gribnitz
fonte