2

I am putting together an HTML5 form for music teachers so they can classify their music library a bit easier. One of the requested elements is a double handled range slider that would be able to define the range of a particular piece. This is all fine if we were talking 0 to 100, but we're talking G-Flat 3 to B5, a series of arbitrary values that wouldn't normally be found on a range slider.

What I'm thinking of doing is creating an array of all the different note ranges from G-Flat 3 to B5 (lowest to highest), and then somehow mapping those to a double handled range slider. Problem is, I don't know how to do that, nor do I know if it is even possible.

In essence, I would like to create an array:

Array = ['Gb3', 'G3', 'G#3', 'Ab3', 'A3',... 'A#5', 'Bb5', 'B5'];

Somehow match those up to numeric values:

0 = 'Gb3';
1 = 'G3';
2 = 'G#3';

And then map those to a double handle range slider.

Any one know if there is a library, or plugin or magic toadstool that can point me in the right direction?

UPDATE: I want to thank EVERYONE who posted to this. There are so many fantastic answers, I'm having a very difficult time choosing "the" answer.

Murphy1976
  • 1,415
  • 8
  • 40
  • 88
  • 1
    It seems you've already matched notes up to numeric values: their index. If it's "all fine if we're talking 0 to 100," then it seems 0 to `n` array indices should be no problem. – Matt Mokary Feb 07 '17 at 21:30

4 Answers4

3

I found this polyfill for a now-abandoned proposal to add a multiple attribute to the <input type="range"> spec. It's simple and works well enough, but has an unfortunate bug: Event handlers aren't fired for when the second handle is moved. It's pretty easy to work around, though:

const notes = ['Gb3', 'G3', 'G#3', 'Ab3', 'A3', /* ... */ 'A#5', 'Bb5', 'B5'];

function handleChange() {
  const { valueLow, valueHigh } = this;
  const lowNote = notes[parseInt(valueLow, 10)];
  const highNote = notes[parseInt(valueHigh, 10)];
  console.log('%s to %s', lowNote, highNote);
}

document.addEventListener('DOMContentLoaded', () => {
  const input = document.querySelector('input[type=range]');
  input.addEventListener('input', handleChange);
  input.nextElementSibling.addEventListener('input', handleChange.bind(input));
});
<script src="https://cdn.rawgit.com/LeaVerou/multirange/gh-pages/multirange.js"></script>
<link href="https://cdn.rawgit.com/LeaVerou/multirange/gh-pages/multirange.css" rel="stylesheet"/>

Change me: <input type="range" min="0" value="0,7" max="7" step="1" multiple>

As you can see, I've set the <input>'s min attribute to 0, max to the highest index in notes (notes.length - 1 == 7), and value to 0,7. In addition to adding an 'input' event handler to input, I've added the same handler to input.nextElementSibling. The latter is the generated "ghost" element which renders the second handle. It's necessary to bind the event handler to input because the "ghost" input doesn't have valueLow and valueHigh.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
2

I would use a javascript object. Here is an example.

HTML

<input type="range" value='0' min='0' max='3'>
<div class='sound'></div>

jQuery

var obj = {
  0 : 'Gb3',
  1 : 'G3',
  2 : 'G#3'
}

$('body').on('change', 'input[type=range]', function(){
  var val = $(this).val();
  $('.sound').text(obj[val])
});
K.G
  • 121
  • 7
  • ARE YOU KIDDING ME..... it's really THAT easy?!?!? I still want to use the slider though. like put those NOTE values into tooltips above the handles, or permanently on the ends. I assume everything I'm saying now, is just cosmetic, right? – Murphy1976 Feb 07 '17 at 21:37
  • 1
    @Murphy1976 yep, it's that easy. Only note here is I wouldn't use `$('body').on('change', 'input[type=range]', ...)` because presence of any other range input on the page will screw up the logics. – Dennis Baizulin Feb 07 '17 at 21:48
  • 1
    Yes, you can just output the object value to the tooltip text. Everytime the range input is moved, have the tooltip update. – K.G Feb 07 '17 at 21:49
  • 1
    @Dennis Yes, this was just for a quick demo! I would give the input range a name so you can be more specific! – K.G Feb 07 '17 at 21:51
1

A similar question was asked here. The solution offered is to use the jQuery UI Slider.

To answer the part of your question specific to your case, you will probably need to build the array of possible values (like you've done above: notesArr = ['Gb3', 'G3' ... 'Bb5', 'B5']; and in the ui-slider code, set the min to 0 and the max to notesArr.length - 1. In the portion of the code that defines the label, use the integer value of the slider position to index into the array to get the string value to display.

Community
  • 1
  • 1
Scott Schupbach
  • 1,284
  • 9
  • 21
1

Using jquery UI slider you can do something like this fiddle:

    $(function () {
    var note =  ['Gb3', 'G3', 'G#3', 'Ab3', 'A3', 'A#5', 'Bb5', 'B5'];
        $("#slider-range-min").slider({
            range: true,
            values: [0,7],
            min: 0,
            max: 7,          
            step: 1,
            slide: function (event, ui) {           
                $("#amount").val(note[ui.values[0]]+ "-"+note[ui.values[1]]);
            }
        });
        $("#amount").val(note[0]+ "-"+note[7]);

});
Nico
  • 6,259
  • 4
  • 24
  • 40