Jonis Maurin Ceara
Jonis Maurin Ceara

Reputation: 165

Steps not working in noUISlider

I have code to create dynamic sliders for user to chose frequencies (from 118.000 Mhz to 136.975 Mhz). This list is dynamic so users have the capability to add or remove frequencies (and their sliders). But I have two problems:

  1. Step is not working. It works perfectly for '15' or higher, but below this values, step doesn't work. If I use '10', steps become 20. Same if I choose 5 as step (which is the value that I need).

  2. I can't make keyboard inputs work! I can make it work with static code (ex: static div, values, etc..), but not with this dynamic code. Also, there are two problems with keyboard inputs.

    • It always move the last item of list (probably because of 'I', that doesn't exist on event....but I don't know how to solve/fix it).
    • The value always slides to either the max or min of slider. If I type -> then slider moves to max value. If I type <- then slider move to min value.

Any tips?

var freqs = ["118000", "119550", "121725"];
for (var i = 0; i < freqs.length; i++) {

  var $last = $('#freq_table').find('tbody').append("<tr><td class='col-md-2' id='value_freq_" + i + "'>oi</td><td class='col-md-8 center slider' id='td_slider_" + i + "'><div id='slider_" + i + "'></div></td></tr>");

  var tmp = noUiSlider.create($('#slider_' + i).get(0), {
    start: freqs[i],
    step: 10,
    range: {
      'min': 118000,
      'max': 136975
    },
    format: wNumb({
      decimals: 3,
      thousand: '.'
    }),
  }).on('update', function(values, handle) {
    var id = this.target.id.slice(-1);
    $('#value_freq_' + id).html(values[handle] + ' Mhz');
    freqs[id] = values[handle];
  });
  var item = $('#slider_' + i).get(0);
  var handle = item.querySelector('.noUi-handle');

  handle.addEventListener('keydown', function(e) {

    var value = Number(item.noUiSlider.get());

    if (e.which === 37) {
      item.noUiSlider.set(value - 10);
    }

    if (e.which === 39) {
      item.noUiSlider.set(value + 10);
    }
  });

};

Upvotes: 4

Views: 2291

Answers (1)

user9263373
user9263373

Reputation: 1074

The problems you're having are due to the wNumb option decimals: 3 which affects the .get() value in your keydown handler. In effect it returns the value divided by 1000. To correct this, after calling .get(), you then need to re-multiply the value by 1000 before adding/subtracting 5 (or whatever step value may be).

The other problem I noticed is the keydown event was only bound to the last slider in your for loop regardless of which slider you click/slide with the mouse. The reason for that is because the value of item in the keydown event was always #slider_2 (2 coming from the last loop iteration). To remedy that, assign a variable to the slider that was just clicked using the following statement var slider = $(this).closest('.noUi-target')[0];.

A couple of UX enhancements

  • You can activate the slider by clicking on the label next to it and then use the keyboard to move the slider.
  • Using the keyboard with a slider step of 5 moves the slider very slowly. So I added an accelerator calculation that increases the slider speed the longer the key remains pressed and resets when released. This allows the keyboard to have full granularity without being painfully slow.

var freqs = ["118000", "119550", "121725"];
var stepSize = 5; // stepSize for the slider and arrow key increments

// Populate sliders by looping the freqs array
for (var i = 0; i < freqs.length; i++) {

  // Append label and slider to table
  var $last = $('#freq_table').find('tbody').append("<tr><td class='col-md-2' id='value_freq_" + i + "'>oi</td><td style='' class='col-md-8 center slider' id='td_slider_" + i + "'><div id='slider_" + i + "'></div></td></tr>");

  // Create the noUiSlider
  var tmp = noUiSlider.create($('#slider_' + i).get(0), {
    start: freqs[i],
    step: stepSize,
    range: {
      'min': 118000,
      'max': 136975
    },
    format: wNumb({
      decimals: 3,
      thousand: '.'
    }),
  }).on('update', function(values, handle) {
    var id = this.target.id.slice(-1);
    $('#value_freq_' + id).html(values[handle] + ' Mhz');
    freqs[id] = values[handle]; // Update the freqs array with new slider value
  });

  var item = $('#slider_' + i).get(0);
  var handle = item.querySelector('.noUi-handle');
  var keydownIteration = 1; // track iterations of keydown until keyup.  To be used for slider acceleration.

  // Add keyboard event to the slider handle
  handle.addEventListener('keydown', function(e) {
    var slider = $(this).closest('.noUi-target')[0];
    var value = Number(slider.noUiSlider.get()) * 1000;
    var multiplier = Math.floor(keydownIteration/10) + 1; // multiplier will be used to calculate slider acceleration

    if (e.which === 37) {
      slider.noUiSlider.set(Math.trunc(value - stepSize*multiplier));
    }

    if (e.which === 39) {
      slider.noUiSlider.set(Math.trunc(value + stepSize*multiplier));
    }

    keydownIteration++; // increment counter 
  });

  // Add keyboard event to the slider handle
  handle.addEventListener('keyup', function(e) {
    keydownIteration = 1; // resetting the iteration also resets accelaration
  });

};

// When clicking on the label, focus and click the slider
$('#freq_table').on('click', 'td:first-child', function () {
  $(this).closest('tr').find('.noUi-handle').focus().click();
});
#freq_table  td:nth-child(2) {
  width : 300px;
}

#freq_table  td:first-child {
  cursor : pointer;
}

#freq_table td {
  padding : 10px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/11.0.3/nouislider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/wnumb/1.1.0/wNumb.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/11.0.3/nouislider.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="freq_table">
  <thead>
  </thead>
  <tfoot>
  </tfoot>
  <tbody>
  </tbody>
</table>

Upvotes: 2

Related Questions