2

I'm totally learning here! I'm still playing with JS.

When checkbox is altered I want the range slider to be inactive AND the output to be zero.

function outputUpdate(obj) {
  obj.previousElementSibling.value = obj.value;
}

function handleClick(obj) {
  obj.previousElementSibling.value = obj.value;
}
<fieldset>
  <legend><h3>Engine</h3></legend>
  <output for="engine" id="engine">0</output>
  <input id="engine" type="range" min="0" max="10" value="0" step="1" list="0-10" oninput="outputUpdate(this)">
  <input type="checkbox" name="engine-closed" id="engine-closed" onclick='handleClick(this);' value="0" /><label for="engine-closed">Closed</label>
</fieldset>

I'm not sure how to make the range slider inactive / invisible on checkbox. Then when unchecked make slider active / visible again.

Further, how to make the output zero when checkbox changes?

So far, with above code, slider works great and it updates output. When checkbox checked the slider goes to zero as wanted, but not output.

edit: there are about 20 different form sliders with outputs. So it would be nice to use the id that called the function rather than declaring a var...

2
  • You can use onchange event in the checkbox to know when the state changes, not only onclick, so you can know when the checkbox is checked or not. Commented Jun 16, 2016 at 15:32
  • you can use .innerHTML to update output text. Commented Jun 16, 2016 at 15:34

4 Answers 4

3

Remove all the inline javascript and use addEventListener instead.
Then you can just get the elements, bind the events in a loop, and reference the other elements by using this and getting the siblings etc.

var ranges = document.querySelectorAll('fieldset input[type=range]');
var boxes  = document.querySelectorAll('fieldset input[type=checkbox]');

[].slice.call(ranges).forEach(function(range) {
  range.addEventListener('input', function() {
    this.previousElementSibling.value = this.value;
  });
});

[].slice.call(boxes).forEach(function(box) {
  box.addEventListener('change', function() {
    var range = this.previousElementSibling;
    var event = new Event('input');

    range.value = 0;
    range.dispatchEvent(event);
    range.disabled = this.checked;
  });
});
<fieldset>
  <legend><h3>Engine</h3></legend>
  <output for="engine" id="engine">0</output>
  <input id="engine" type="range" min="0" max="10" value="0" step="1" list="0-10">
  <input type="checkbox" name="engine-closed" id="engine-closed" value="0" />
  <label for="engine-closed">Closed</label>
</fieldset>

Sign up to request clarification or add additional context in comments.

3 Comments

Lovely succinct code. I've learned something from you today. Thanks ;)
I was thinking how cool this [].slice.call(NodeList) method is and then I found this: toddmotto.com/ditch-the-array-foreach-call-nodelist-hack
@Moob - and that's one guys opinion, all though he is correct in that [].slice.call and forEach isn't cross browser, but that only holds true if one needs to support IE8 or lower, and noone does that anymore anyway.
2

Here, with minimal changes to your code :)

function outputUpdate(obj) {
  obj.previousElementSibling.value = obj.value;
}

function handleClick(obj) {
  obj.previousElementSibling.value = obj.value;
  obj.previousElementSibling.disabled = obj.checked;
}
<fieldset>
  <legend>
    <h3>Engine</h3>
  </legend>
  <output for="engine" id="engine">0</output>
  <input id="engine" type="range" min="0" max="10" value="0" step="1" list="0-10" oninput="outputUpdate(this)">
  <input type="checkbox" name="engine-closed" id="engine-closed" onclick='handleClick(this); outputUpdate(this.previousElementSibling);' value="0" />
  <label for="engine-closed">Closed</label>
</fieldset>

You can add the visibility toggle part if you want to.

4 Comments

That worked awesome! I want the slider AND output display="none" / display="block" based on checked not checked. I don't exactly understand '? true : false' in your code. I think i can use it to add if statement.
Glad it helped! And that "? :" is a conditional (ternary) operator. Works like if else statement, but shorter and cleaner :)
after reading about ternary operators i was able to add this: obj.previousElementSibling.style.display = (obj.checked) ? "none" : "block";
As a sidenote, the ternary statement isn't needed at all, as obj.checked will already be either true or false.
2

If you're happy to put your checkbox before the range slider you can do this very simply with just CSS by using the Adjacent Sibling Selector and the :checked pseudo class to target sibling elements after a checked input:

function outputUpdate(obj) {
  obj.previousElementSibling.value = obj.value;
}

