Forum Moderators: open

Message Too Old, No Replies

Dynamic Extended Form Validation Problem

Javascript doesn't validate extended form?

         

Sherif

11:52 am on Dec 5, 2010 (gmt 0)

10+ Year Member



Hey Guys,

First of all, I would like to start of by thanking each and everyone of you for your contribution in that fruitful site. I would also like to mention that i am new to Javascript, and have relatively little knowledge in it. Recently, i was working with javascript to extend a html form that i had created so that it can adapt to the number of entries that a user needs when he is submitting the form.

Basically, i was using the script provided here [quirksmode.org ], and everything works perfectly.

I tried applying the SpryValidation that dreamweaver has, but it doesn't seen to be working probably due to the naming of the new fields added since they are not mentioned in the script at the end of the page. Does anyone have any idea how i can create a validation for the newly created fields?

Basically, this was the Javascript that i used in the code to extend the form:
var counter = 0;

function moreFields() {
counter++;
var newFields = document.getElementById('readroot').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i=0;i<newField.length;i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
}

window.onload = moreFields;




Also, the jQuery Overlay Date picker [flowplayer.org ] doesn't work even though the only requirement is to set the type of the input field as date. Can anyone help me fix this problem?


Thank you in advance for your cooperation, and i hope to find a solution to these problems soon.

Sincerely,
Sherif

Fotiman

4:42 pm on Dec 17, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




I believe that this is due to the form elements being removed, and the spry validation associated with these fields were not removed, and as a result it is waiting for an input in something that doesn't exist, and as a result the form is not submitted...

That sounds like a logical assumption. How are you deleting the form fields? Got any sample code? My thought is that you need to call Spry.Widget.Utils.destryWidgets on the element when you delete it.


after the spry validator has been run at least once in IE, the newly created elements appear with a state as required, and not blank as the first group of elements.

I'm not sure what you mean.

Sherif

5:09 pm on Dec 17, 2010 (gmt 0)

10+ Year Member



Hey Fotiman....

I delete the form field using the following:
 <input type="button" value="Remove Payment"
onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />

You fill find it in the code that i had provided initially located in the hidden form fields.

When the button remove payment is pressed, the onclick action removes the items as shown above.

-----------------

for the second problem, when i click on Add Payment on IE, and enter some data in the newly created fields, and the click again on Add Payment, the newly created fields appear in RED and next to each field a message states A Value is Required...

How can i make the newly created fields appear without the warning that a value is required in its initial state.


Thanks a lot for the support,
Sherif

Fotiman

6:07 pm on Dec 17, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Ok, I just had a "Duh" moment. Forgot to look at the example I had which actually has the Remove Payment button. :)

Here is an updated example to remove the validators when deleting a row:

