0

There is an number input with min of '2' and max of '10'. By default this input is disabled. Nearby checkbox if checked toggles 'disabled' attribute so input become enabled and sets '2' as a default value. When checkbox is not checked it resets the value of input so you can see a placeholder text insted of the number. The problem is how to restore the last entered value of input before its deactivation?

For example:

  1. Checkbox is active: input becomes enabled an sets '2' as a default value.
  2. I change its value to '7'.
  3. I deactivate checkbox, so input becomes disabled and its value resets to ''.
  4. I activate checkbox again and wait for input to display early entered '7', but see default '2' instead.

Wrote the following code. I don't know how to get rid of passQuantityInput.value = '2' after I called it for the first time.

const Input = document.querySelector('#input');
const Checkbox = document.querySelector('#box');

let inputValue = Input.value;

Checkbox.addEventListener('click',function(){
    if (this.checked){
        Input.toggleAttribute('disabled');
        Input.value = '2';
        Input.value = getValue(); //this line returns "The specified value "NaN" cannot be parsed, or is out of range."
    }
    if (!this.checked){
        Input.toggleAttribute('disabled');
        Input.value = '';
    }
})

Input.addEventListener('input',getValue);

function getValue(){
    let inputValue = this.value;
    if(this.value<2){
        this.value = '';
    } else if (this.value>10){
        this.value = '10';
    }
    return inputValue;
}
danronmoon
  • 3,814
  • 5
  • 34
  • 56
coldheart
  • 3
  • 2
  • 2
    What do you imagine `this` to be in `getValue()`? – pilchard Jul 01 '23 at 11:28
  • Does this answer your question? [How does the "this" keyword work, and when should it be used?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work-and-when-should-it-be-used) – pilchard Jul 01 '23 at 11:30
  • @pilchard Oops. This function originally was inside of `Input.addEventListener('input',getValue)` so `this` was an input. – coldheart Jul 01 '23 at 11:37
  • If you want to retrieve the last entered value you can make use of local storage – CodeThing Jul 01 '23 at 11:37

5 Answers5

1

There is such a thing as HTMLInputElement.defaultValue that you can use here

const Input = document.querySelector('#input');
const Checkbox = document.querySelector('#box');

Checkbox.addEventListener('click', function() {
    Input.disabled = !this.checked;
    if (Input.disabled)  Input.value = '';
    else Input.value = Input.defaultValue || 2;
})

Input.addEventListener('input', getValue);

function getValue() {
  let inputValue = this.value;
  if (this.value < 2) {
    this.value = '';
  } else if (this.value > 10) {
    this.value = '10';
  }
  this.defaultValue = this.value;
}
<input type="number" min="2" max="10" id="input" value="" disabled /> <input type="checkbox" id="box" />
mplungjan
  • 169,008
  • 28
  • 173
  • 236
0

It maybe simpler to use a data attribute to store the current input value. That way you don't have to rely on global variables.

Set a current attribute on the input element, and assign it an initial value. Any changes to the input value are stored on the data attribute, when the checkbox is toggled it retrieves the current value from the input element's dataset / sets the value to an empty string.

// Cache the elements
const text = document.querySelector('input[type="number"]');
const box = document.querySelector('input[type="checkbox"]');

// Add the listeners
text.addEventListener('input', handleInput);
box.addEventListener('click', handleClick);

// If the input value changes store that in the current
// data attribute
function handleInput(e) {
  e.target.dataset.current = e.target.value;
}

// If the box is clicked
function handleClick() {

  // Enable/disable the input element
  text.disabled = !this.checked;
  
  // If the input is disabled set its value to
  // an empty string, otherwise set its value to the
  // value from its data attribute
  text.value = text.disabled ? '' : text.dataset.current;
}
<input
  data-current="2"
  type="number"
  min="2"
  max="10"
  placeholder="Add some text"
  disabled
