I'm trying to use HTML form validation when using Google Apps Script's HTML Service. As another user asked, according to the documentation example, you must use a button input instead of a submit input. Using a submit button seems to do the validation, but the server function is called anyway. The answer given to that user didn't work for me. Also, I want to call two functions when submitting the form and this can make it more complex.
This is what I'm trying to do: The user fills a form and I generate a Google Doc and give him the URL. When he clicks the submit button, I show him a jQuery UI dialog saying "Your document is being created" with a nice spinner. Then, when the document is generated, I give him the link. I use the success handler to show the result when the Google Doc stuff is finished, but meanwhile I need a function to show the spinner. I don't know if there is a better way to do that than adding another function to the onclick event and maybe it can be damaging the process in some way. Is there a way not to call any of these functions if the form is not valid (using HTML validation)?
This is a simplified version of my code:
Code.gs
function generateDocument(formObject) {
var doc = DocumentApp.create("Document name");
...
return doc.getUrl();
}
Page.html
<main>
<form id="myForm">
...
<input type="button" value="Generate document"
onclick="showProgress();
google.script.run
.withSuccessHandler(openDocument)
.generateDocument(this.parentNode);"/>
</form>
<div id="dialog-confirm" title="Your document">
<div id="dialog-confirm-text"></div>
</div>
Javascript.html
$( "#dialog-confirm" ).dialog({ autoOpen: false, resizable: false, modal: true });
function showProgress() {
$( "#dialog-confirm" ).dialog({ buttons: [ { text: "Cancel", click: function() { $( this ).dialog( "close" ); } } ] });
$( "#dialog-confirm" ).dialog( "open" );
$( "#dialog-confirm-text" ).html( "<br />Wait a second, your document is being generated...<br /><br /><img src='http://i.stack.imgur.com/FhHRx.gif' alt='Spinner'></img>" );
return false;
}
function openDocument(url) {
$( "#dialog-confirm" ).dialog({ autoOpen: false, resizable: false, width: 400, buttons: [ { text: "Ok", click: function() { $( this ).dialog( "close" ); } } ] });
$( "#dialog-confirm-text" ).html( '<br />Click here to open and print your document!' );
return false;
}
All three HTML docs are joined together (and working with its respective tags) with the include function as recommended in the documentation.
The Cancel button in the dialog will close it but won't stop the doc being created. Is it possible to stop this process?
Here's a solution that I found:
"<input type='submit' onclick='if(verifyForm(this.parentNode)===true){google.script.run.withSuccessHandler(YOUROUTPUT).YOURFUNCTION(this.parentNode); return false;}' value='Submit'></form>";
JavaScript side
function verifyForm(){
var elements = document.getElementById("myForm").elements;
for (var i = 0, element; element = elements[i++];) {
if (element.hasAttribute("required") && element.value === ""){
resetInputs();
return false;
}
if (element.hasAttribute("pattern")){
var value = element.value;
if(value.match(element.pattern)){
}else{
resetInputs();
return false;
}
}
}
return true;
}
Calling the window has issues in iOS sometimes, which is why I investigated this further.
Move the function call to the <form> element; remove any function call from the submit input element; and put intermediary JavaScript code into a <script> tag:
<input tabindex="9" type="submit" value="Save Input" id='idInputBtn'>
<form id="myInputForm" name="input" onsubmit="fncWriteInput(this)">
<script>
window.fncWriteInput= function(argTheInfo) {
// Do additional checks here if you want
var everythingIsOk = . . . . . . . ;
if (everythingIsOk) {
google.script.run
.withSuccessHandler(openDocument)
.generateDocument(argTheInfo);
};
};
Notice that this.parentNode gets removed to the arg of the function call, and just use this in the function argument because the function is getting called from the <form> element, which is the parent.
If there are any errors, the form will not be submitted, and the user will get a msg that something was wrong. No code will run.
This is pseudo code, but I do use a set up like this in my application. But use developer tools and you can put a break point right in your browser and step through every line to test it without needing to put in console.log statements.
Related
i have an array of inputs generated upon a press of a button. the index is maintained using a global variable numInput set to 0. This is the first HTML of the input.
<label><span>First name</span>
<input placeholder="First name" type="text" name="fName[0]" id="fName"></label>
This is the addmore button, which increments numInput and appends the new and mostly the same HMTL with different index
$(document).on('click','#addmore',function(){
numInput++;
var html = '<label><span>First name</span>
<input placeholder="First name" type="text" name="fName['+numInput+']" id="fName"></label>';
$("#frm").append(html);
rerunRules();
});
Here is my validate method. I did not create the message object here...
$(document).ready(function() {
$('#frm').validate({
highlight: function(element, errorClass) {
$(element).fadeOut(function() {
$(element).fadeIn();
});
},
errorPlacement: function(error, element) {
error.appendTo(element.prev("span"));
},
});
});
rerunRules();
instead I changed the defaults:
<script>
jQuery.extend(jQuery.validator.messages, {
required: "- This field is required."
});
</script>
The rerunRules() is a function that I had to call to "reinject" the rules everytime a new HTML is generated. It is also called right after validate() because the fName rules are in it.
function rerunRules(){
$.each( $('input[name^="fName"]'), function () {
$(this).rules('add', {
required: true
})
});
}
Contrary to what I expected, the error message appears on the first fName[0] just as where its meant to be (to the previous span) but doesnt appear on the next generated HTML from the button. The validate works (wont proceed when I dont enter proper data), the highlight works, but no error message.
I have a website and it contains the following code:
<li class="scroll-to-section">Services</li>
and the appropriate section:
<section class="section" id="services">
...
</section>
When I press the li element, the page scrolls to the services section as expected however, the url is appended with /#[object Object] instead of /services.
Here's an image:
Any idea about what the issue may be?
EDIT: Sorry, forgot to add the javascript: The issue was the new_target variable. I had to change it to new_target as it was also target before and that fixed the issue!
$(document).ready(function() {
$(document).on("scroll", onScroll);
//smoothscroll
$('a[href^="#"]').on("click", function(e) {
e.preventDefault();
$(document).off("scroll");
$("a").each(function() {
$(this).removeClass("active");
});
$(this).addClass("active");
var target = this.hash,
menu = target;
var new_target = $(this.hash);
$("html, body")
.stop()
.animate(
{
scrollTop: new_target.offset().top - 79
},
500,
"swing",
function() {
window.location.hash = target;
$(document).on("scroll", onScroll);
}
);
});
});
The question doesn't allows us to examine the problem in big detail but here is -at a glance- what is going on:
You are trying to assign an object to a place where only strings are valid.
Take a look at the following example:
function changeText() {
document.getElementById("input").value = "hello"
}
function changeTextWrong() {
document.getElementById("input").value = {link: "hello"}
}
<input id="input" value="sample text"/>
<button onclick="changeText()">change text (OK)</button>
<button onclick="changeTextWrong()">change text (WRONG)</button>
When the input text (equivalent to the url bar) receives the text "hello" it works because "hello" is a text. However when the input text receives an object, this object is then converted into a string. Any object converted into a string is equivalent to "[object Object]".
Solution? Instead of doing window.location.hash = object you very likely need to do something like window.location.hash = object.hash, or window.location.hash = object.WhAtEvEr depending on the object's actual content.
i think you tried to simplify the question without presenting us the complete or relevant code.
From your example, if you only do that it should work but i have a feeling that your code is doing more than that.
#adelriosantiago was right in pointing out that you might return an object instead of the expected string.
I integrated the new hidden reCAPTCHA (v2) framework which by default verifies the user with the click event of the submit button. But this event is triggered before the built-in HTML form validation. I am looking for a way to make it in the expected order: form validation first, reCAPTCHA after.
You have to do it programmatically thanks to a new v2 grecaptcha method: grecaptcha.execute() so that recaptcha doesn't replace the button's default click event which was preventing the default HTML5 form validation.
The event path is:
Submit button click event: browser built-in form validation
Form submit event: call grecaptcha.execute()
reCAPTCHA callback: submit the form
$('#form-contact').submit(function (event) {
event.preventDefault();
grecaptcha.reset();
grecaptcha.execute();
});
function formSubmit(response) {
// submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
<div class="g-recaptcha"
data-sitekey="your-key"
data-size="invisible"
data-callback="formSubmit">
</div>
<button type="submit">Submit</button>
</form>
Here is my solution to get HTML5 validation + Invisible recaptcha:
HTML:
<form id="my-form">
<!-- Your form fields ... -->
<div class="g-recaptcha"
data-sitekey="..."
data-callback="submitMyForm"
data-size="invisible">
</div>
<button type="submit">Submit</button>
</form>
JS:
var myForm = $('my-form');
function submitMyForm () {
myForm.trigger('submit', [true]);
}
$(function () {
myForm.on('submit', function (e, skipRecaptcha) {
if(skipRecaptcha) {
return;
}
e.preventDefault();
grecaptcha.execute();
});
})
Hi got a working solution here. Working with invisible Recaptcha.
jQuery(document).ready(function() {
var commentform = jQuery("#commentform");
commentform.on("click", "#submit-comment", function(e) {
if(commentform[0].checkValidity()) {
e.preventDefault();
grecaptcha.execute();
}
});
});
function submitCommentForm(data) {
document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
<div class="form-submit">
<div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
<button id="submit-comment">Leave a comment</button>
</div>
</form>
I had this problem as the default method seems to override the html5 form validation. I also wanted all code to be generic rather than hard coding any functions/element names. In the end I came up with the following code using the v3 api -
HTML
<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
...
<input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>
Javascript (I'm using jQuery but would be fairly easy to adapt to vanilla js)
$('.ui-recaptcha').submit(e => {
var form = e.target;
if( $(form).data('recaptcha-done') )
return;
e.preventDefault();
grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {
$(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
$(form).data('recaptcha-done', true);
$(form).submit();
});
});
I found that just calling submit as in some examples above caused a loop for me, which would make sense seeing as the recaptcha handler runs on the submit event.
This runs recaptcha for any ui-recaptcha form, passes the form name attribute as the action which can be seen in reCaptcha console, and then inserts the token into the form. Once run it sets a data attribute on the form so the recursive call to submit doesn't try to run recaptcha again.
Here's my solution.
Uses reCaptcha v3 (invisible) docs
Uses native HTML5 form validation
Uses pure JS
Uses standard POST processing (can be modified to AJAX)
Add as many forms as needed, just change the 'UNIQUE_FORM_ID' in the two places, and update the POST_URL for the form.
Ensure you use your own key in the locations of 'RECAPTCHA_SITE_KEY'.
<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
<!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
<input type="hidden" name="g-recaptcha-response" value="">
<!-- Add other hidden nonce fields -->
<!-- Required field -->
<input name="fullname" type="text" placeholder="Full Name" required>
<!-- Submit button -->
<!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
<!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
<button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>
<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
/**
* Handles form submissions for Google recaptcha v3.
* Allows for HTML5 form validation to complete before processing.
*/
function formSubmitBtn($event) {
/**
* Checks the validity of the form.
* Return if invalid; HTML5 validation errors should display.
*/
if (!$event.target.form.checkValidity()) {
return;
}
/**
* Form is client-side valid; taking over the remainder of processing.
*/
$event.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
/**
* Adds the token g-recaptcha-response token to our hidden form element.
* ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
*/
$event.target.form.elements['g-recaptcha-response'].value = token;
/**
* Use the form API directly to submit the form.
*/
$event.target.form.submit();
});
});
}
</script>
let siteKey = "...";
$("form").submit(function (eventObj) {
var myForm = this;
eventObj.preventDefault();
grecaptcha.execute( siteKey, {
action: "submit"
})
.then(function (token) {
$('<input />').attr('type', 'hidden')
.attr('name', "g_recaptcha_response")
.attr('value', token)
.appendTo(myForm);
myForm.submit();
});
});
This will execute recapcha, wait for response, add hidden attribute g_recaptcha_response to any form when browser try to submit it and then actually submit it. You need global variable siteKey
I was wanting the same behavior, but using the new recaptcha, the invisible one. After looking at some code and testing some stuff, I got into this. The main difference is that this uses the default browser validation as well:
var contact_form;
$(function() {
contact_form = $('#contact-form');
contact_form.submit(function (event) {
if ( ! contact_form.data('passed')) {
event.preventDefault();
grecaptcha.execute();
}
});
});
function sendContactForm(token) {
contact_form.data('passed', true);
contact_form.submit();
}
It basically stores the jquery form object in a global var, including, it uses sendContactForm as the callback, but when called by the recaptcha, it sets a data var named passed, which allows the form to not be prevented. It's exactly the same behavior as recaptcha would normally do, but with that condition.
Update: re-looking at my code right reminds me that it probably needs a way to restore data passed to false after grecaptcha's execution. Consider that if you'll implement this.
This solution is similar to solution by #PigBoT but with the addition of reportValidity() and is using ReCAPTCHA v3
Credit to https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131
<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
function contactOnSubmit(token) {
var contactForm = document.getElementById('contactUs');
if(contactForm.checkValidity()) {
//SERVER SIDE VALIDATION here,
//on success, contactForm.submit();
} else {
grecaptcha.reset();
contactForm.reportValidity();
}
}
</script>
Form (id="contactUs")
<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>
"Can I Use" site currently reports 97% uses have support for checkValidity() https://caniuse.com/?search=checkValidity
I have been doing some searching and most of what I find relates to the html <input type="submit"> buttons or connecting to a button by the ID tag. What I am trying to do is attach to every html5 <button> based off the NAME tag.
I have tried the following but when the button is clicked it submits and I don't get any alerts before it does.
$("button[name=Submit]").click(function ()
{
var error;
alert("test1");
if ($("#SelectedProjectCodes").val() == "") { alert("test"); }
return false;
});
Also tried
$("input[name=Submit]").click(function ()
{
var error;
alert("test1");
if ($("#SelectedProjectCodes").val() == "") { alert("test"); }
return false;
});
Here is the HTML for one of the submit buttons:
<button formnovalidate="formnovalidate" type="submit" name="Submit" value="Add Time">Add Time</button>
So how am I supposed to attach to all my html5 buttons?
Note: The JS in in an external file with about a dozen other functions which all work so I do know the JS file is being loaded properly and working. And all the code is in a $(document).ready(function ()
You need to prevent the default behaviour:
$("button[name=Submit]").click(function(event) {
event.preventDefault();
// other code
});
You must put the id in the button like below
<button formnovalidate="formnovalidate" id="id1" type="submit" name="Submit" value="Add Time">Add Time</button>
And also you jquery code sometime like this (call via button id)
$( "#id1" ).click(function() {
var error;
alert("test1");
if ($("#SelectedProjectCodes").val() == "") { alert("test"); }
return false;});
Hope it helps you...
How can I detect that a click event is fired on any checkbox on a page using jQuery? Please also note that on page load, may be checkbox(s) is/are not created but could be created on request. So HTML DOM will be updated in that fashion.
$(":checkbox").on("click", function(){
// your work
} );
also see bind
delegate
live
reference On
TRy this
$( document ).on( "click", "input[type='checkbox']", function() {
alert( "check box clicked" );
});
$(":checkbox").on("click", function(){
// ALL YOUR STUFF
} )
Simply create a function checkboxClick() as -
function checkboxClick() {
// ---
// your code goes here
// ...
}
Now for every checkbox (even when you add them dynamically) add attribute onclick like
<input type="checkbox" onclick="javascript:checkboxClick();" class="checkbox" />
Note : Since javascript works on existing dom elements, even if you do something like jQuery(".checkbox").click(function() {...});, it wont work on dynamicically added elements
$(document).on('click', ':checkbox', function() {
//your code
});