<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="http://static.flowplayer.org/tools/demos/dateinput/css/skin1.css">
<link href="SpryValidationTextFields.css" rel="stylesheet" type="text/css" >
<title>Test</title>
</head>
<div id="standardform" style="display:none">
<input type="hidden" name="counter" value="marker" />
<select name="payment_type">
<option value="single" selected="selected">Single</option>
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Semi Anual</option>
<option value="12">Anual</option>
</select>
<span id="spryvalidatorNumber">
<input name="number" value="" />
<span class="textfieldRequiredMsg">A value is required.</span>
<span class="textfieldInvalidFormatMsg">Invalid format.</span>
</span>
<span id="spryvalidatorValue">
<input name="value" value="" />
</span>
<span id="spryvalidatorStartDate">
<input type="date" class="date" name="startdate" value="" />
</span>
<input type="button" value="Remove Payment"
onclick="lessFields(this.parentNode);" /><br />
</div>
<form method="post" action="formprocess.php">
<div id="writeroot"></div>
<input type="button" id="btnmoreFields" value="Add Payment!" onclick="moreFields();" />
<input type="submit" value="Send" />
</form>
<script src="dateinput.js"></script> <!--http://cdn.jquerytools.org/1.2.5/full/jquery.tools.js?foo"-->
<script src="SpryValidationTextField.js" type="text/javascript"></script>
<script type="text/javascript">
var counter = 0;
var spryValidators = {
textFields: []
};
/**
* Given a DOM node, iterate through the children and return the name of the
* first input found. If no inputs are found, return null.
* @param n The Node whose children will be iterated through
*/
function getFirstInputName(n) {
var children = n.childNodes,
i,
n;
for (i = 0, n = children.length; i < n; i++) {
// if a child node is an element (nodeType 1) and has tagName "input"
// then we can stop searching and return the name
if (children[i].nodeType == 1 && children[i].tagName.toLowerCase() == "input") {
return children[i].name;
}
}
// if no input elements were found, return null
return null;
}
/**
* @param el The element whose children we want to inspect
* @param uniqueValue A unique value that will be appended to the id of child
* elements.
* is needed to create the Spry.Widget.ValidationTextField.
*/
function updateChildren(el, uniqueValue) {
var i, // index to an array
n, // length of the array
newFields = el.childNodes, // The children of the current el
theName, // the name attribute of child inputs
uid; // a unique id of the validator span wrapper around an input
// iterate through each of the children of the element passed in
for (i = 0, n = newFields.length; i < n; i++) {
// ignore all children that are not HTMLElement nodes (nodeType 1)
if (newFields[i].nodeType == 1) {
// check to see if the current element is a span element. If so,
// we'll need to updated that elements children as well.
if (newFields[i].tagName.toLowerCase() == "span") {
var firstInputName = getFirstInputName(newFields[i]);
if (firstInputName) {
// give it a unique id based on it's current id, plus the
// unique value that was passed in, plus the index in the array
uid = newFields[i].id + "_" + uniqueValue + "_" + i;
newFields[i].id = uid;
// Record the id value in an array. When we're all done looping,
// we'll create a validator for each item in the array.
spryValidators.textFields.push({uid: uid, name: firstInputName});
// update the child nodes of the span
updateChildren(newFields[i], uid);
}
}
else {
// this is not a span element, so must be a form control
// Note, that is not a strict requirement... if the markup that
// we copied contained other elements than form controls and
// span element, then we should probably add additional checks
// here. However, given the HTML above, this should work.
// ...
// give this element a unique name based on the current name
// plus the global counter
theName = newFields[i].name;
if (theName) {
newFields[i].setAttribute("name", theName + counter);
newFields[i].name = theName + counter;
}
}
}
}
}
/**
* Add a duplicate of 'standardform' field items to the form.
*/
function moreFields() {
counter++;
// Get the block containing the nodes to be cloned
var newFields = document.getElementById('standardform').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
//var newField = newFields.childNodes;
updateChildren(newFields, counter);
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
// Setup date field
var d = $("input.date[name!=startdate]");
d.each(function (i, el) {
$(el).dateinput({
format: 'yyyy-mm-dd', // the format displayed for the user
selectors: true, // whether month/year dropdowns are shown
//min: -100, // min selectable day (100 days backwards)
//max: 1, // max selectable day (100 days onwards)
offset: [10, 20], // tweak the position of the calendar
speed: 'fast', // calendar reveal speed
firstDay: 0, // which day starts a week. 0 = sunday, 1 = monday etc..
yearRange: [-6, 7],
trigger: true
});
});
// Attach spry Validation
for (i = 0, n = spryValidators.textFields.length; i < n; i++) {
if (spryValidators.textFields[i]) {
// This is just for debugging
alert('creating validator: ' + spryValidators.textFields[i].uid + ", " +spryValidators.textFields[i].name);
switch (spryValidators.textFields[i].name) {
case "number":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "integer", {validateOn: ["change"]});
break;
case "value":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "real", {validateOn: ["change"]});
break;
case "startdate":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "date", {validateOn: ["change"], format:"yyyy-mm-dd"});
break;
}
// remove this field from the array so we dont attempt to create
// it again next time moreFields is called.
spryValidators.textFields[i] = null;
}
}
}
/**
* @param el The node to be removed
*/
function lessFields(el) {
var children = el.childNodes,
i,
n;
for (i = 0, n = children.length; i < n; i++) {
if (children[i].nodeType == 1) {
// check to see if the current element is a span element. If so,
// we'll need to updated that elements children as well.
if (children[i].tagName.toLowerCase() == "span") {
var firstInputName = getFirstInputName(children[i]);
if (firstInputName) {
Spry.Widget.Utils.destroyWidgets(children[i]);
}
}
}
}
el.parentNode.removeChild(el);
}
</script>
</body>
</html>



for the second problem, when i click on Add Payment on IE, and enter some data in the newly created fields, and the click again on Add Payment, the newly created fields appear in RED and next to each field a message states A Value is Required...

How can i make the newly created fields appear without the warning that a value is required in its initial state.


Hmmm... I'm not seeing that behavior in my example. Using IE7, WinXP.

Sherif