/>
<input type="checkbox" />
Andy
  • 61,948
  • 13
  • 68
  • 95
  • The HTMLInputElement has an attribute called defaultValue that is more logical to use than a data attribute in this case. [See my answer](https://stackoverflow.com/a/76594921/295783) – mplungjan Jul 01 '23 at 13:39
  • "Default" implies it's a static value though @mplungjan which is why I didn't use it for a value that keeps changing. – Andy Jul 01 '23 at 13:58
  • 1
    I like it exactly because it is a value that will be default whenever it toggles from disabled to enabled – mplungjan Jul 01 '23 at 14:01
0

A <input type="hidden"> to store a new value is simple and effective. In the example below there are three sets of <input> -- each consisting of [type="checkbox"], [type="number"], and [type="hidden"]. It's always better to plan your page for future expansion if you haven't done so already. That means using a <form> and handle multiple form controls.

Wrap your form controls (ex. <input>s) in a <form> and then register the "input" and "change" events to it. If you arrange a layout like that, you can control an unlimited number of form controls without adding extra listeners (even if the form controls were dynamically added after the page loaded, (see event delegation).

Details are commented in example

// Reference <form id="UI">
const ui = document.forms.UI;
/**
 * Register #UI to the "change" event, call change handler 
 * userInterface() when a "change" event triggers on #UI or it's
 * form controls (ex. <input>, <fieldset>, etc).
 */
ui.onchange = userInterface;

// Event handlers always passes the Event Object by default
function userInterface(event) {
  /**
   * Reference the element that the user is interacting with
   * (ex. clicked <button>, an <input> the user typed into, etc)
   */
  const point = event.target;
  // Declare variables used later on
  let input, cache;
  // If the element the user interacted with has the [name="switch"]...
  if (point.name === "switch") {
    // ...reference the <input type="number"> that proceeds the checkbox...
    input = point.nextElementSibling;
    // ...reference the <input type="hidden"> that proceeds <input type="number">...
    cache = input.nextElementSibling;
    // ...toggle the [disabled] attribute on <input type="number">
    input.toggleAttribute("disabled");
    // If the checkbox is checked...
    if (point.checked) {
      // ...assign the value of <input type="number"> to <input type="hidden">
      input.value = cache.value;
    }
  }
  // If the interacted element is <input type="number">...
  if (point.name === "number") {
    /**
     * ...constrain the .value of <input type="number">
     * input value is below .min -- return .min,
     *                above .max -- return .max,
     *                between .min and .max -- return value
     */
    point.value = +point.value < +point.min ? point.min :
      +point.value > +point.max ? point.max :
      point.value;
    // ...remove class "error" from parent <label>
    point.parentElement.classList.remove("error");
    // Reference the proceeding <input type="hidden">
    cache = point.nextElementSibling;
    // Assign value of <input type="number"> to <input type="hidden">
    cache.value = point.value;
  }
}

// Register "input" event to #UI
ui.oninput = limitHint;

function limitHint(event) {
  const point = event.target;
  // Reference the <label> that contains <input type="number">
  const line = point.parentElement;
  // If <input name="number">...
  if (point.name === "number") {
    // ...and if it's value is lower than .min OR higher than .max...
    if (+point.value < +point.min || +point.value > +point.max) {
      // ...add class: "error"...
      line.classList.add("error");
      // ...otherwise...
    } else {
      // ...remove class: "error"
      line.classList.remove("error");
    }
  }

  /**
   * This is an immediately invoked function expression (iife)
   * It will run first and only once. It basically interpolates 
   * min/max values into an error message and then adds it to 
   * each <label> as the [data-error] value.
   * ex. <label data-error="Enter a number within\nthe range of -10 and 10.">
   */
  (() => {
    Array.from(ui.elements.number).forEach(input => {
      let hint = `Enter a number within\nthe range of ${input.min} and ${input.max}.`;
      input.parentElement.dataset.error = hint;
    });
  })();
:root {
  font: 2.25ch/1.15 "Segoe UI"
}

fieldset {
  width: max-content;
}

legend {
  font-size: 1.1rem;
  letter-spacing: 1.5px;
}

label {
  display: flex;
  align-items: center;
  position: relative;
  width: 100%;
  margin: 0.25rem 0 0;
}

label+label {
  margin-top: 0.5rem;
}

.error input {
  color: red;
  outline: 3px solid red;
}

.error::after {
  content: attr(data-error);
  display: block;
  position: absolute;
  z-index: 1;
  left: 75%;
  top: -5px;
  min-width: 18ch;
  max-height: 8ex;
  padding: 4px 8px;
  font-size: 0.75rem;
  white-space: pre;
  color: red;
  background: rgb(240, 234, 214);
  box-shadow: rgba(0, 0, 0, 0.09) 0px 2px 1px, rgba(0, 0, 0, 0.09) 0px 4px 2px, rgba(0, 0, 0, 0.09) 0px 8px 4px, rgba(0, 0, 0, 0.09) 0px 16px 8px, rgba(0, 0, 0, 0.09) 0px 32px 16px;
}

input {
  font: inherit;
  font-family: Consolas;
}

[name="switch"] {
  width: 1.95ch;
  height: 1.95ch;
}

[name="number"] {
  width: 10ch;
  margin-left: 1rem;
  text-align: center;
}
<form id="UI">
  <fieldset>
    <legend><code>[disabled]</code> Attribute</legend>
    <label>
      <input name="switch" type="checkbox">
      <input name="number" type="number" min="2" max="10" placeholder="2 - 10" disabled>
      <input type="hidden">
    </label>
    <label>
      <input name="switch" type="checkbox">
      <input name="number" type="number" min="0" max="40" placeholder="0 - 40" disabled>
      <input type="hidden">
    </label>
    <label>
      <input name="switch" type="checkbox">
      <input name="number" type="number" min="-10" max="10" placeholder="-10 - 10" disabled>
      <input type="hidden">
    </label>
  </fieldset>
</form>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
-1

Try changing your function getValue to this:

function getValue(){
    if(inputValue<2){
        Input.value = '';
    } else if (inputValue>10){
        Input.value = '10';
    } else {
        Input.value = inputValue;
    }
    return Input.value;
}

Change the Checkbox.addEventListener part to this

Checkbox.addEventListener('click',function(){
    if (this.checked){
        Input.toggleAttribute('disabled');
        Input.value = '2';
        Input.value = getValue(); //this line returns "The specified value "NaN" cannot be parsed, or is out of range."
    }
    if (!this.checked){
        Input.toggleAttribute('disabled');
        inputValue = Input.value;
        Input.value = '';
    }
})

So the value is saved in inputValue before the shown value is set to ''.

kzi
  • 186
  • 1
  • 8
-2

To fix this issue you can define a variable outside of function or Event listener and the value will be 2. suppose, we named the variable "defaultValue" When you get the value from input, you can update the value of defaultValue and every time your event listener enabled set the value with defaultValue.

const Input = document.querySelector('#input');
const Checkbox = document.querySelector('#box');

let inputValue = Input.value;
let defaultValue = 2 //define the default value
Checkbox.addEventListener('click',function(){
    if (this.checked){
        Input.toggleAttribute('disabled');
        Input.value = defaultValue; //add the defaultValue as a global variable
        Input.value = getValue(); //this line returns "The specified value "NaN" cannot be parsed, or is out of range."
    }
    if (!this.checked){
        Input.toggleAttribute('disabled');
        Input.value = '';
    }
})

Input.addEventListener('input',getValue);

function getValue(){
    let inputValue = this.value;
    if(this.value<2){
        this.value = '';
    } else if (this.value>10){
        this.value = '10';
    }
    
    defaultValue = inputValue //upgrade the value of defaultValue
    return inputValue;
}