6

Here's a sample form:

var form = document.querySelector('form');

function detectChange() {
  var inputs = form.querySelectorAll('input');
  for (var input of inputs) {
    if (input.value != input.defaultValue) {
      return true;
    }
  }
}

form.querySelector('button').addEventListener('click', function() {
  if (detectChange() && confirm('Are you sure you want to reset?')) {
    form.reset();
  }
});
<form>
  <input type="number">
  <input type="number" value="7">
  <button type="button">Reset</button>
</form>

I'd like the reset button to work even if the user enters non-numeric values.

Horai Nuri
  • 5,358
  • 16
  • 75
  • 127
Mori
  • 8,137
  • 19
  • 63
  • 91

8 Answers8

9

If you look at the input DOM object, there is a property badInput under validity object whose value is a boolean. For numeric entry or empty field, it's false. However it's true for non numeric values, which can interestingly be used in your case.

Note: Tested only on firefox and safari

input
|  +-- ...
|  +-- validity
|  |   +-- badInput
|  |   +-- ...
|  +-- ...

Using this knowledge you can modify the function to check for badInput to achieve what you want with minimal tweaking.

// non-empty and non default
if ((input.value && input.value != input.defaultValue) || input.validity.badInput)

var form = document.querySelector('form');

function detectChange() {
  var inputs = form.querySelectorAll('input');
  for (var input of inputs) {
    if ((input.value && input.value != input.defaultValue) || input.validity.badInput) {
      return true;
    }
  }
}

form.querySelector('button').addEventListener('click', function() {
  if (detectChange() && confirm('Are you sure you want to reset?')) {
    form.reset();
  }
});
<form>
  <input type="number">
  <input type="number" value="11">
  <button type="button">Reset</button>
</form>

Update:

update to cover:

inputs with non-empty default values

1565986223
  • 6,420
  • 2
  • 20
  • 33
  • seems to work fine on firefox, could you elaborate? – 1565986223 Apr 13 '19 at 12:17
  • 1
    I was wrong, `input.value` will return `"0"` (a string) and not `0` which would evaluate to `false`. – connexo Apr 13 '19 at 12:22
  • yes, which i would say is a *little strange* given the input `type="number"` – 1565986223 Apr 13 '19 at 12:24
  • 1
    Any `input` regardless of type will always only hold `string` values. – connexo Apr 13 '19 at 12:25
  • 1
    "_The HTMLFormElement.reset() method restores a form element's default values._" The default value isn't always an empty string. Your answer is already perfect. All you need to generalize your snippet to cover inputs with non-empty default values is the following: `if (input.value != input.defaultValue || input.validity.badInput)`: [DEMO](https://jsfiddle.net/Mori/Lpzdrsk1/6/) – Mori Apr 14 '19 at 04:55
  • Updated my question to prevent the contusion. – Mori Apr 14 '19 at 05:18
2

One alternative to approach your goal is using a set of standard pseudo-classes, like :invalid and :valid. Also note, we are going to use some array methods and features, like Array.some() and the Spread Syntax:

var form = document.querySelector('form');

function detectChange()
{
    var invalids = form.querySelectorAll('input:invalid');
    var valids = form.querySelectorAll('input:valid');
    return (invalids.length > 0) || [...valids].some(i => i.defaultValue !== i.value);
}

form.querySelector('button').addEventListener('click', function()
{
    if (detectChange() && confirm('Are you sure you want to reset?'))
        form.reset();
});
<form>
  <input type="number">
  <input type="number">
  <input type="number" value="7">
  <button type="button">Reset</button>
</form>

If you are able to use placeholders on your inputs, then another possible solution is to use the CSS pseudo-class :placeholder-shown. However, give a check to the browser's support to be sure it will fit your need. Note it is experimental and is not recommended to use on production. Then, you can use the next selector:

input:not(:placeholder-shown)

to get all inputs where the placeholder is not shown, i.e, all not-empty inputs, and re-implement your code something like this:

var form = document.querySelector('form');

function detectChange()
{
    var inputs = form.querySelectorAll(
        'input:not([value]):not(:placeholder-shown), input[value]'
    );
    return [...inputs].some(i => !i.defaultValue || i.defaultValue !== i.value);
}

form.querySelector('button').addEventListener('click', function()
{
    if (detectChange() && confirm('Are you sure you want to reset?'))
        form.reset();
});
<form>
  <input type="number" placeholder="Insert a number">
  <input type="number" placeholder="Insert a number">
  <input type="number" value="7" placeholder="Insert a number">
  <button type="button">Reset</button>