8:53 pm on Dec 17, 2010 (gmt 0)

10+ Year Member



Hey Fotiman....


Thanks a lot for the all of the effort that you have spent here to help my solve the problem...

Now the validation works perfectly, and when we remove the input group, the associated validation is also removed correctly... and the form is submitted correctly...


Regards the initial state of the newly created form fields, it appears correctly if nothing has been entered in any created new fields... if only one field was triggered, the next time we click on add more fields, the initial state appears as red, and states A Value is Required for any fields being added from that point on.

This is what happens on IE8 on Windows 7....

Do you have any ideas...

Thanks a lot for the support.

Sincerely,
Sherif

Fotiman

9:15 pm on Dec 17, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Ok, I just took a look from another system I have which is running IE8 in Win7. I still do not see the same behavior that you're seeing. Here's what I'm doing.
1. Create a new set of fields
2. Enter a value in the first field
3. Click on the button to create new fields
a. The field I entered a value in is validated
b. The new fields are created blank as expected, and no validation errors appear anywhere

So I'm not sure why you're seeing the behavior you describe.

Sherif

9:17 pm on Dec 18, 2010 (gmt 0)

10+ Year Member



Hey Fotiman.....

Thanks for all of the support that you had provided until now... everything works perfectly less the initial state of the newly created form fields on IE8 after any fields have had text being typed or triggered in any way.

If i didn't enter any text at all in any of the fields, the newly created fields appear with the correct state, else the initial state appears as required.

I have no idea why this happens, Any way if you have any possible solutions for that problem then that would be great...


Again, thanks a lot for all of the support and patience that you have provided.

You are my NUMBER ONE [1] here on this forum....

Sincerely,
Sherif

Fotiman

5:04 am on Dec 19, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I wish I was able to reproduce the problem, but I just don't see that behavior. The only thing I could think of that might do that was if the fields were not being correctly named (so that they all ended up with the same name).

Sherif

12:24 pm on Dec 19, 2010 (gmt 0)

10+ Year Member



Hey Fotiman....

Its ok.. probably i have something wrong with my browser... I'll check it later and see how everything goes...


By the way, i wanted to add an additional field, so i created a text field in the hidden form, and i added the span tag for the spry validator...
 <span id="sprytextfield4">
<input name="textfield" value="" />
<span class="textfieldRequiredMsg">A value is required.</span><span class="textfieldInvalidFormatMsg">Invalid format.</span></span>


and modified the switch case to create the spry validator for the text field as follows


 // Attach spry Validation
for (i = 0, n = spryValidators.textFields.length; i < n; i++) {
if (spryValidators.textFields[i]) {
// This is just for debugging
alert('creating validator: ' + spryValidators.textFields[i].uid + ", " +spryValidators.textFields[i].name);
switch (spryValidators.textFields[i].name) {
case "number":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "integer", {validateOn: ["change"]});
break;
case "value":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "real", {validateOn: ["change"]});
break;
case "startdate":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "date", {validateOn: ["change"], format:"yyyy-mm-dd"});
break;
case "textfield":

new Spry.Widget.ValidationTextField(spyrValidators.textFields[i].uid, "none", {validateOn:["change"]});
break;
}
// remove this field from the array so we dont attempt to create
// it again next time moreFields is called.
spryValidators.textFields[i] = null;
}


The alert appears to have detected it, but for some reason it doesn't validate if it is empty...

Am I missing something?
If i later needed to add or remove a field, what should i do in the code to adapt for the changes?

Thanks a lot for the support.


Sincerely,
Sherif

Fotiman

2:36 pm on Dec 20, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




but for some reason it doesn't validate if it is empty...

You have a typo:

new Spry.Widget.ValidationTextField(spyrValidators.textFields[i].uid, "none", {validateOn:["change"]});

Should be:

new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "none", {validateOn:["change"]});

(spyr vs. spry)

It looks like you've got the basics down. If you add another field you just need to add the HTML for it, and then add the case for that field where you attach the validator.

Sherif

8:39 am on Dec 21, 2010 (gmt 0)

10+ Year Member



Hey Fotiman...

Thanks for the hint.. now everything works perfectly......

I can't believe that that slipped from me..

For some reason, when I place the form elements in a Table to set their layout, none of the validation work at all.


<table>
<tr>
<td>
FORM LABELS
</td>
<td>
FORM ELEMENTS
</td>
</tr>......
</table


Do you have any explanation or suggestions for that, or a possible solution?

Thanks a million for the support.

