-1

I have a form with several input fields.

If one of the inputs of the form was changed, I would like to toggle the css class of the form, so that I can display a text like "Dear user, please submit the form now".

Optional, but would be nice: if the user changes the input field to the initial value again, then the class should be toggled back again, so that I can hide the text (via css)

I don't need to support IE, just modern browsers.

CSS for the text could look like this:

form.unchanged div.submit-text {
    display: hidden;
}

form.changed div.submit-text {
    display: block;
}

I could do it like this:

function initChangeDetection(form) {
  Array.from(form).forEach(el => el.dataset.origValue = el.value);
}
function formHasChanges(form) {
  return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value);
}

Source: https://stackoverflow.com/a/52593167/633961

But this creates too much load at the client.

guettli
  • 25,042
  • 81
  • 346
  • 663
  • Add a `change` event listener to the form, it can toggle the `unchanged` and `changed` classes. – Barmar Jun 22 '21 at 20:46
  • what kind of elements are in your form? it is variable or can you be definite about the types of form elements? – Kinglish Jun 22 '21 at 21:04

1 Answers1

2

FormData Snapshot

With a suggestion from @Terry, I did this 2 ways, the first I like the best. It takes a snapshot of the original form via FormData, stores it in JSON and is used to check all elements in the DB when the form changes

let oform = {}
window.onload = function() {
  let form = document.querySelector('#post'), formData = new FormData(form)
  formData.forEach((value, key) => {
    oform[key] = value;
  });
  document.querySelector('form').addEventListener('change', event => {
    let changed = Object.entries(oform).filter(el => el[1] !== document.querySelector('[name=' + [el[0]] + ']').value).length > 0,
      f = event.target.closest('form')
    if (changed) f.classList.add('changed');
    else f.classList.remove('changed');
  })
}
form{
padding:10px;
border:1px dashed #ccc;
border-radius:8px;
}

form.changed {
  background: #f0f0f0;
border:1px solid #f00;
}
label{
margin:5px 0 2px 0;
display:block
}
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600' rel='stylesheet' type='text/css'>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet">

<div class="main-block">
  <form action="/" id='post'>
    <div><label>Email*</label><input type="text" name="email" value="john@doe.com" required></div>
    <div><label>Password*</label><input type="password" value="123456" name="password" required></div>
    <label>Greetings</label>
    <select name='stuff'>
      <option></option>
      <option value='hello' selected>hello</option>
    </select>
    <button type="submit" href="/">Submit</button>
  </form>
</div>

Data Attributes

This way loops through the elements on page load and store their original values in data attributes. Then every time the form.change is triggered, loop through the elements to test if their values match the originals. This is imperfect and only covers selects, text/email/number/time/date inputs, checkboxes and radio buttons

window.onload = function() {
  document.querySelectorAll('input, select').forEach(e => {
    if (['checkbox', 'radio'].includes(e.getAttribute('type').toLowerCase())) {
      if (e.getAttribute('checked')) e.setAttribute('data-orig', 'checked');
    } else e.setAttribute('data-orig', e.value);
  })

  document.querySelector('form').addEventListener('change', (event => {
      let changed = false
      event.target.querySelectorAll('input, select').forEach(e =>
        if (['checkbox', 'radio'].includes(e.target.getAttribute('type').toLowerCase()) && e.target.getAttribute('checked') && !e.target.dataset.orig) changed = true
        else if (e.target.value !== e.target.dataset.orig) changed = true;
      });
      if (changed) event.target.classList.add('changed');
      else event.target.classList.remove('changed');
  })
}
Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • Instead of doing through all possible input elements, what about simply serializing the form data to JSON, store/cache it and then compare whenever `input` events are triggered? – Terry Jun 23 '21 at 07:25
  • @terry - thanks for the suggestion. I added it to my answer. – Kinglish Jun 23 '21 at 08:38