Silver Challenge: Showing the Value as the Slider Changes


#1

I’m wondering if I am going in the right direction for this challenge. Below is what I have so far. When I move the slider on the form I can get the value to log to console but I’m not sure where to go from here or if this is even the right approach?? Any help would be appreciated! Thank you!

Created the rangehandler.js file in the scripts directory. The rangehandler.js file is below

(function (window){
‘use strict’;
var App = window.App || {};
var $ = window.jQuery;

function RnageHandler(selector){
    if(!selector){
        throw new Error('No selector provided');
    }
    this.$formElement = $(selector);
    if(this.$formElement.length === 0){
        throw new Error('Could not find element with selector: ' + selector);
    }
}
RangeHandler.prototype.addRangeHandler = function(){
    //console.log('Setting slider handler for form');
    this.$formElement.on('change', function(event){
        event.preventDefault();
        **console.log(this.value);**
        var data = this.value;
        //console.log(data);
    });
};
App.RangeHandler = RangeHandler;
window.App = App;

})(window);

Then I updated the main.js file

(function(windows){
‘use strict’;
var FORM_SELECTOR = ‘[data-coffee-order=“form”]’;
var RANGE_SELECTOR = ‘[data-coffee-order=“strenghtRange”]’;

var App = window.App;
var Truck = App.Truck;
var DataStore = App.DataStore;
var FormHandler = App.FormHandler;
var RangeHandler = App.RangeHandler;
var myTruck = new Truck('ncc-1701', new DataStore());
window.myTruck = myTruck;
var formHandler = new FormHandler(FORM_SELECTOR);
var rangeHandler = new RangeHandler(RANGE_SELECTOR);
rangeHandler.addRangeHandler();
console.log(RangeHandler);
formHandler.addSubmitHandler(myTruck.createOrder.bind(myTruck));
console.log(formHandler);    

})(window);


#2

UPDATE: I got it working but still not sure if this is what the question was asking.

in the main.js I changed

(function(windows){
‘use strict’;
var FORM_SELECTOR = ‘[data-coffee-order=“form”]’;
var RANGE_SELECTOR = ‘[data-coffee-order=“strenghtRange”]’;
var RANGE_VALUE = ‘[data-coffee-order=“strengthValue”]’;

var App = window.App;
var Truck = App.Truck;
var DataStore = App.DataStore;
var FormHandler = App.FormHandler;
var RangeHandler = App.RangeHandler;

var myTruck = new Truck(‘ncc-1701’, new DataStore());
window.myTruck = myTruck;
var formHandler = new FormHandler(FORM_SELECTOR);
var rangeHandler = new RangeHandler(RANGE_SELECTOR);

rangeHandler.addRangeHandler(RANGE_VALUE);
console.log(RangeHandler);

formHandler.addSubmitHandler(myTruck.createOrder.bind(myTruck));
console.log(formHandler);
})(window);

And changed the rangehandler.js

RangeHandler.prototype.addRangeHandler = function(fn){
//console.log(‘Setting slider handler for form’);
this.formElement.on('change', function(event){ event.preventDefault(); //console.log(this.value); var data = this.value; **(fn).html(data);**
});
};


#3

Ok, you don’t need all that code. Here’s mu solution:

In your index.html you have your input element (where the slider is). Now, you have to add a few extra things in it for it to work:

<div class="form-group">
                        <label for="strengthLevel">Caffeine Rating</label>
                        <div class="range-slider">
                            <input type="range" name="strength" class="range-slider__range" value="50" min="0" max="100">
                            <span class="range-slider__value">50</span>
                        </div>
                    </div>

You need to add a min and max and below you add a span element where the value will display. As you see, I have it at 50.

Now in main.js outside you IIFE you create a function and work with jQuery to manipulate the html elements you’re going to work with:

var rangeSlider = function() {
  var slider = $('.range-slider');
  var range = $('.range-slider__range');
  var value = $('.range-slider__value');

  slider.each(function() {

    value.each(function() {
      var value = $(this).prev().attr('value');
      $(this).html(value);
    });

    range.on('input', function() {
      $(this).next(value).html(this.value);
    });
  });
};
rangeSlider();

This should show the numbers. I’m still trying to figure out a way of changing the colours. I thought about giving it a nice gradient effect to the bar and a background to the element but I will do that later on when I finish the entire project. Hope this works for you!Preformatted text


#4

