How Do I Correctly Set a getElementById Object? - html

I have some JavaScript in which I set a global variable to hold the function document.getElementById. In a function in the same file, I then try to use that variable, along with the id of an HTML paragraph element, to write to the innerHTML property. However, in the IE11 console, I get the error "SCRIPT65535: Invalid Calling Object". Explicitly writing document.getElementByID("someid").innerHTML = "value" works. Here are the key parts of the code (all in the same file).
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<script>
var objDocGEBI = document.getElementById;
function writeData() {
if (true) {
objDocGEBI("name1").innerHTML = "value";
}
}
</script>
</body>
</html>

Your problem is to do with function binding.
The short version, is you need to bind the function to the document like so:
var objDocGEBI = document.getElementById.bind(document);
This will make sure that it is correctly bound to the document without actually running the function. Once you fix this line, you should find that the rest of your code above works as intended.

Because, you need to call your function for executing the innerHTML function.
And you cannot using your syntax. You need to write your document.getElementById; like that :
The first way :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<script>
var objDocGEBI = document.getElementById('name1');
function writeData(){
if (true){
objDocGEBI.innerHTML = "value";
}
}
writeData(); // it's calling your function and execute your innerHTML
</script>
</body>
</html>
The second way :
// Example with IIFE
(() => {
var objDocGEBI = document.getElementById('name1');
if (true){
objDocGEBI.innerHTML = "value";
}
})()

This may be useful for you:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<p id="name2"></p> <!-- added, just for a 2nd example -->
<script>
function writeData(id, value){
document.getElementById(id).innerHTML = value;
}
writeData('name1', 'John');
writeData('name2', 'Peter');
</script>
</body>
</html>
It's much cleaner this way, and you won't have to keep that weird variable. ;)

It's because your variable objDocGEBI is storing a method/function in the syntax of an event listener, but it's not an event listener. Correct syntax would be:
function writeData(name) {
if (true) {
document.getElementById(name).value = "value";
}
}

Related

How do I pass a variable from GAS to HTML and back?

I am using GAS to create a web app. I have a doGet that generates the HTML page for the web app and a button on the web app that triggers a script. When I create the HTML page, there is a variable that I need to send to the web app that is then sent back to the script. That variable isn't used in the HTML page other than just transferring it.
Here is a minimal example of what I want to do:
My doGet() creates the HTML and passes the variable foo to the page
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile("page");
var foo = "12345";
htmlOutput.foo = foo;
return htmlOutput.evaluate().setTitle('Sample');
}
The HTML page has a button that, when clicked, should pass the variable foo back to GAS to run the function checkOut
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id="btn" onclick="doStuff()">Click Here</button>
<script>
function doStuff(){
google.script.run.checkOut(foo);
}
</script>
</body>
</html>
In this example, checkOut just displays foo
function checkOut(foo){
Logger.log(foo);
}
I don't want foo displayed anywhere on the HTML page, but what should I add in order to get it sent back to GAS?
TIA
I believe your goal is as follows.
You want to use a value of foo in the function of checkOut at Google Apps Script side.
From That variable isn't used in the HTML page other than just transferring it. and I don't want foo displayed anywhere on the HTML page, you want to achieve this without including the value of foo in the HTML data.
In this case, how about the following modification?
Modified script 1:
In this modification, the value of foo is used with the scriptlets. This has already been mentioned in the comment.
In this method, the value of foo is shown in the HTML data like google.script.run.checkOut("12345"). I'm worried that this might not your expected situation from I don't want foo displayed anywhere on the HTML page. How about this?
Google Apps Script side:
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile("page");
var foo = "12345";
htmlOutput.foo = foo;
return htmlOutput.evaluate().setTitle('Sample');
}
function checkOut(foo){
Logger.log(foo);
}
HTML & Javascript side:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id="btn" onclick="doStuff()">Click Here</button>
<script>
function doStuff(){
google.script.run.checkOut("<?!= foo ?>");
}
</script>
</body>
</html>
Modified script 2:
In this modification, the value of foo is used as the background side (Google Apps Script side).
In this method, the value of foo is not shown in the HTML data.
Google Apps Script side:
function doGet(e) {
var foo = "12345";
PropertiesService.getScriptProperties().setProperty("sampleKey", foo);
var htmlOutput = HtmlService.createHtmlOutputFromFile("page");
return htmlOutput.setTitle('Sample');
}
function checkOut(){
var foo = PropertiesService.getScriptProperties().getProperty("sampleKey");
Logger.log(foo);
}
HTML & Javascript side:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id="btn" onclick="doStuff()">Click Here</button>
<script>
function doStuff(){
google.script.run.checkOut();
}
</script>
</body>
</html>
References:
HTML Service: Templated HTML
Properties Service
SUGGESTION
As what the TheWizEd has commented, you can also achieve your goal using the withSuccessHandler(). You may check this tweaked script below using a different implementation:
Code.gs
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile("page");
return htmlOutput.evaluate().setTitle('Sample');
}
function setupFoo(){ //Define a value for the "foo"
return "12345";
}
function checkOut(foo){
Logger.log(foo);
}
page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id="btn" onclick="doStuff()">Click Here</button>
<script>
function onSuccess(foo) {
google.script.run.checkOut(foo); //on Success will pass back the foo value to GAS function "checkOut"
}
function doStuff(){
google.script.run.withSuccessHandler(onSuccess).setupFoo(); //Run "setupFoo" function & pass its returned value to the "withSuccessHandler" function named "onSuccess"
}
</script>
</body>
</html>
Demonstration
After clicking the Click Here button on the web app, checkOut will log the foo value:

