Obtención del valor de select (desplegable) antes del cambio

237

Lo que quiero lograr es que cada vez que <select>se cambie el menú desplegable, quiero el valor del menú desplegable antes de cambiar. Estoy usando la versión 1.3.2 de jQuery y estoy usando un evento de cambio, pero el valor que obtengo allí es después del cambio.

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

Digamos que actualmente la opción My está seleccionada ahora cuando la cambio para apilar en el evento onchange (es decir, cuando la cambié a stack) Quiero su valor anterior, es decir, mi esperado en este caso.

¿Cómo se puede lograr esto?

Editar: en mi caso, tengo varios cuadros de selección en la misma página y quiero que se aplique lo mismo a todos ellos. También todos mis selectos se insertan después de cargar la página a través de ajax.

Alpesh
fuente
1
¿Has comprobado esto? Stackoverflow.com/questions/1983535/…
Rajat

Respuestas:

434

Combine el evento de enfoque con el evento de cambio para lograr lo que desea:

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

Ejemplo de trabajo: http://jsfiddle.net/x5PKf/766

Andy E
fuente
1
@Alpesh: Desafortunadamente no, ya que no estás usando jQuery 1.4.1 o posterior, no puedes usar live () con el foco y cambiar los eventos. Lo único que puede hacer es ajustar su secuencia de comandos para enlazar después de que los elementos se inserten en la página.
Andy E
1
Gracias, ya he enlazado ambos con live y funcionó con éxito. :)
Alpesh
44
Tenga en cuenta que también debe enfocar el elemento select en su controlador de cambios, de lo contrario no se activará después de un cambio de valor repetido
Alex
52
No estoy de acuerdo con esta solución. Si cambia el valor una vez, funcionará, pero si lo hace una vez más, no lo hará. Tienes que hacer clic en algún lugar para perder el foco y otro clic en el menú desplegable. Sugiero: $ ("# dropdownId"). On ('change', function () {var ddl = $ (this); var previous = ddl.data ('previous'); ddl.data ('previous', ddl .val ());});
free4ride
55
@ free4ride, para arreglar eso, acabo de llamar $(this).blur();al final de la función de cambio
chiliNUT
135

no utilice una var global para esto: almacene el valor anterior en los datos aquí es un ejemplo: http://jsbin.com/uqupu3/2/edit

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

acabo de ver que tiene muchas selecciones en la página; este enfoque también funcionará para usted, ya que para cada selección almacenará el valor anterior en los datos de la selección

Avi Pinto
fuente
jQuery.data es una buena solución aquí. en su solución, está creando un cierre para cada selección que tiene un impacto en el rendimiento si hay muchos elementos seleccionados
Avi Pinto
Mi preocupación no era el rendimiento, y no estoy convencido de que la creación de funciones sea más lenta que de jQuery.datatodos modos.
Agosto Lilleaas
99
Entonces, ¿cuál es su objeción para usar jQuery.data? que yo sepa, la biblioteca jQuery también lo usa internamente y recomienda usarlo
Avi Pinto
Definitivamente de acuerdo con el uso de jQuery Data, gracias por recordarme que estaba a punto de usar la var global. No hay nada malo con la var global, pero el uso de datos es más conciso. Pero encadenar el enfoque y luego el cambio también es bueno :)
Piotr Kula
44
@ppumkin: Una cosa definitivamente está mal con una variable global: ¡solo admite una select! La respuesta aceptada se romperá ya que coincide con todas, selectpero solo almacena el valor de la primera. data Es la mejor manera de hacerlo. +1 :)
Se fue la codificación el
86

Voy por la solución de Avi Pinto que usa jquery.data()

Usar el foco no es una solución válida. Funciona la primera vez que cambia las opciones, pero si permanece en ese elemento de selección y presiona la tecla "arriba" o "abajo". No volverá a pasar por el evento de enfoque nuevamente.

Entonces la solución debería ser más parecida a la siguiente,

//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
})
usuario1310312
fuente
3
¡Gran solución, mucho mejor que la respuesta seleccionada!
TJL
2
La segunda parte funciona bien, pero la primera parte no está configurando el atributo de datos. ¿Algunas ideas?
Sinaesthetic
1
@Sinaesthetic, tuve el mismo problema (y ya voté por esta respuesta). No creo que $ (esto) sobreviva al método .data (). La respuesta de Avi Pinto se remonta al elemento select por su valor, y esto funcionó para mí (así que también voté por Avi).
goodeye
1
¡Esto no funcionará ya que esto! == $ ('mySelect'). Aquí hay una versión fija. 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 línea establece un valor para todos los elementos "mySelect" $('mySelect').data('pre', $(this).val());Debería ser como: $('mySelect').each(function() { $(this).data('pre', $(this).val()); });No tengo suficiente reputación para editar la respuesta: /
Abderrahim
8

