'JavaScript closure function not called on form submit
This has me perplexed. I have a textbox. You type something in, and press enter. JavaScript creates a read-only input element in separate form. Alongside the text input boxes are buttons to remove them. There is also a submit button at the bottom of that form to submit all of the read-only text inputs.
Since clicking a button inside of a form would submit the form (and I just want to remove the parent div that contains both the button and its corresponding text input), a function is called when the form submits. This function determines which type of button (remove or submit) was pressed and acts accordingly.
Here comes the problem. When a remove button is pressed, the function destinations.enter
is never called. What I did to solve this was to create a global function called submitDestinations
that replicates the functionality of destinations.enter
. If this function is called instead, everything goes off without a hitch.
Does anyone have an idea as to why destinations.enter
will not run on submit, but submitDestinations
will? I want to believe that it has something to do with closures, because the function scope appears to be the only difference between the two functions. However, this is my first time using closures, and I have only a limited understanding of them.
Javascript:
var destinations = (function(){
var max_destinations = 7;
var counter = 0;
function increment(){
counter += 1;
if(counter > max_destinations){
throw 'Too many destinations. (Max 7)'
}
}
function decrement(){
counter += 0;
if(counter < 0){
alert('Cannot have less than 0 destinations..')
throw 'Too few destinations. Get out of the console!'
}
}
return {
add : function(form){
try{
var formInput = form.elements[0];
var destination = formInput.value;
// Dont do anything if the input is empty
if(destination == ""){
return false;
}else{
// increment the destinations counter
increment();
}
}catch(err){
alert(err);
return false;
}
// add the text value to a visual element
var elem = document.createElement('div');
elem.setAttribute('class','destination');
// create the input
var input = document.createElement('input');
input.setAttribute('id','dest'+String(counter));
input.setAttribute('class','destinationText');
input.setAttribute('style','border: none');
input.setAttribute('name','destinations');
input.setAttribute('readonly','readonly');
input.setAttribute('value',destination);
// create the remove button
var button = document.createElement('button');
button.setAttribute('onclick','this.form.submitted=this;');//'return destinations.remove(this);');
button.setAttribute('class','removeButton')
button.setAttribute('id','but'+String(counter))
var buttonText = document.createTextNode('Remove');
button.appendChild(buttonText);
// add the elements to the div
elem.appendChild(input);
elem.appendChild(button);
var parent = document.getElementById('destinationsDiv');
parent.appendChild(elem);
// clear the input box
formInput.value = '';
return false;
},
enter : function(form){
alert('hi')
var button = form.submitted;
if(button.id != 'submitBtn'){
return remove(button);
}else{
return true;
}
return false;
},
remove : function(button){
try{
decrement();
}catch(err){
// do not allow less than 0 counter
alert(err);
return false;
}
// remove the button's parent div altogether
var toDelete = button.parentNode;
toDelete.parentNode.removeChild(toDelete);
return false;
}
}
})();
And the html:
<div>
<form id='hi' onsubmit="return destinations.add(this);">
<input type="text" value="" />
</form>
<!--form id='submitDiv' method="post" onsubmit="alert(this.submitted);return submitDestinations(this);"-->
<form id='submitDiv' method="post" onsubmit="alert(this.submitted);return destinations.enter(this);">
<div id='destinationsDiv'>
<div>
<input id="dest1" class="destinationText" style="border: none" name="destinations" readonly="readonly" value="aadasd" \>
<button onclick="this.form.submitted=this;" class="removeButton" id="but1" \></button>
</div>
<div>
<input id="dest2" class="destinationText" style="border: none" name="destinations" readonly="readonly" value="Hi Stackoverflow" \>
<button onclick="this.form.submitted=this;" class="removeButton" id="but2" \></button>
</div>
</div>
<input type="submit" id='submitBtn' onclick="this.form.submitted=this;"/>
</form>
</div>
Everything works fine if I add the following javascript function to the global scope and call it instead. This does the exact same thing as destinations.enter
function submitDestinations(form){
var button = form.submitted;
if(button.id != 'submitBtn'){
return destinations.remove(button);
}else{
return true;
}
}
All I change in the html is the method that is called on submit:
<div>
<form id='hi' onsubmit="return destinations.add(this);">
<input type="text" value="" />
</form>
<form id='submitDiv' method="post" onsubmit="alert(this.submitted);return submitDestinations(this);">
<!--form id='submitDiv' method="post" onsubmit="alert(this.submitted);return destinations.enter(this);"-->
<div id='destinationsDiv'>
<div>
<input id="dest1" class="destinationText" style="border: none" name="destinations" readonly="readonly" value="aadasd" \>
<button onclick="this.form.submitted=this;" class="removeButton" id="but1" \></button>
</div>
<div>
<input id="dest2" class="destinationText" style="border: none" name="destinations" readonly="readonly" value="Hi Stackoverflow" \>
<button onclick="this.form.submitted=this;" class="removeButton" id="but2" \></button>
</div>
</div>
<input type="submit" id='submitBtn' onclick="this.form.submitted=this;"/>
</form>
</div>
Solution 1:[1]
It turns out there was a naming conflict. The name attribute of the text inputs I was creating were set to "destinations", the same name as the javascript object I was calling on submit. The javascript in "onsubmit" was therefore trying to reference the DOM element and call enter on it instead of referencing my javascript function.
Solution 2:[2]
There is an error in the implementation of destinations.enter(). It calls remove() to remove the button, but the name 'remove' is not bound in the scope where destinations.enter() is defined.
It looks as though destinations.remove() is never called elsewhere, so the easiest fix is just to move it up to being a private function in the destinations constructor, rather than making it a method on the destinations object.
The modified version works because you have changed the body of submitDestinations() to call destinations.remove(), which is bound in that scope.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Patch Rick Walsh |
Solution 2 | Ben Butler-Cole |