Sincerely,
Sherir

Fotiman

3:04 pm on Dec 21, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The reason that doesn't work is because the updateChildren method is only handling span elements and if it doesn't find a span element, it assumes that the element is a form control (this was based on the previous markup). So that method needs to change to handle the other possibilities. Notice I explicitly pointed that out in the original code:

// Note, that is not a strict requirement... if the markup that
// we copied contained other elements than form controls and
// span element, then we should probably add additional checks
// here. However, given the HTML above, this should work.


Note also, the more elements you need to traverse, the less efficient this method becomes. As a side note, some might argue that you should not use tables for layout (I would be one of those people). :) However, rather than get into that age old debate, I offer the following code which has been updated to handle your new markup:


<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="http://static.flowplayer.org/tools/demos/dateinput/css/skin1.css">
<link href="SpryValidationTextFields.css" rel="stylesheet" type="text/css" >
<title>Test</title>
</head>
<div id="standardform" style="display:none">
<input type="hidden" name="counter" value="marker" />
<table>
<tr>
<td>
FORM LABELS
</td>
<td>
<select name="payment_type">
<option value="single" selected="selected">Single</option>
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Semi Anual</option>
<option value="12">Anual</option>
</select>
<span id="spryvalidatorNumber">
<input name="number" value="" />
<span class="textfieldRequiredMsg">A value is required.</span>
<span class="textfieldInvalidFormatMsg">Invalid format.</span>
</span>
</td>
</tr>
</table>
<span id="spryvalidatorValue">
<input name="value" value="" />
</span>
<span id="spryvalidatorStartDate">
<input type="date" class="date" name="startdate" value="" />
</span>
<span id="sprytextfield4">
<input name="textfield" value="" />
<span class="textfieldRequiredMsg">A value is required.</span>
<span class="textfieldInvalidFormatMsg">Invalid format.</span>
</span>
<input type="button" value="Remove Payment"
onclick="lessFields(this.parentNode);" /><br />
</div>
<form method="post" action="formprocess.php">
<div id="writeroot"></div>
<input type="button" id="btnmoreFields" value="Add Payment!" onclick="moreFields();" />
<input type="submit" value="Send" />
</form>
<script src="dateinput.js"></script> <!--http://cdn.jquerytools.org/1.2.5/full/jquery.tools.js?foo"-->
<script src="SpryValidationTextField.js" type="text/javascript"></script>
<script type="text/javascript">
var counter = 0;
var spryValidators = {
textFields: []
};
/**
* Given a DOM node, iterate through the children and return the name of the
* first input found. If no inputs are found, return null.
* @param n The Node whose children will be iterated through
*/
function getFirstInputName(n) {
var children = n.childNodes,
i,
n;
for (i = 0, n = children.length; i < n; i++) {
// if a child node is an element (nodeType 1) and has tagName "input"
// then we can stop searching and return the name
if (children[i].nodeType == 1 && children[i].tagName.toLowerCase() == "input") {
return children[i].name;
}
}
// if no input elements were found, return null
return null;
}
/**
* @param el The element whose children we want to inspect
* @param uniqueValue A unique value that will be appended to the id of child
* elements.
* is needed to create the Spry.Widget.ValidationTextField.
*/
function updateChildren(el, uniqueValue) {
var i, // index to an array
n, // length of the array
newFields = el.childNodes, // The children of the current el
tagname, // the tag name of the child element
theName, // the name attribute of child inputs
uid; // a unique id of the validator span wrapper around an input
// iterate through each of the children of the element passed in
for (i = 0, n = newFields.length; i < n; i++) {
// ignore all children that are not HTMLElement nodes (nodeType 1)
if (newFields[i].nodeType == 1) {
tagname = newFields[i].tagName.toLowerCase();
switch (tagname) {
case "span":
// if the current element is a span element we'll need to
// update that elements children as well. We'll only do that
// if the span element contains an input element.
var firstInputName = getFirstInputName(newFields[i]);
if (firstInputName) {
// give it a unique id based on it's current id, plus the
// unique value that was passed in, plus the index in the array
uid = newFields[i].id + "_" + uniqueValue + "_" + i;
newFields[i].id = uid;
// Record the id value in an array. When we're all done looping,
// we'll create a validator for each item in the array.
spryValidators.textFields.push({uid: uid, name: firstInputName});
// update the child nodes of the span
updateChildren(newFields[i], uid);
}
break;
case "input":
case "select":
case "textarea":
// this is a form control
// give this element a unique name based on the current name
// plus the global counter
theName = newFields[i].name;
if (theName) {
newFields[i].setAttribute("name", theName + counter);
newFields[i].name = theName + counter;
}
break;
default:
// this is neither a form control or a span element so drill
// down to it's children
updateChildren(newFields[i], uniqueValue);
break;
}
}
}
}
/**
* Add a duplicate of 'standardform' field items to the form.
*/
function moreFields() {
counter++;
// Get the block containing the nodes to be cloned
var newFields = document.getElementById('standardform').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
//var newField = newFields.childNodes;
updateChildren(newFields, counter);
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
// Setup date field
var d = $("input.date[name!=startdate]");
d.each(function (i, el) {
$(el).dateinput({
format: 'yyyy-mm-dd', // the format displayed for the user
selectors: true, // whether month/year dropdowns are shown
//min: -100, // min selectable day (100 days backwards)
//max: 1, // max selectable day (100 days onwards)
offset: [10, 20], // tweak the position of the calendar
speed: 'fast', // calendar reveal speed
firstDay: 0, // which day starts a week. 0 = sunday, 1 = monday etc..
yearRange: [-6, 7],
trigger: true
});
});
// Attach spry Validation
for (i = 0, n = spryValidators.textFields.length; i < n; i++) {
if (spryValidators.textFields[i]) {
// This is just for debugging
//alert('creating validator: ' + spryValidators.textFields[i].uid + ", " +spryValidators.textFields[i].name);
switch (spryValidators.textFields[i].name) {
case "number":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "integer", {validateOn: ["change"]});
break;
case "value":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "real", {validateOn: ["change"]});
break;
case "startdate":
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "date", {validateOn: ["change"], format:"yyyy-mm-dd"});
break;
case "textfield":
alert('attaching textfield validator to ' + spryValidators.textFields[i].uid);
new Spry.Widget.ValidationTextField(spryValidators.textFields[i].uid, "none", {validateOn:["change"]});
break;
}
// remove this field from the array so we dont attempt to create
// it again next time moreFields is called.
spryValidators.textFields[i] = null;
}
}
}
/**
* @param el The node to be removed
*/
function lessFields(el) {
var children = el.childNodes,
i,
n;
for (i = 0, n = children.length; i < n; i++) {
if (children[i].nodeType == 1) {
// check to see if the current element is a span element. If so,
// we'll need to updated that elements children as well.
if (children[i].tagName.toLowerCase() == "span") {
var firstInputName = getFirstInputName(children[i]);
if (firstInputName) {
Spry.Widget.Utils.destroyWidgets(children[i]);
}
}
}
}
el.parentNode.removeChild(el);
}
</script>
</body>
</html>


