I want to validate on button click that at least one of the rows must be edited and updated in JavaScript.
So I wrote the below code for validation
function checkGridValidate() {
var StrPriError = "";
var grdCount = GrdProspective1.Rows.length;
for (var i = 0; i < grdCount; i++) {
if (GrdProspective1.Rows[0].Cells[5].Value == "" || GrdProspective1.Rows[0].Cells[7].Value == "") {
StrPriError += "Kindly edit atleast one row \n";
}
if (StrPriError != "") {
alert(StrPriError);
return false;
}
else {
return true;
}
}
}
What happening here is, when I update the first row and submit it is not giving any alert that's perfect, but when I update the second row it still asks me Kindly edit at least one row.
I don't know what's going wrong here.
Have a look the js fiddle for the same
Currently, the validation is limited to only check the top row for two reasons:
.Rows[0] will always inspect the top row, despite the for loop.
This should make use of i as it increments through the collection:
if (GrdProspective1.Rows[i].Cells[5].Value == "" ||
The last if..else, by returning in either case, will interrupt the loop. The return statements here have a similar effect to break statements, with regards to the loop.
So, unless you want the loop to be interrupted, they should be moved out the loop:
for (var i = 0; i < grdCount; i++) {
if (...) {
// ...
}
}
if (StrPriError != "") {
alert(StrPriError);
return false;
}
else {
return true;
}
Though, fixing those should reveal a different issue – the function is checking that every row has been edited rather than one-or-more.
If, for example, there are 5 rows and you fill in both fields in 2 of the rows, the remaining 3 rows will match the condition and append the error message.
Inverting the condition, so you're searching for a row that's filled in and remembering whether you have, should resolve this.
function checkGridValidate() {
// assume invalid until found otherwise
var anyEdited = false;
var grdCount = GrdProspective1.Rows.length;
for (var i = 0; i < grdCount; i++) {
var cells = GrdProspective1.Rows[i].Cells;
// verify that both fields were given a value
if (cells[5].Value !== "" && cells[7].Value !== "") {
anyEdited = true; // remember that you've found an edited row
break; // and, no need to keep looking for more
}
}
// alert only if no rows were filled out
if (!anyEdited) {
alert("Kindly edit at least one row.");
}
return anyEdited;
}
Related
I am having an issue with the following code when trying to iterate through the items in a form and delete them to make way for new sections/questions. However, I sometimes get the following error "Invalid data updating form". I have worked around this multiple times now, but it keeps coming back up. My current workaround has been to set the section title to "", which made it available to delete. Previously, I didn't need to do this until today.
My question: What is the best way to iterate through the items in a form and delete them from a starting point and not encounter this error?
Reference:
f = the current active form
f_items = all of the items of the form in an array
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date/Time")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
Logger.log(item.getTitle() + " | " + item.getType());
Logger.getLog();
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear);
f_i_len = f.getItems().length-1;
clear++;
}
}
function find(src, name)
{
var s_len = src.length;
for(var iter = 0; iter < s_len; iter++)
{
var s = src[iter].getTitle();
if(s == name)
{ return iter; }
}
return -1;
}
The issue I had with this was that the PageBreakItem I was trying to delete was the destination for a conditional answer earlier in the form.
Below is my code where I needed to delete everything after a certain item, which linked to the sections I needed to delete, so I was able to iterate backwards with a while loop.
function getParentNameItem_(form, form_items){
//finds the item with conditional navigation to what you want to delete
var parent_name_item = find_(form_items, "Your Name");
parent_name_item = parent_name_item.asListItem();
//clears all choices which breaks the navigation dependency
//this frees up the items to be deleted
parent_name_item.setChoices([parent_name_item.createChoice("")]);
var size = form_items.length - 1;
//iterates from the end back to the last question I want to keep
while(form_items[size].getTitle() != "Your Name"){
//this can take either the item itself or the index as I've done
form.deleteItem(size);
size--;
}
/*I rebuild the choices for parent_name_item
later based on information from a spreadsheet
which I also use to determine the content of
the PageBreakItems I just deleted*/
return parent_name_item;
}
I found out the issue! It was the clear++ at the end of the loop. With the number of items in the going down with each iteration, the clear++ was causing it to skip over the page break items. Below is my finished code:
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear); //}
f_i_len = f.getItems().length-1;
}
}
I have 8 form inputs that are asking for either 8 half-day activity dates or, 4 fullday dates.
I collected all of the input values and put them into an array, and to test the collection process, wrote the following function that just says if ALL the inputs are empty, keep a button disabled and if ALL are full, enable the button.
function checkMeetings()
{
for(var i = 0; i < meetings.length; i++)
{
if(meetings[i] === "" || meetings[i] === null)
{
meetingsCanSubmit = false;
}
else
{
meetingsCanSubmit = true;
}
}
}
checkMeetings();
That test worked fine.
What I'd like to do is create a counter that counts the number of input boxes that have been filled in and when it gets to at >= 4 enable the button. (In reality it won't enable the button it's going to run a secondary function but for the purposes of this example I'm keeping it simple.)
Since the for loop is counting via the i++ anyways, I tried something to the effect of
if(meetings[i] <= 4) do the following, but that doesn't seem to be doing the trick. Should I be setting up a second counter within my if-statement?
You can use Array.prototype.filter(), check the .length of resulting array
var meetingsCanSubmit = meetings.filter(function(input) {
return input !== "" && input != null
}).length >= 4;
if (meetingsCanSubmit) {
// do stuff
}
Situation: I want to create a simple code whereby users must select at least one extension to proceed. Users should select at least 1 or more extension, else an alert message will appear.
Problem: The problem is, if there shall be only 1 extension available for selection, whether it is selected or not, the alert message will appear disallowing the registration to complete.
//Select atleast one extension
var arrCheckboxes = document.checkForm.elements["product"];
var checkCount = 0;
for (var i = 0; i < arrCheckboxes.length; i++) {
checkCount += (arrCheckboxes[i].checked) ? 1 : 0;
}
if (checkCount > 0){
return true;
} else {
alert("Select at least one Extension.");
return false;
}
It is a legacy from the very early days of browsers that if there is only one form control with a name of product, then:
document.checkForm.elements["product"];
will return a reference to that control, not a collection which you seem to expect. Such controls do not have a length property by default so:
arrCheckboxes.length
returns undefined and
i < arrCheckboxes.length
is false so the loop is never entered.
To fix that, use querySelectorAll which always returns a collection:
var arrCheckboxes = document.checkForm.querySelectorAll('[name=product]');
Supported in IE 8+ and everywhere else. A simpler version of your code (assuming it's in the body of a function):
var arrCheckboxes = document.checkForm.querySelectorAll('[name=product]');
for (var i = 0; i < arrCheckboxes.length; i++) {
if (arrCheckboxes[i].checked) return true;
}
alert("Select at least one Extension.");
return false;
enter code hereI am trying to make something editable online with a function like this
function toggle_editable (div, cssclass) {
var classToEdit = document.getElementsByClassName(cssclass)
for (i = 0;classToEdit.length; i++) {
if (classToEdit[i].contentEditable == false) {
classToEdit[i].contentEditable = true ;
}
if (classToEdit[i].contentEditable == true) {
classToEdit[i].contentEditable = false ;
}
}
}
classToEdit is a collection of HTML elements with the same class name or whatever document.getElementsByClassName(cssclass) returns
when going through the debugger it jumps over the line
classToEdit[i].contentEditable == true
as well as over the line
classToEdit[i].contentEditable == true
and does not execute the code in the braces following the if statements
this works however - meaning it sets the contenteditable property without hesitation
classToEdit.contenteditable = true;
as well as this
classToEdit.contenteditable = false;
(well obviously)
also this seemed to have no effect
classToEdit.contenteditable = !classToEdit.contenteditable
ideas anyone?
ps why is the loop
You've created an infinite loop here:
for (i = 0;classToEdit.length; i++) {
Should be:
for (var i = 0; i < classToEdit.length; i++) {
But, if you say classToEdit.contenteditable = true "works", you've to define "not working/is working" since the snippet doesn't definitely do what you expect it to do, if classToEdit is a HTMLCollection.
It looks like you'd want to toggle contentEditable values, you can do it like this:
for (var i = 0; i < classToEdit.length; i++) {
if (classToEdit[i].contentEditable == false) {
classToEdit[i].contentEditable = true ;
} else { // Notice else here, no need for another check
classToEdit[i].contentEditable = false;
}
}
Or simply without ifs in the loop:
classToEdit[i].contentEditable = !classToEdit[i].contentEditable;
Your current code will switch the value back to it's original in a case the value was false.
HTMLElement.contentEditable returns a string and not a boolean.
Hence, what you want to identify the state of your editable field is:
// Incorrect
classToEdit[i].contentEditable == true
// Coorect
classToEdit[i].contentEditable === 'true'
What's even better if you want to know the state of your fields is to use HTMLElement.isContentEditable
which returns a boolean:
classToEdit[i].contentEditable = !element.isContentEditable
Another way to refactor the above:
function toggleContentEdit() {
var editableFields = document.getElementsByClassName('editable');
[].forEach.call(editableFields, function(field){
var isEditable = field.isContentEditable;
field.setAttribute('contenteditable', !isEditable);
});
};
JSFiddle: http://jsfiddle.net/6qz3aotv/
I am designing a JavaScript function checkForm(), which is meant to check if at least half of the answers of a form have been completed. It returns ”true” if the condition is met and an alert message and ”false” if not. The answers are given in input text fields, whose content mustn't be the empty string in order to be regarded as answered.
In order to make the check, the function checkForm() calls two other functions: totalNumberOfQuestions() and totalCompletedAnswers(), as follows:
function totalNumberOfQuestions() /* Returns the total number of questions in the form */
{ total
numberOfQuestions = 0;
i = 1;
do
{
nameOfAnswer = 'answer_' + i;
if(document.getElementsByName(nameOfAnswer)[0] != null)
{
totalnumberOfQuestions ++;
}
i++;
}
while (document.getElementsByName(nameOfAnswer)[0] != null);
return totalnumberOfQuestions;
}
function totalCompletedAnswers() /* Returns the total number of completed answers in the form */
{
numberOfCompletedAnswers = 0;
i = 1;
for(i = 1; i<= totalNumberOfQuestions(); i++)
{
nameOfAnswer = 'answer_' + i;
if ( form[nameOfAnswer].value != '')
{
numberOfCompletedAnswers ++;
}
}
return numberOfCompletedAnswers;
}
function checkForm()
{
if ( totalCompletedAnswers() < 7 )
{
alert("Please answer at least half of the questions.");
return false;
}
return true;
}
But, surprisingly, the function checkForm() does not work correctly in this form, but returns true even when the number of completed answers is less than a half of the total number of answers. However, the function works properly if the function totalCompletedAnswers() is checked against a constant value, e.g.:
function checkForm()
{
if ( totalCompletedAnswers() < 7 )
{
alert("Please answer at least half of the questions.");
return false;
}
return true;
}
This means that using the function totalNumberOfQuestions() in the body of the checkForm() function is ”importing” some unwanted returned value. This happens even if I call the former separately (x= totalNumberOfQuestions() and then rewriting the condition: if ( totalCompletedAnswers() < x )
). Does anyone know how one can avoid such unwanted returned values when calling a function?
In totalComletedAnswers you have the snippet:
if(numberOfCompletedAnswers < totalNumberOfQuestions() )
{
alert("Please answer at least half of the questions.");
return false;
}
So totalCompletedAnswers will return false if all the answers are not completed not the number of questions answered.
In checkForm the the following will evaluate to true: false < 7
See: http://jsfiddle.net/5mURZ/
Have a look at these two articles for more information on truthiness/falsiness in javascript:
http://11heavens.com/falsy-and-truthy-in-javascript
http://www.sitepoint.com/javascript-truthy-falsy/
UPDATE
Upon closer inspection you have a scoping issue. All your variables have a global scope. This will cause the most issues with the variable i. Declaring the variable with the var key word will restrict the scope of the variable to the function. It is best practice in javascript to delvare all variable with the var keyword to explicitly declare the scope of the variable.
Working Fiddle: http://jsfiddle.net/Dr5Hx/1/
Article on scoping: http://coding.smashingmagazine.com/2009/08/01/what-you-need-to-know-about-javascript-scope/
Update 2
For a completely different approach, try the magic of jQuery. This approach relies on wrapping the answers in some kind of container. In this case a div. This is required as elements such as check boxes and radio buttons would be multiple elements, but each set should only be counted once.
//The selector below finds DOM elements with a name beginning with "answer"
var answers = $("[name^='answer']"); //'Cache' answers
var answerDivs = $("div").has("[name^='answer']"); //'Cache' answer divs
var totalAnswers = answerDivs.length; //'Cache' number of answers
$("#checkIt").click(function(){ //Add a click listener to the button
var numAnswered = $(answerDivs).filter(function(){ //Filter our answer divs to answered question
var answer = $(this).find("[name^='answer']"); //Get the answer element(s) in the div
var textCheck = $(answer).is("input:text") && $(answer).val() != ''; //Check For Text Value
var selCheck = $(answer).is("select") && $(answer).val() != ''; //Check for selected
var radioCheck = $(answer).is(":checked"); // Check for Checked, WOrks for radio & checkbox
//console.log($(answer).attr("name") + " " + textCheck + " " + selCheck + " " + radioCheck)
return (textCheck || selCheck || radioCheck); //End of the filter
}).length;
if(numAnswered < (totalAnswers/2))
{
alert("Please answer atleast half the questions");
return false;
}
alert("We're good to go");
return true;
});
Just make sure to wrap the above in $(document).ready();
http://jsfiddle.net/Dr5Hx/3/