0

I want to check if a form has changed by using pure javascript.

My plan is to take all text including html tags between the form tag, hash the string and then when I need to check if any of the values has changed, I can just rehash the form and compare them.

So I have

 <form action="/Building" method="post">    <div class="row">
        <div class="col-md-12">
            <div class="row">

    <div class="col-md-3"> Building Address </div>
    <div class="col-md-2"> City </div>
    <div class="col-md-1"> State </div>
    <div class="col-md-2"> Zip </div>
</div>
<div class="row">
    <div class="col-md-3">
        <input id="bldgResult_bldg_mail_address" name="bldgResult.bldg_mail_address" type="text" value="">
    </div>
<div> ...etc
        <input type="submit" value="Save and Next Building »" name="action:SaveContinue" class="btn btn-info pull-right">
        <input type="submit" value="Save" class="btn btn-primary pull-right" name="action:Save">
        <input type="submit" value="Go To Next Building" class="btn btn-primary hash" name="action:Next">
    </div>
</form>

The problem is "value" of the input fields doesn't update. I'm able to change every textbox field and the value or the inner HTML doesnt change.

Here is the code that actually hashes and gets the innerHTML

window.onload = function () {

        var forms = document.getElementsByTagName("form");
            var hashValue = forms[1].innerHTML.hashCode();
            Array.prototype.map.call(document.getElementsByClassName("hash"), function (hObj) {
                hObj.addEventListener("click", function (event) {

                    if (document.getElementsByTagName("form")[1].innerHTML.hashCode() == hashValue) {
                        return true;
                    }
                    else {
                        var conf = confirm("Continue to the next building WITHOUT saving? Pressing \"Okay\" will undo any pending changes." );
                        if(conf)
                        {
                            return true;
                        }
                        event.preventDefault();
                        return false;
                    }
                });
            });
    };

The above block

if (document.getElementsByTagName("form")[1].innerHTML.hashCode() == hashValue) {
                        return true;
                    }

Is always returning true, because the innerHTML doesnt change, even after the textboxes have been typed in.

What can I do? Is there another way to get the text in the HTML with updated information?

christopher clark
  • 2,026
  • 5
  • 28
  • 47

2 Answers2

0

You could loop though your form elements, get and concatenate the values, and then hash the values.

Update:

Here is an example using FormData (depends on browser target):

Hash Function from Here: Generate a Hash from string in Javascript/jQuery

String.prototype.hashCode = function() {
  var hash = 0, i, chr;
  if (this.length === 0) return hash;
  for (i = 0; i < this.length; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};



function GetFormHash() {

    var hashes = [];
    var forms = document.getElementsByTagName("form");
    var _hash = ""

    for(var i=0;i<forms.length;i++) {

       var formData = new FormData(forms[i]);


      for (var key of formData.keys()) {
           console.log(key + "=" + formData.get(key)); 
           _hash = _hash + key + "=" + formData.get(key);
       }

    hashes.push(_hash.hashCode());
    console.log(_hash.hashCode());

  }

  return hashes;
}

There is also an onchange event for <form>. Depends on browser...

<form onchange="alert('changed')"></form>

If you use something like jQuery you could use that change() event: https://api.jquery.com/category/events/form-events/

Change will not tell you if they change the data back - so not 100% reliable. If you were open to a library like jQuery - you could possibly serialize the data https://api.jquery.com/serialize/ to keep track of changes,

One last incomplete example. You would need to update to get non "input" form elements like textarea etc. You would also have to do a bit of work to get the selected radios...

function GetFormHashOther() {

var hashes = [];
    var forms = document.getElementsByTagName("form");
    var _hash = ""

    for(var i=0;i<forms.length;i++) {

     var chill = forms[i].getElementsByTagName("input");

     for (var c of chill) {
        console.log(c.name + " = " + c.value);
        _hash = _hash + c.name + " = " + c.value;
     }

    hashes.push(_hash.hashCode());
    console.log(_hash.hashCode());

  }

  return hashes;
}
Community
  • 1
  • 1
Allan
  • 2,889
  • 2
  • 27
  • 38
  • I would prefer to avoid this, but I may have to. I was curious if there was any code like .allInnerHTML or something that I could use. It's weird because the HTML should be there if they type in the box!!! – christopher clark May 09 '17 at 18:09
0

You could assign an event handler to the 'input' event of each of your fields that changes a boolean flag. You then just check that flag and set it back to false after your check is complete.

For example

document.querySelectorAll("#yourForm input").forEach(input => {
    input.addEventListener("input", () => {
        changed = true;
    });
}

/* ... */

function checkIfChanged() {
    if(changed) {
       // ...
    }

    changed = false;
}

If you also need to check for backspace you could use the keypress event instead.

Jonas Zell
  • 106
  • 1
  • 4
  • I'm seeing a few issues with this method as well, if they end up deleting out what they wrote in a textbox, the user will still get the code to execute, even though nothing needs to be saved. It also might have a problem if the input is inside the form. It definitely will have adverse affects with different keypresses like you mentioned `backspace` – christopher clark May 09 '17 at 18:50
  • I suppose I don't quite understand what you want to count as a "change" and what not. You could bind the handler to the "keypress" event and check whether the user actually typed something via a function like `function isInputEvent (e) { let str = String.fromCharCode(e.which || e.keyCode); return str.match(/[a-zA-Z0-9 ]/); }` This function will return true only if the user types a-z, A-Z or 0-9 – Jonas Zell May 09 '17 at 19:05
  • but if they delete to the initial value, then there is no change. – christopher clark May 09 '17 at 19:09
  • I see. I suppose then the most efficient method is actually iterating through all values, concatenating and comparing them. Although depending on the number of input fields and the length of their values, hashing them might even be unnecessary. I would really doubt that this event needs to be fired often enough for any of these methods to have a measurable performance impact though, so I wouldn't worry about it too much. – Jonas Zell May 09 '17 at 19:22