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.


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


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


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

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

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

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

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

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

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.


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)


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

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

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.


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

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

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.


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()
        }
    })
})
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.