function handleClick(obj) {
  obj.previousElementSibling.value = obj.value;
}
#engine-closed:checked ~ #engine {display:none;}
<fieldset>
  <legend><h3>Engine</h3></legend>
  <input type="checkbox" name="engine-closed" id="engine-closed" onclick='handleClick(this);' value="0" />
  <output for="engine" id="engine">0</output>
  <input id="engine" type="range" min="0" max="10" value="0" step="1" list="0-10" oninput="outputUpdate(this)">
  <label for="engine-closed">Closed</label>
</fieldset>

If you want a JS solution then simply toggle the elements visibility when the checkbox is set like so: (UPDATED to cope with multiple instances)

(function(){
  function initWierdCloseRangeThingThatMakesNoSense(el){
      var engine = el.querySelector("input[type='range']"),
      output = el.querySelector("output"),
      engineClosed = el.querySelector("input[type='checkbox']"),
      setOutput = function(){
        output.value = engine.value;
      },
      openClose = function(){
        engine.style.display = (this.checked)?"none":"";
        engine.value = "0";
        setOutput();
      };    
  engine.addEventListener("change", setOutput, false);
  engineClosed.addEventListener("change", openClose, false);
  };

  
// for every fieldset:
  var fieldsets = document.querySelectorAll("fieldset");
  
  for (i=0; i<fieldsets.length; ++i){
    var fs = fieldsets[i];
    initWierdCloseRangeThingThatMakesNoSense(fs);
  }
   

})();
<fieldset>
  <legend><h3>Engine</h3></legend>  
  <output for="engine" id="output">0</output>
  <input id="engine" type="range" min="0" max="10" value="0" step="1" list="0-10">
  <input type="checkbox" name="engine-closed" id="engine-closed" value="0" />
  <label for="engine-closed">Closed</label>
</fieldset>
<fieldset>
  <legend><h3>Engine 2</h3></legend>  
  <output for="engine2">0</output>
  <input type="range" min="0" max="10" value="0" step="1" list="0-10">
  <input type="checkbox" name="engine-2-closed" value="0" />
  <label for="engine-3-closed">Closed</label>
</fieldset>
<fieldset>
  <legend><h3>Engine 3</h3></legend>  
  <output for="engine3">0</output>
  <input type="range" min="0" max="10" value="0" step="1" list="0-10">
  <input type="checkbox" name="engine-3-closed" value="0" />
  <label for="engine-3-closed">Closed</label>
</fieldset>

Better still, if you're going for a JS solution use @adeneo's because it's superior ;)

Comments

0

Essentially this:

function outputUpdate(obj) {
  obj.previousElementSibling.value = obj.value;
}

function handleClick(obj) {
  // grab the slider element (a.k.a. engine)
  var engine = obj.previousElementSibling;
  // grab the result element (a.k.a. output)
  var output = engine.previousElementSibling;
  // grab the checkbox's name
  var checkBoxName = obj.name;
  console.log('Name:', checkBoxName)
  // these are both grabbed relative to the
  // the checkbox, so you can have as many
  // of these fieldsets as you want, and
  // they will not interfere with each other.
  
  // set the out to zero
  output.value = 0
 
  if (obj.checked) { // "closed" is checked
    engine.disabled = true; // hide the slider
  } else {
    engine.disabled = false; // otherwise, show it
  }
  obj.previousElementSibling.value = obj.value;
}
<fieldset>
  <legend><h3>Engine</h3></legend>
  <output for="engine">0</output>
  <input type="range" min="0" max="10" value="0" step="1" list="0-10" oninput="outputUpdate(this)">
  <input type="checkbox" name="engine-closed" onclick='handleClick(this);' value="0" /> <label for="engine-closed">Closed</label>
</fieldset>

<fieldset>
  <legend><h3>Dash / Console</h3></legend>
  <output for="dask-console" id="dask-console">0</output>
  <input id="dash-console" type="range" min="0" max="10" value="0" step="1" list="0-10" oninput="outputUpdate(this)">
  <input type="checkbox" name="dash-console-closed" onclick='handleClick(this);' value="0"><label for="dash-console-closed">Closed</label>
</fieldset>

8 Comments

i should have also said that there are about 20 different form sliders with outputs. So it would be nice to use the id that called the function rather than declaring a var...
See my updated post, it should do everything you want. Including 20 different form sliders :) The explanation is in the code, let me know if you want me to elaborate further.
Btw, some of the other answers tell you to get rid of the inline functions, there is no need for that. If all you are doing is a form implementation, the approach I depicted above is simple, and to the point. Move on to more complex event handling when you feel the need to.
agreed: one step at a time... I will look at the other answers next.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.