Take a look and make sure you understand what it's doing.

Sherif

4:37 pm on Dec 21, 2010 (gmt 0)

10+ Year Member



Hey Fotiman....

I remember reading this segment of the code, but the thing that made me thought that this method would work was that I placed the span elements inside the table, so I assumed that it would work since when it analyzed the different components, it will still find the span tags as is, and it would find the input fields as is.

Regards the styling and the use of tables, I wanted to use the tables so that the text would align to the right next to the input elements, and the input elements will have the same start point so that it will look more professional. Nothing more, nothing less. :P


Regards the modification that you have done, I think i understand it...
Based on what i understood, you use switch tag with different cases to analyze the various elements, if one of the conditions were satisfied (being a span, textarea, input or a select), then there will be a specific set of action for each either updating the name of the tag, or also updating its children.

If none of the switch cases is satisfied, then the default action is to take the id of the tag that it is analyzing, and reiterate through the function
updateChildren(newFields[i], uniqueValue);
with the
newFields[i]
information with the associated uniqueValue, and loop until it finds something.

Please tell me if what i understood is correct...

Any ways.... Thanks a lot for the support that you have provided here in this post. I'll test it out, and come back later telling you how everything goes.


Sincerely,
Sherif

Fotiman

4:46 pm on Dec 21, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




Regards the styling and the use of tables, I wanted to use the tables so that the text would align to the right next to the input elements, and the input elements will have the same start point so that it will look more professional. Nothing more, nothing less. :P

In other words, you wanted to modify the "presentation"... sounds like a job for CSS. ;) There are ways to do what you're trying to do with CSS, though I admit it can be more difficult to get just right.


Please tell me if what i understood is correct...

Yup, you got it exactly. :)
This 43 message thread spans 2 pages: 43