(Question edited for clarity and updates). I am building a flask app game in which the user inputs a guess in the web browser, the guess is checked on the back end (with python), and then the user is told if the guess is correct or incorrect. I also have some JavaScript running to display the previous guesses on the page, using local storage. These two code snippets are interfering with each other. In my play.html, I have:
<form action="/play" method ="POST">
<div class = "form-group">
<label>This is the input</label>
<label>
<input class ="form control" type="text" name="guess" placeholder="Type guess!">
</label></div>
<div class="container>"><button type="submit" id="submit">Check Answer</button></div>
Then, in app.py, I have:
# gets guess and checks against answers, flashes appropriate message
if request.method == "POST":
req = request.form
guess = req.get("guess")
if guess == ans.next():
flash("Correct", "success")
return redirect(request.url)
flash("Incorrect", "secondary")
return redirect(request.url)
Then, in my display_guesses.js, I have (along with a lot of other stuff for storing and displaying, which has no problems):
const input = document.getElementsByName('guess');
form.addEventListener('submit', function () {
displayGuesses.push(input[0].value);
localStorage.setItem('my_guesses', JSON.stringify(displayGuesses));
liMaker(input[0].value);
input[0].value = "";
form.submit();
});
In theory, this should display the guesses after they are submitted (and it does under certain conditions, see below).
The problem is that only one of these things can work at a time. If I run the code as-is, then the guesses get displayed on the page, but the answer-checking mechanism fails. If I remove the code for displaying guesses, then the answer-checking mechanism works.
It appears that there is some kind of conflict between having JavaScript and Python both trying to do something with the user's guess on the form. Any ideas on how to solve? I am very new to coding and building the plane as I fly it.
As already mentioned, python delivers the page and the browser receives and displays it. From then on, JavaScript runs and executes your instructions.
The problem arises in my opinion in your event listener for the submit event. You catch this event, but don't pay attention to the fact that the form is still submitted by default to the server. Now the browser tries to execute your code on the one hand and to send the form on the other hand. Take a look at the preventDefault() command to prevent the default behavior and run your code.
form.addEventListener('submit', function (event) {
event.preventDefault();
// ...
});
The second problem is that you try to change the content of the page and then submit the form, which results in a complete reload of the page, causing the post-injected JavaScript content to disappear. Don't add the saved guesses until the page reloads.
A possible solution could look like this.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Play</title>
</head>
<body>
<form method="post">
<div>
<label for="guess">This is the input</label>
<input type="text" name="guess" id="guess" placeholder="Type guess!" />
</div>
<button type="submit">Check Answer</button>
</form>
<ul id="guesses"></ul>
<script type="text/javascript" src="{{ url_for('static', filename='display_guesses.js') }}" defer></script>
</body>
</html>
(function() {
let elem, guesses = JSON.parse(localStorage.getItem('my_guesses')) || [];
elem = document.getElementById('guesses');
elem.innerHTML = guesses.map(guess => `<li>${guess}</li>`).join('');
elem = document.querySelector('form');
elem.addEventListener('submit', function(evt) {
evt.preventDefault();
const guess = this['guess'].value.trim();
if (guess != '') {
guesses.push(guess);
localStorage.setItem('my_guesses', JSON.stringify(guesses));
this.submit();
}
});
})();
At some point you will wonder how to remove the saved data from memory and probably run into another problem. You store your guesses inside the client. The server is the one who decides whether the assumption was correct. So at some point the server has to tell the client when to move on to a new round and discard the previous guesses. In order to solve this problem, you have to rethink your application structure and consider, for example, whether the page really needs to be reloaded or whether the storage location is the right one.
Detlef's solution above seems to work, but so does just making sure that what is inputted in the form is actually being caught; solution below
const form = document.querySelector('form');
const ul = document.getElementById('show_guesses');
const input = document.getElementsByName('guess');
let displayGuesses = localStorage.getItem('my_guesses') ? JSON.parse(localStorage.getItem('my_guesses')) : [];
const liMaker = (text) => {
const li = document.createElement('li');
li.textContent = text;
ul.appendChild(li);
}
form.addEventListener('submit', function () {
displayGuesses.push(input[0].value);
localStorage.setItem('my_guesses', JSON.stringify(displayGuesses));
liMaker(input[0].value);
});
data.forEach(guess => {
liMaker(guess);
});
button.addEventListener('click', function () {
localStorage.removeItem('my_guesses');
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
displayGuesses = [];
});
The main thing was deleting the input[0].value=" "; which was passing an answer consisting of nothing to be checked agains what's on the backend.
Play.html, not including a bunch of other code that currently has a "clear guesses" button:
<div class="container">
<form action="/play" method ="POST">
<div class = "form-group">
<label><b>What's on your Bookshelf today?</b></label>
<label>
<input class ="form control" type="text" name="guess" placeholder="Type your guess here!"></label></div>
<div class="container>"><button type="submit" class="btn btn-secondary" id="submit">Check Answer</button></div>
<div class="container">
<p><b>Your Guesses So Far</b></p>
<ul id="show_guesses"></ul>
</div>
(Edited to remove some irrelevant code that is related to other things going on in the app, but not to the question at hand).
Related
Simplified the code as much as possible. How are the button clicks counted?
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Example File 3</h1>
<form action="<?= url ?>" method="GET">
<input type="text" name="update" />
<input type="submit" name="Submit" /><br> <!-- count clicks -->
<span><?= update ?></span>
<span><?= count ?></span>
</form>
</body>
</html>
The Code.js file:
var clicks = 0;
function doGet(e) {
Logger.log(JSON.stringify(e));
var htmlOutput = HtmlService.createTemplateFromFile('ExampleFile3');
if (!e.parameter['update']) {
htmlOutput.update = '';
}
else {
htmlOutput.update = 'updated value is: ' + e.parameter['update'];
htmlOutput.count = 'count is: ' + clicks;
clicks++
}
htmlOutput.clicks = clicks;
htmlOutput.count = clicks;
htmlOutput.url = getUrl();
return htmlOutput.evaluate();
}
function getUrl() {
var url = ScriptApp.getService().getUrl();
return url;
}
the increment isn't increasing, that I see, and the output is squished together. Looking to count the number of form submissions.
Here is a sample code for creating an oversimplified web app. It shows one way to count button clicks using JavaScript.
The client-side code is added to the HtmlOutput object by using the HtmlService.HtmlOutput.append method.
The global variable is set in the client-side instead of the server-side. Please bear in mind that reloading the web app will reset the global variable.
In <form> uses the attribute onsubmit instead of the attributes action and method. This attribute calls a client-side JavaScript function that updates the client-side global variable and display it in <div>.
Please note the use of event.preventDefault(). This is required when submitting html forms in Google Apps Script web apps because the form is hosted in a special Google service that cannot be modified by the Google Apps Script web app developers.
function doGet(e) {
return HtmlService.createHtmlOutput()
.append(`
<form onsubmit="handleFormSubmit()">
<input type="text" name="something"><br>
<button type="submit">Submit</button>
</form>
<div>0</div>
<script>
var clicks = 0;
function handleFormSubmit(){
event.preventDefault();
document.querySelector('div').innerHTML = ++clicks;
}
</script>
`);
}
Notes:
The web browser console will show several warnings. You can ignore them
The web browser console will show the following error:
Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
at gas-hub.js:25:10
For the purpose of this oversimplified example, you can ignore it. To avoid this error, the HtmlOutput object should include a "full" HTML5 structure:
function doGet(e) {
return HtmlService.createHtmlOutput()
.append(`
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form onsubmit="handleFormSubmit()">
<input type="text" name="something"><br>
<button type="submit">Submit</button>
</form>
<div>0</div>
<script>
var clicks = 0;
function handleFormSubmit(){
event.preventDefault();
document.querySelector('div').innerHTML = ++clicks;
}
</script>
</body>
</html>
`)
.setTitle('Demo')
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
To send <form> values to the server side, as well for other type of values, without reloading the web app, use google.script.run to call a server side function as is explained in https://developers.google.com/apps-script/guides/html/communication.
You also can use google.script.run to call server side functions on client-side event like clicking button. A very simple way is by using a button (<button> or <input type="button"> with an onclick attribute).
<button onclick="google.script.run.myFunction();">Run</button>
Also it's possible to use it with <form>'s onsubmit attribute and buttons of type submit but you have to include event.preventDefault() to avoid the page redirection cause the form submit action.
Please bear in mind that the use of event attributes is discouraged. Instead set the function to be called on the event by using HtmlElement.addEventListener whenever be possible.
To store a value persistently, do not use server-side global variables, instead consider to use the Properties Service among other options (see the related questions).
Here is another example, now using the "full" HTML5 structure, the Properties Service to persistently keep the number of button clicks. Instead of using the attribute onsubmit it uses the <form>attributes action and method but also use <base> to set the elements target to `"_top". This is required to properly reload the web app when the form is submitted.
A hidden <input> is used to pass a value to control when the click counter should be increased, this way we prevent that following the link (without the clicked=yes url parameter) increase the counter. Please note that no JavaScript is included on the client-side (HtmlOutput).
function doGet(e) {
const props = PropertiesService.getScriptProperties();
const name = "clicks";
let clicks = Number(props.getProperty(name));
if (e && e.parameter && e.parameter.clicked === 'yes') {
props.setProperty(name, ++clicks);
}
return HtmlService.createHtmlOutput()
.append(`
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form action="${ScriptApp.getService().getUrl()}" method="GET">
<input type="hidden" name="clicked" value="yes">
<input type="text" name="something" value="default value"><br>
<button type="submit">Submit</button>
</form>
<div>${clicks}</div>
</body>
</html>
`)
.setTitle('Demo')
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
If you are on your way to create a production class web application, please be aware that the client-side code is "satinized" and there are some limitations about what can done compared to other platforms. Please study the references to learn the basics about this.
Related
How to define global variable in Google Apps Script
How do I know which button was clicked in the Google Apps script?
References
https://developers.google.com/apps-script/guides/web
https://developers.google.com/apps-script/guides/html
I am attempting to create a couple of web pages that will allow me to fill out a form on input.html and have the entered data appended to a different HTML file, index.html.
I have been searching for an answer for a couple of hours now and it might just drive me insane!
Here is some example code of what I'm trying to do:
HTML form input.hmtl:
<form>
<label>
Enter something:
<input type="text" id="userinput" required>
</label>
<input type="submit" id="submit" value="submitted">
</form>
Javascript to get entered data and pass to index.html:
var userInput = document.querySelector("#userinput");
var submit = document.querySelector("#submit");
function addToIndex()
{
// create new element on index.html
}
submit.addEventListener("click", addToIndex, false);
HTML output file index.html:
<div id="contentstart">
<!-- newly created element here -->
</div>
I have attempted using this solution, but Chrome's console gives me an error and tells me that newWindow is not a function. I just stumbled upon using the <iframe> element in a couple of answers but don't quite understand it yet (I'm a noob).
Thanks in advance!
The best option is to use a web server or a serverless implementation. Server code can be written in multiple languages. Some of the languages include PHP, NodeJS, and ASP.NET.
However, you can pass data using browser storage.
But, browser storage is not secure and can be wiped at any time by the user. If you are storing information such as passwords or data that should be visible to multiple users, you should use a web server and/or database.
You need to have a script on both pages. The page with the form will store/set the data. The index page will retrieve the data and use javascript to render more content.
function addToIndex()
{
localStorage.setItem('input', userInput .value)
}
The script for the index page would look something like this.
var data = localStorage.getItem('input');
if (input) {
document.querySelector('#contentstart').innerHTML = data;
}
I put together a simple demo here.
http://plnkr.co/edit/iAitGxtdsHwXowNg
For you to receive data from a form in a different file, you will need a server-side language like php.
So the form will have this structure
<form action="external_file.php" method="get">
<label>
Enter something:
<input type="text" id="userinput" name="user_input" required>
</label>
<input type="submit" id="submit" value="submitted">
</form>
Note the action="external_file.php and the name="user_input" attribute added to the input element.
Then the file: external_file.php might have the following structure to receive the content from the form
<?php
$input = $_GET["user_input"];
//do something with $input
echo 'The data you entered is: ' . $input;
?>
I showed you the way to start. The rest is up to you, you can do whatever you want to do. I hope you could help.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form id ="form">
<label>
Enter something:
<input type="text" id="userinput" required>
</label>
<input type="submit" id="submit" value="submitted">
</form>
<script>
let form = document.getElementById("form");
let userInput = document.getElementById("userinput");
form.addEventListener("submit", submitForm) //this function will work when the form is submit
function submitForm (e) {
e.preventDefault() // this event will prevent page refresh when form submit
let userInputValue = userInput.value; // userinput value
console.log(userInputValue);
moveToAnotherPage(userInputValue); //we send the value we get from userinput to use in another function.
}
function moveToAnotherPage (value) {
// Select the index2 html elements and add with innerhtml or something.
//to do this, you may need to save the userinput value you received to localStorage and pull it from it. I suggest you look into localStorage .And if you know php, you can use it.
}
</script>
</body>
</html>
Let me Clear what title means:
In my code for a validation purpose of one field dependent on field "t1" I need to auto submit my form once (Just Once). But my below code is submitting it infinite times and I know the reason why its happening.
I guess Reason is everytime the form submits again JS in header runs. Please help me avoid this. Following is my code:
<html>
<head>
<script>
window.onload = function()
{
var f = document.getElementById("CheckForm");
var temp = document.getElementById("CheckForm.t1");
if(f.name == "CheckForm")
{
var temp1 = document.getElementById("t1");
temp1.value = "Task";
}
document.CheckForm.submit();
}
</script>
</head>
<body>
<form name="CheckForm" id="CheckForm" method="Post">
<input type="text" id="t1" name="t1"/>
</form>
</body>
</html>
I tried stopping it using variable like flag and static variables like arguments.callee.count = ++arguments.callee.count || 1 and placing my CheckForm.submit() line in if clause. But nothing worked. Any advice or help is appreciable.
<html>
<head>
<script>
window.onload = function()
{
var f = document.getElementById("t1");
var temp = document.getElementById("CheckForm.t1");
if(f.name == "CheckForm")
{
var temp1 = document.getElementById("CheckForm.t1");
temp1.value = "Task";
}
if(window.location.search=="")document.CheckForm.submit();
}
</script>
</head>
<body>
<form name="CheckForm">
<input type="text" id="t1"/>
</form>
</body>
</html>
Surely your form is more complex than:
<form name="CheckForm">
<input type="text" id="t1">
</form>
That will not submit anything to the server since there are no successful controls (the only control doesn't have a name).
Since the form is just submitting to the same page, you can submit a hidden value like:
<form name="CheckForm">
<input type="text" id="t1">
<input type="hidden" name="hasBeenSubmitted" value="yes">
</form>
Now when the form submits the URL of the new page will include ...?hasBeenSubmitted=yes so you can look for that value in the URL, e.g.
if (/hasBeenSubmitted=yes/.test(window.location.search)) {
// this page loaded because the form was submitted
}
If it exists, don't submit the form again.
So since you are using a post method the easiest way's to handle this is to ubmitted to a new url , however you seem set on keeping the form submitted to the same url in which case is you are using php (or really any other language) you can check if the http request has a post attribute with a value t1
<?php
if(isset($_POST['t1']){
$your_text=$_POST['t1'];
/*do some string checking to make safe and the throw into your database or mdo whatever you please with data
if you wanted to include the ip address of the user you can get a basic and most likely client ip address like so
$ip_address= $_SERVER['REMOTE_ADDR'];
if you are handing a mulitpage form look into php session or similar tech ... cookies is kind of over kill for this scenario
then include a succes page as the form has been submitted
or you could end php with this tag ?> and then have your html and start again with <?
*/
include 'form_submitted.php';
}else{
//this would be the html page that you included in your question and can be handle in same ways as form submitted
include 'my_form.php'
}
?>
Ip address may not be best included as it would stop 2 user from filling out the form if they are in the same LAN for eg. 2 people in same office or same house (if your page is acttual on the worldwide web).
I would take a look at #RobG answer as it he is basically suggesting the same type of thing with a get instead of post
ANyways hope this helps
I am a noob to programming, so I'd appreciate any advice from you more knowledgeable folks out there. I am working on a bit of javascript for a web page and I need the javascript to print to that current HTML page, preferably in the div tag I have set up for that purpose. Here's what I have so far:
<html>
<head>
<title>Tardy Reporting</title>
<script src="students.js" type="text/javascript">
</script>
</head>
<body>
<h1>Scan in Student ID</h1>
<form method="POST" name="idForm" onSubmit="getId(parseInt(document.idForm.studentId.value));">
<input type="text" name="studentId" id="studentId"/>
<input type="Submit" name="Submit" />
</form>
<div id="div1"></div>
<p>
</body>
</html>
and my JS file:
var studentNumberArray = [50011234, 50012345, 50013456];
var studentNameArray = ["Mike Simpson", "Greg Pollard", "Jason Vigil"];
var studentLastPeriodArray = ["George Washington", "Darth Vadar", "Obi Wan Kenobi"];
var tardyArray = [0, 0, 0];
function getId(studentId) {
for (i = 0; i < studentNumberArray.length; i++){
if(studentId === studentNumberArray[i]){
tardyArray[i] += tardyArray[i] + 1;
document.getElementById('div1').innerHTML='test';
}
}
}
Mind you, this is just the basic framework, so it's not nearly done yet, but the thing that is bugging me is that it'll go through the code correctly and print it out, but the result only lasts a fraction of a second on my browsers (chromium and firefox). Any help would be appreciated.
Here is an easier/better way to accomplish what you are trying to do
var students = {};
// Add students to object
students[50011234] = { 'id': '50011234', 'name':"Mike Simpson", 'lastPeriod':"George Washington", 'tardy':0 };
students[50012345] = { 'id': '50012345', 'name':"Greg Pollard", 'lastPeriod':"Darth Vadar", 'tardy':0 };
students[50013456] = { 'id': '50013456', 'name':"Jason Vigil", 'lastPeriod':"Obi Wan Kenobi", 'tardy':0 };
function getId(studentId) {
students[ studentId ].tardy += 1;
document.getElementById('div1').innerHTML='test';
}
Also, as pointed out below, you should change your button to not submit if that is not what you are intending to happen:
<form method="POST" name="idForm">
<input type="text" name="studentId" id="studentId"/>
<input type="button" onclick="getId(parseInt(document.idForm.studentId.value));" name="Mark Tardy" />
</form>
The reason why you see it only for a fraction of a second is that you are actually causing a submit. A submit is a full call back to the server which returns the page to its initial status.
To fix this simply make the function call on the onclick event of the button:
<html>
<head><title>Tardy Reporting</title>
<script src="students.js" type="text/javascript"> </script>
</head>
<body>
<h1>Scan in Student ID</h1>
<form method="POST" name="idForm" >
<input type="text" name="studentId" id="studentId" />
<input type="button" onclick="getId(parseInt(document.idForm.studentId.value));" value="submit" />
</form>
<div id="div1"></div>
<p>
</body>
</html>
What do you mean by "result"? It appears that you are setting the innerHTML of div1 to "test" over and over again.
Perhaps you mean to write
document.getElementById('div1').innerHTML += 'test';
Doing this is not efficient and it is preferable you concatenate on a string, or even better, join an array, before assigning the innerHTML.
but the result only lasts a fraction of a second on my browsers (chromium and firefox).
That is because you are submitting the page, so the page gets refreshed. You need to change the button type to button from submit. Also add a onclick to the button and call the js function getId
Forms are a special construct that allows communication with a server:
When a form is submitted, the form data is "POSTED" to a server via an HTTP request.
Typically, the browser displays the server's response as a new web page.
Forms use the action attribute to specify which server page should process the request
In your case, no action is specified, so the form POSTS to the current page, which is equivalent to refreshing the page. This means that all client-side (JavaScript) changes are wiped out, which is why you only see them for a split-second.
To achieve your desired result, change the input type from submit to button:
<input type="button" onclick=".." value="submit" />
Ideally, the student data exists in a database that is manipulated by code on a server. Your form would POST a request that returns an HTML page containing the desired data.
References
HTTP
Forms
I had to take my working example here. For some reason, it does not work as easily as the initial example.
New Example
Suppose I want to see M5s every time the page loads. So how can I fire the same query for M5 every time the page load?
I copied the critical part here:
<body>
<div id="search">
<form onSubmit="makeRequest(1); return false;" style="margin: 2px; padding: 2px; font-size: 1.2em;">
<input id="searchinput" type="text" name="tags" size="20" value="">
<input id="searchbutton" type="button" onClick="makeRequest(1);" value="Create VideoWall"><br />
...
</form>
</div>
Response to the idea in MiffTheFox's and Tom's reply
So I added the command before the form above:
<body onload="document.getElementById('myform').submit();">
However, the wall stays black. It should be full of M5s.
Emerged problem to the initial Question: Why does it not work? Why does the wall stay black?
makeRequest asked by Tom
function makeRequest(page){
startrequest = 0;
for(i =1; i < 4; i++){
clearList('ul'+i);
var tags = encodeURI(document.getElementById('searchinput').value);
if(i == 1 || i == 2){
quantity = 45;
}
if(i == 3){
quantity = 36;
}
insertVideos('ul'+i,'search',tags,quantity,startrequest);
startrequest = startrequest + quantity;
}
}
Please, see the url at the top and press CTRL+U to see the code.
Well, thereĀ“s on load attribute inside the body element
<body onload = "javascript:doSubmit()">
...
</body>
<script>
function doSubmit(){
var form = document.getElementById("myform");
if (form !=null)
form.submit();
}
</script>
Also, you could add javascript at the end of your html page. This is not as portable as the first option
<html>
<body>
<form id="myForm" ...>
...
</form>
<script>
//this executes when the page finishes loading
var form = document.getElementById("myForm");
if (form!=null) form.submit();
</script>
</body>
</html>
First add an ID to the form, then add an onLoad handler that submits it.
<body onload="myForm.submit();">
<form id="myForm" name="input" action="form_action.asp" method="get">
...
Not sure what you're trying to accomplish, but you can certainly use jQuery to do
$(document).ready( function() {
$("#submitButton").click();
});
The problem is ensuring that this only happens the first time the document is submitted; you will need to keep track of that on the server-side and remove the submission code after the first time.
A better approach is probably to compose your HTML on the server side so that whatever initial state you want to display is displayed. Many web applications have a form to submit a query of some kind (say, a search) but start with some initial sample result below the form. This is just created on the server side before loading, not by "pre-submitting".