Rastrea el valor a mano.

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
fuente
No puedo usar el enfoque anterior porque tengo varios cuadros de selección en la misma página con los que tengo que tratar.
Alpesh el
1
No dijo nada sobre los botones de selección múltiple en su pregunta. Sin embargo, actualicé mi respuesta para admitir varios cuadros.
Agosto Lilleaas
1
la mejor respuesta. Creo que esta solución es mejor que el enfoque usign () y el cambio () porque se basa en jQuery para raither la compatibilidad del navegador y luego la ejecución ef flujo correcto de ambos métodos
coorasse
@coorasse: el flujo correcto de ejecución nunca podría ser diferente. El usuario nunca puede cambiar un valor de cuadros de selección antes de que el elemento tome el foco. Dicho esto, tampoco hay nada de malo en este enfoque :-)
Andy E
Desde el punto de vista del uso de "cierre", no estoy seguro de si esto es mejor que la solución de "enfoque".
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
fuente
Esto funcionó para mí sólo es ligera corrección en el selector de sus dropdownId en lugar de uno, gracias
malkoty
2
Evento de cambio puede ocurrir sin la interacción del usuario, por lo que esta no es una obviedad
qdev
3

Estoy usando el evento "live", mi solución es básicamente similar a Dimitiar, pero en lugar de usar "focus", mi valor anterior se almacena cuando se activa "clic".

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
fuente
2
Yo no usaría esto. ¿Qué sucede si el usuario "tabula" el elemento seleccionado y usa las flechas del teclado para seleccionar una opción diferente?
Christian Lundahl el
@Perplexor sí, no funcionará con teclas, de lo contrario almacenará cada valor cada vez que presione la tecla. Esto solo supone que el usuario haga clic en el menú desplegable. El problema es que el evento 'en vivo' no funcionará con 'enfoque', no conozco una solución mejor.
Cica Gustiani
Podría almacenar el valor actual en foco en una variable separada. Esto es solo desde la parte superior de mi cabeza, así que no estoy seguro de si esto es ideal.
Christian Lundahl
Si 'focus' puede funcionar con el evento 'live', será perfecto. Pero si crea dinámicamente esos menús desplegables, solo puede usar 'live', eso es lo que sé. Desafortunadamente, el "enfoque" en "en vivo" no activa el evento para almacenar los nuevos valores, pero no lo sé con el nuevo jquery
Cica Gustiani
live is dead en las nuevas versiones de jQuery! [ stackoverflow.com/questions/14354040/…
remo
2

mantenga el valor desplegable seleccionado actualmente con jquery elegido en una variable global antes de escribir la función de acción desplegable 'al cambiar'. Si desea establecer el valor anterior en la función, puede usar la variable 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
fuente
1

¿Qué tal usar un evento jQuery personalizado con una interfaz de tipo de vigilancia 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
fuente
1

Sé que este es un hilo viejo, pero pensé que podría agregar un poco más. En mi caso, quería transmitir el texto, val y algunos otros datos. En este caso, es mejor almacenar toda la opción como un valor anterior en lugar de solo el valor.

Código de ejemplo a continuación:

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:

Olvidé agregar .clone () al elemento. al no hacerlo cuando intentas retirar los valores, terminas obteniendo la nueva copia de la selección en lugar de la anterior. El uso del método clone () almacena una copia de select en lugar de una instancia de este.

JBW
fuente
0

Bueno, ¿por qué no almacena el valor seleccionado actual, y cuando se cambia el elemento seleccionado, se almacenará el valor anterior? (y puede actualizarlo nuevamente como lo desee)

Soufiane Hassou
fuente
Bueno, no puedo hacer eso porque en mi caso tengo múltiples cuadros de selección en esa misma página. Y guardar el valor inicial de todos al principio será demasiado aburrido.
Alpesh el
0

Use el siguiente código, lo he probado y está funcionando

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
fuente
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
fuente
0

Me gustaría aportar otra opción para resolver este problema; ya que las soluciones propuestas anteriormente no resolvieron mi escenario.

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

Esto usa jQuery para que def. es una dependencia aquí, pero se puede adaptar para trabajar en javascript puro. (Agregue un oyente al cuerpo, verifique si el objetivo original era una función de selección, ejecución, ...).

Al adjuntar el oyente de cambio al cuerpo, puede estar seguro de que se activará después de oyentes específicos para las selecciones, de lo contrario, el valor de 'data-previous' se sobrescribirá antes de que pueda leerlo.

Por supuesto, esto supone que prefiere utilizar escuchas separadas para su conjunto anterior y su valor de verificación. Encaja perfectamente con el patrón de responsabilidad única.

Nota: Esto agrega esta funcionalidad 'anterior' a todas las selecciones, así que asegúrese de ajustar los selectores si es necesario.

thisisboris
fuente
0

Esta es una mejora en la respuesta @thisisboris. Agrega un valor actual a los datos, por lo que el código puede controlar cuándo se cambia una variable establecida en el valor actual.

(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
fuente
0

Mejor solución:

$('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
fuente
1
esta respuesta se basa en la biblioteca js "Selectric" y no en jQuery o javascript 'simple'. Aunque sea el mejor, no se ajusta a la pregunta original ni dice que el usuario necesita instalar algo más para que funcione
gadget00
0

Hay varias formas de lograr el resultado deseado, esta es mi forma humilde de hacerlo:

Deje que el elemento mantenga su valor anterior, así que agregue un atributo 'previousValue'.

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

Una vez inicializado, 'previousValue' ahora podría usarse como un atributo. En JS, para acceder al Valor anterior de esta selección:

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

Una vez que haya terminado de usar 'previousValue', actualice el atributo al valor actual.

Rishi Alluri
fuente
0

Necesitaba revelar un div diferente basado en la selección

Así es como puedes hacerlo con jquery y sintaxis 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
fuente