I got a gradient background for the bar instead of the numbers as the book suggests and it looks awful but it will look better later on (i hope). Here’s what I did:
In you css file:

    input[type=range] {
  -webkit-appearance: none;
  margin: 10px 0;
  width: 100%;
}
input[type=range]:focus {
  outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
  width: 50%;
  height: 12.8px;
  cursor: pointer;
  background: #4CAF50;
  background: -webkit-linear-gradient(to left, #F44336, #4CAF50);
  background: linear-gradient(to left, #F44336, #4CAF50);
  border-radius: 25px;
  border: 0px solid #000101;
}

#5

I wanted to see if I could do the challenge without using jquery. So here is what I came up with:

I added an output element and gave ids as necessary:

<label for="strengthLevel" id="strengthLabel">Caffeine Rating</label>
<input type="range" name="strength" value="30" id="strengthLevel">
<output for="strengthLevel" id="strengthOutput">30</output>

and then at the end of the FormHandler(selector) function:

  // update strengthOutput when slider changes and add color
    var slider = document.getElementById("strengthLevel");
    var sliderOutput = document.getElementById("strengthOutput");
    var sliderLabel = document.getElementById('strengthLabel');

    // set initial color
    sliderOutput.style.color = "green";
    sliderLabel.style.color = "green";

    slider.addEventListener("input", function() { // using change only shows
                                                  // number after letting go of slider
      // both value and textContent seem to work
      // can't find any discussion online about which to use
      // sliderOutput.textContent = slider.value;
      sliderOutput.value = slider.value
      // change the color of the label and number based on intensity
      var intensityColor;
      if (slider.value < 34) {
        intensityColor = "green";
      }
      else if (slider.value < 68) {
        intensityColor = "#aaaa00";
      }
      else {
        intensityColor = "red";
      }
      sliderOutput.style.color = intensityColor;
      sliderLabel.style.color = intensityColor;
    });

As pointed out in the comments, I am not sure which is more appropriate to use between value and textContent (or even something else). I am also using getElementById and ids as oppose to data- attributes and querySelector. One of the hardest part about javascript so far is understanding which method to use when several options seem to get the job done. I do find some good help from discussions online, but not always.


#6

Does resetting the form work properly for you? In my case only the slider itself is reset, but the color and value of the span remain untouched until you start moving the slider again.


#7

Hi @ballgeier, MDN uses .value so I’d go with that since you’re using the new <output> tag.

Your approach works great! Here are some things you could do to improve it a bit: first, instead of updating the look with inline styles (sliderOutput.style.color), use the .classList API to update a class, then add some CSS to style the particular class. For example:

      var intensity = slider.value;
      var intensityClass;
      if (intensity < 34) {
        intensityClass = "low";
      }
      else if (intensity < 68) {
        intensityClass = "medium";
      }
      else {
        intensityClass = "high";
      }
      sliderOutput.classList.remove('low', 'medium', 'high');
      sliderOutput.classList.add(intensityClass);
#strengthOutput.high {
  color: red;
}

/* ... */

This will help separate your logic from the styling.


#8

Hello. I also have some doubts. I sense that the way I solved it won’t be reusable. Hope you can bring some light for me to this problem.

This is my html:

...
            <div class="form-group">
              <label for="strengthLevel">Caffeine Ratings</label>
              <input type="range" name="strength" id="strengthLevel" value="30">
              <span id="strenghtLevelNumber">30</span>
            </div>
...

I added a span element and set an id attribute to it in this line <span id="strenghtLevelNumber">30</span>.

Then I added a handler to the slider in the formhandler.js file:

...
};

FormHandler.prototype.addSliderHandler = function () {
  this.$formElement.on('input', function () {
    var caffeineRate = $(this).val();
    $(this).next().html(caffeineRate);
  });
};

  App.FormHandler = FormHandler;
...

What I did here was to add a listener to the input attribute and return the numerical value of the slider. I tested this on the console like this:

var fh = new App.FormHandler('[id="strengthLevel"]');
fh.addSliderHandler();

When I moved the slider the code works, but the problem that I have is that the $(this).next()method doesn’t seem quite well to make the code reusable. Plus, when I submit or reset the form, the value that was sent to the <span> element remains with the new value and won’t go back to 30. I solve it by adding a line to the addSubmitHandler function after submitting the form:

...
    this.reset();
    $('#strenghtLevelNumber').html(30);
    this.elements[0].focus();
...

This solution also doesn’t seem to be reusable. It actually seems like a bad practice and the same problem is happening with the reset button (I haven’t solved that yet). What would be a better approach?

Thanks in advance :slight_smile:


#9

Hi @skrial, way to go taking the challenge for a spin! Here are some ideas to tidy up your solution:

First, instead of hardwiring the behavior for when the slider changes, your addSliderHandler function could take in a callback. e.g.

FormHandler.prototype.addSliderHandler = function (callback) {
  this.$formElement.on('input', function () {
    var caffeineRate = $(this).val();
    callback(caffeineRate);
  });
};

Then you could pass in that callback like this:

var fh = new App.FormHandler('[id="strengthLevel"]');
fh.addSliderHandler(function(caffeineRate) {
  // do the caffeine dance
});

A couple more tips that may help: right now, the $(this).next().html(caffeineRate); line is a bit ill performant and has a security issue. First off, you could calculate $(this).next() once (outside the event handler) and put it in a local variable. Next, when you use the .html() helper, you are setting the innerHTML property of that node. So if the caffeineRate happened to be:

var caffeineRate = "<script>alert('muahaha')</script>"

…much chaos would ensue. So instead, use the .text() helper which will do what you want and not allow raw HTML to be injected :slight_smile:

Hope those help!


#10

Thank you very much @nybblr! it worked better!


#12

I am unable to reset the value of the slider. Can anybody help me with that? The value is stored in a span element.