GoogleSheets google.script.run always going to FailureHandler

I am using GoogleSheets HTMLService. I am calling google.script.run from my Html page's script. But it is always going to FailureHandler. What is wrong in it? Please see the code below. When I run it, it always shows the alert Failed. Also, the logger does not show any error. It is also not showing the console log "Inside Hello" in the hello() function. Do we also need to do some browser settings (I am using chrome - javascript allowed).
[UPDATED]
After replacing Logger.log with console.log, I am seeing it as Transport Error.
modeDialog.gs
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile("test");
html.setWidth(90).setHeight(1);
var ui = SpreadsheetApp.getUi().showModalDialog(html, "Opening ..." );
}
function hello() {
console.log("Inside Hello");
return "hello";
}
test.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function onSuccess(str) {
window.alert("executed");
}
function onFailure(error) {
window.alert("failed");
Logger.log(error);
}
google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onFailure).hello();
</script>
</head>
<body>
<div id="failureContent"></div>
Hello world
</body>
</html>
As I mentioned that it was working earlier in Chrome and is currently working in FireFox, I tested it again after changing my chrome settings to default by going to Chrome > Settings > Advanced > Reset and clean up > Restore settings to their original defaults.
It is working fine after that. So setting this as the answer.
I ran it this way:
GS:
function openDialog() {
SpreadsheetApp.getUi().showModalDialog(HtmlService.createHtmlOutputFromFile("ah3"), "Opening ..." );
}
function hello() {
return "hello";
}
HTML:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Hello world
<script>
window.onload=function(){
google.script.run
.withSuccessHandler(function(str){window.alert("executed");})
.withFailureHandler(function(error){window.alert("failed");})
.hello();
}
console.log('MyCode');
</script>
</body>
</html>
I just like to use onReadyState function or onload to run most javascript so that html is already loaded. Not that it makes much difference in this trivial example. Also I tend to put the scripts in the body rather than in the head.

Calling for multiple onclick functions?

I have been looking for a solution for this for a long time and I just could not find what I was looking for. I have made a code that is an automatic traffic light and it loops through. How can I change it so when I click it goes through the sequence and stops when it gets back to Red?
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
</head>
<body onload="startTime();">
<img id="img1" />
<script>
var imgArray = new Array("Red.jpg","RedA.jpg","Green.jpg","Amber.jpg");
var imgCount = 0;
function startTime() {
if(imgCount == imgArray.length) {
imgCount = 0;
}
document.getElementById("img1").src = imgArray[imgCount];
imgCount++;
setTimeout("startTime()", 1000);
}
</script>
</body>
</body>
</html>
You just need to change setTimeout function syntax.
setTimeout(function(){
startTime();
},1000);
Check working example

Converting from NATIVE to IFRAME sandbox