</form>
Shidersz
  • 16,846
  • 2
  • 23
  • 48
  • 1
    "_we are going to use some array methods and features, like Array.some() and the Spread Syntax_" Do we really need to? Why not simply: `if (input.value != input.defaultValue || form.querySelectorAll('input:invalid').length)`: [DEMO](https://jsfiddle.net/t9ndo4ab/) –  Apr 16 '19 at 02:54
  • @Mike No, you don't need it, you have demonstrate it yourself. However I feel comfortable using they. – Shidersz Apr 16 '19 at 03:35
1

Change this:

<button type="button">Reset</button>

TO

<input type="reset" value="Reset"/>
Amy
  • 4,034
  • 1
  • 20
  • 34
  • "You should usually avoid including reset buttons in your forms. They're rarely useful, and are instead more likely to frustrate users who click them by mistake (often while trying to click the submit button)." [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/reset) – Mori Apr 10 '19 at 08:52
  • @Mori but the notice does not refer to the input `type=reset` specifically. It refers to the reset buttons in general. No matter how are you going to do that it's still a reset button which is *"rarely useful, and is instead more likely to frustrate users who click it by mistake"* – Sebastian Kaczmarek Apr 12 '19 at 07:51
  • 1
    @SebastianKaczmarek: The confirm dialog box prevents an unwanted reset of the form. – Mori Apr 12 '19 at 07:53
0

Loop over the input fields and manually set values = "";

var form = document.querySelector('form');

function detectChange() {
  var inputs = form.querySelectorAll('input');
  for (var input of inputs) {
    input.value = "";
  }
}

form.querySelector('button').addEventListener('click', function() {
  if (confirm('Are you sure you want to reset?')) {
    detectChange(); 
  }
});
<form>
  <input type="number">
  <input type="number">
  <button type="button">Reset</button>
</form>
Syed Mehtab Hassan
  • 1,297
  • 1
  • 9
  • 23
0

Just set an event listener for onkeydown, onpaste of the input element:

const inputs = document.querySelectorAll('input[type=number]')
inputs.forEach((el) => el.onkeydown = el.onpaste = (event) => event.target.dataset.receivedEntry = 1)

Then check for the input.dataset.receivedEntry in detectChange:

function detectChange () {
  const inputs = form.querySelectorAll('input');
  for (var input of inputs) {
    if (input.dataset.receivedEntry) {
      return true;
    }
  }
}
webgodo
  • 81
  • 8
  • @MarkSchultheiss just add the event listener to `onpaste` event of the input. (UPDATED THE ANSWER) – webgodo Apr 13 '19 at 07:58
0

Simply use a button type="reset", and attach an event listener on the form that listens for reset events.

The main problem with your code is that your detectChange() function will never work for invalid input because browsers will always return the empty string for any non-numeric input if you ask for the value. This is by the specification. Check https://stackoverflow.com/a/18853513/3744304

The solution to fix this has been suggested originally in @naga - elixir - jar's answer. Edited my answer a) to make it working answer and b) to put it all together in a concise, ES6 way.

const form = document.querySelector('form');

form.addEventListener('reset', (event) => {
  if (detectChange() && !confirm('Are you sure you want to reset?')) {
    event.preventDefault();
  }
});

function detectChange() { 
  return [...form.querySelectorAll('input')].some(el => el.validity.badInput || el.value !== el.defaultValue); 
};
<form>
  <input type="number" value="87">
  <input type="number">
  <button type="reset">Reset</button>
</form>
connexo
  • 53,704
  • 14
  • 91
  • 128
  • This does not work if I enter an invalid number such as "e23e333" – Mark Schultheiss Apr 13 '19 at 06:32
  • @MarkSchultheiss Added an explanation (and workaround) for that. – connexo Apr 13 '19 at 06:46
  • 1
    A side note: if you write something on one input and later you use `backspace` to delete all the characters, the confirm dialog will still triggers. However, I'm not sure if the OP wants this case to trigger the confirm dialog or not (since, after all, there was some changes on the inputs). – Shidersz Apr 13 '19 at 15:54
  • @Shidersz: "_I'm not sure if the OP wants this case to trigger the confirm dialog or not_" The confirm dialog box should appear only when there’s a value — preferably a real value: number. – Mori Apr 14 '19 at 05:36
  • @Mori So it's desired that the confirm dialog does **not** appear in case the form only has either empty or invalid values? – connexo Apr 14 '19 at 06:59
  • Yes, that's OK. – Mori Apr 14 '19 at 09:31
  • Not really: as the question title suggests I'd like the reset button to clear invalid values as well. I don't really care if the confirm doesn't occur for invalid values. – Mori Apr 15 '19 at 02:48
-1

var form = document.querySelector('form');
var btn = form.querySelector('button');
var inputs = form.querySelectorAll('input');
btn.disabled = true;
btn.addEventListener('click', function() {
  if (confirm('Are you sure you want to reset?')) {
    form.reset();
    btn.disabled = true;
  }
});

for (var input of inputs) {
  input.addEventListener('keypress', detectSpace)
  input.addEventListener('input', handleChange);
}

function handleChange(e) {
  var disabled = true;
  for (var input of inputs) {
    input.value = input.value.trim()
    if (input.value) {
      disabled = false
    }
  }
  btn.disabled = disabled
}

function detectSpace(e) {
  if (e.keyCode == 32) {
    e.preventDefault();
    return false
  }
}
<form>
  <input type="number">
  <input type="number">
  <button type="button">Reset</button>
</form>

will this work? it prevents user from pasting into number by trimming the value inside input type="number".

detectSpace is there because in firefox hitting space will delete the number because is not a valid one.

fila90
  • 1,439
  • 11
  • 11
-1
<form>
  <input type="number">
  <input type="number">
  <button type="button" onclick="resetData()">Reset</button>
</form>


function resetData(){
    var y = document.querySelectorAll('input,textarea');
        for (var i= 0; i < y.length; i++) {
        var id =y[i].id;
        if(y[i].value !=''&&y[i].value!= 'undefined'){
        document.getElementById(id).value="";
        }
        } 
}