I have a large application that I want to convert from NATIVE to IFRAME sandbox now that NATIVE is deprecated. The general flow of the application is as follows: The user fills out a form on the beginning page and presses a Begin button. The beginning page is then hidden, and based upon values from the first page, the user is then shown a new page. My problem when using IFRAME is that the new page is never shown. It works as expected in NATIVE mode. I have created a simplified script that exhibits the problem. Please help me understand what I am forgetting or doing wrong.
Code.gs
function doGet() {
Logger.log('enter doget');
var html = HtmlService.createTemplateFromFile('BeginHeader').evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
return html;
}
function include(filename) {
Logger.log('enter include');
Logger.log(filename);
var html = HtmlService.createHtmlOutputFromFile(filename).getContent();
Logger.log(html);
return html;
}
Javascript.html
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script
src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js">
</script>
<script
src="https://apis.google.com/js/api.js?onload=onApiLoad">
</script>
<script>
function showForm(hdr) {
console.log('enter showform');
console.log(hdr);
console.log('hiding first page');
document.getElementById('beginDiv').style.display = 'none';
var el = document.getElementById('recordDiv');
el.innerHTML = hdr;
console.log('showing new page');
el.style.display = 'block';
}
function oops(error) {
console.log('entered oops');
alert(error.message);
}
</script>
<script>
$(document).ready(function() {
console.log('begin ready');
$("#beginForm").submit(function() {
console.log('enter begin submit');
//console.log('hiding first page');
//document.getElementById('beginDiv').style.display = 'none';
console.log('including page 2');
google.script.run
.withSuccessHandler(showForm)
.withFailureHandler(oops)
.include('Page2');
});
});
</script>
BeginHeader.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div id="beginDiv" style="display:block">
<p>Click on Begin. </p>
<form id="beginForm">
<input type="submit" value="Begin">
</form>
</div>
<!-- results of content being filled in -->
<div id="recordDiv"></div>
<?!= include('Javascript'); ?>
</body>
</html>
Page2.html
<!DOCTYPE html>
<html>
<body>
<p> This is page 2. </p>
</body>
</html>
There is no point in ever using a button of the "submit" type, unless you want to force the form to make an HTTP Request, and reload the application. That's what a "submit" type button does. It causes the page to be reloaded. The "submit" type button is meant to work together with a form in a certain way. It causes a GET or POST request to happen. That's what the problem is. So, you'll need to reconfigure things a little bit.
Just use a plain button.
<input type="button" value="Begin" onmouseup="gotoPg2()">
I created a gotoPg2() function to test it:
<script>
window.gotoPg2 = function() {
console.log('enter begin submit');
//console.log('hiding first page');
//document.getElementById('beginDiv').style.display = 'none';
console.log('including page 2');
google.script.run
.withSuccessHandler(showForm)
.withFailureHandler(oops)
.include('Page2');
};
</script>
If you use that, they you don't need the $(document).ready(function() { etc. code anymore. And, if you don't need that code, then you don't need to load jQuery.
Unless you are using jQuery for other things, then you don't need:
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script
src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js">
</script>
The NATIVE mode was probably blocking the intended usage of the "submit" request. That's why the code in NATIVE was working. IFRAME allows things to work as they are built and intended to work, which means that the page was probably trying to be reloaded, and an error was occurring. I was getting a 404 page error in the browser console.

trasferring the Array created in the html page to another page

I need help to transfer an array from one page to another page. Following is the code that I created with a list of names.
In another papge, I want to this array and also print them out. I am wondering whether I could get this array by javascript. Thank you so much!
I have seperated two paragrahs of code so that you can easily read them. Thanks a lot!
<html>
<head>
<title>ys</title>
</head>
<body>
<script type="text/javascript">
var aName = new Array;
aName[0] = "daniel";
aName[1] = "zhang";
aName[2] = "alex";
aName[3] = "yang";
aName[4] = "Amy";
aName[5] = "Wang";
aName[6] = "Vincent";
aName[7] = "Lee";
for (i=0; i<8; i++)
{
document.write(aName[i] + "<br>")
}
</script>
</body>
</html>
<html>
<head>
<title>get the array from s.html and print out</title>
</head>
<body>
<script type="text/javascript">
for (i=0; i<8; i++)
{
document.write(aName[i] + "<br>")
}
</script>
</body>
</html>
Ah, life-cycle management. Is it OK to send it as a parameter? For example,
https://www.google.com/search?q=weather
If yes, see https://stackoverflow.com/a/9146311/227646 which says
Encode the array as a request parameter.
You should turn your array into a URI encoded string, so probably passing through something like JSON first.
Then decode the parameter in the www.testpage.faces.html
How to get "GET" request parameters in JavaScript?
If no (sensitive information), we will have to store it in the browser somehow. Perhaps a cookie?
You can use an external js file if your array is hard-coded.
somefile.js
var aName = ["daniel","zhang", "alex", "yang", "Amy", "Wang", "Vincent", "Lee"];
Html
<html>
<head>
<script type="text/javscript" src="somefile.js"></script>
</head>
<body>
<script type="text/javascript">
for (i = 0; i < aName.length; i++)
{
document.write(aName[i] + "<br>")
}
</script>
</body>
</html>
Other ways: If you use modern browser you can user localStorage for storing big string and split it every time by divider.
Or using cookies but it's ugly.