I'm trying to embed some JavaScript into the output of a Google Apps Script that is running as a web app, but I can't find any evidence of my script tags or jQuery loading in the output, so I think it is getting stripped out, I assume, by Caja.
I'm adding the JavaScript by creating an HTMLOutputObject from a file, like this:
app.add(app.createHTML(HtmlService.createHtmlOutputFromFile("order_form_javascript").getContent()));
Maybe it is worth mentioning here that the javascript is added this way in a serverHandler attached to a listBox change event - NOT in the initial doGet() function - I'm not sure if that makes a difference.
The content of the order_form_javascript.html file is:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<h3>Javascript!</h3>
<script type="text/javascript">
alert ("script ran");
$(function() {
alert ("function ran");
$('.order_table tr:hidden').show();
});
</script>
The H3 tags are in the output, but no script tags appear, no alert boxes pop up and jQuery is undefined.
I tried this code on the Caja playground and it seems to work. So I think that I must be inserting the javascript incorrectly, or missing something obvious.
Thanks, in advance, for any suggestions you can offer.
I'm not deeply familiar with Apps Script, but it looks like you are trying to mix Ui Service (add, createHTML) and Html Service in the same page. This is not supported — you must choose one or the other for the entire page.
A side note on troubleshooting: Caja never inserts the script you write in the DOM (doing so would break the sandbox). In NATIVE sandbox mode you may see <script> elements with empty or stub contents, though. So, the absence of scripts does not itself indicate a problem.
Following Kevin Reids tips that Caja wouldn't likely show script tags anyway, as this would break sandboxing, and also that HtmlService and UiService may be incompatible in the same script, I updated my code to the following:
var js = HtmlService.createHtmlOutputFromFile("order_form_javascript").getContent();
Logger.log(js); //check that HtmlService generates the script properly.
app.add(app.createHTML(js));
Looking in the logs, I can clearly see that HtmlService is returning my HTML file's content verbatim:
[13-07-24 10:02:28:879 BST] <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<h3>Javascript!</h3>
<script type="text/javascript">
alert ("script ran");
$(function() {
alert ("function ran");
$('.order_table tr:hidden').show();
});
</script>
Which makes me think that maybe I could use this method to output arbitrary HTML without writing it all in code, but I digress.
This lead me to the app.createHTML([String]) method and, according to the documentation here, HTML widgets can't actually contain <script> tags. So that's where they are being stripped out. It turns out I should have read the manual. How embarrassing.
The two possible solutions I can think of are;
Re-write my web app using HtmlService instead of UiService, which I think would allow more arbitrary html and scripts from HTML files in the project.
Since my JavaScript is going to be event driven and very simple, I could also use a clientHandler to perform the necessary actions and continue using the UiService for my web app.
I'm going to start with the clientHandler approach.
Related
I want to integrate LinkedIn sharing on my page.
Reading the documentation that LinkedIn provides here:
https://developer.linkedin.com/docs/getting-started-js-sdk
.. I was surprised to see they require this script tag in the head secion of my page
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: [API_KEY]
onLoad: [ONLOAD]
authorize: [AUTHORIZE]
lang: [LANG_LOCALE]
</script>
I don't get what is happening here. First of all, w3schools says that """Note: If the "src" attribute is present, the element must be empty.""" (https://www.w3schools.com/tags/tag_script.asp).
I also went here: https://html.spec.whatwg.org/multipage/scripting.html#the-script-element
(I'm not 100% sure how authoritative this is...but looks authoritative based on the format and length :P) - it also says there that if there's a src attribute, then the body should basically be empty - in any case - LinkedIn's script syntax is not explainable by these 2 resources.
So does anyone know what's up with the script body syntax? Are those JS labels? And if so, I don't get how they're used. I thought labels are used with continue/break statements, to get out of loops. I don't understand how LinkedIn's API can get information from me if I provide it in that syntax.
Is the script body somehow fed to the script, and it parses it itself?
Can someone please explain to me what's going on?
Thanks!
What you said is correct. When the src attribute is added, the body of the script is not executed. There is a way to get around this however. That's by retrieving the script tag, extracting the innerHTML and using eval on it. You will need to of course do that on document ready.
I don't know how LinkedIn does it exactly, but HTML standards didn't change for them nor the order of loading, so either they use something similar to what I explained or some more clever way of parsing the body of the script.
Other notes to consider: instead of using document ready event, you could bind that into your library. As in retrieve the last available script tag, and extract the body, at the time of load of your library, that will be the last element loaded either way, so you should be able to retrieve the code without using any events. (That would need testing, but DOM elements are loaded synchronously, top-down approach).
Obviously using eval is not recommended, its quite slow, but definitely provides the functionality you're looking for.
PS. Forgive any formatting errors. I'm typing this from my mobile, 2k miles away from home. Otherwise I'd be more than happy to even provide some sample code snippets and do the above testing myself.
I'm working on a project where we'd like to load external content onto a customers site. The main requirements are that we'd like the customer to have as simple of an include as possible (like a one-line link similar to Doubleclick) and would preferably not have to be involved in any server-side language. The two proposed ways of doing this were an iframe or loading a javascript file that document.write's out the content.
We looked more at the latter since it seemed to produce more reliable legibility and simplicity for the end user - a single line of Javascript. We have been hit with the reality that this will be indexed unpredictably by Google. I have read most of the posts on this topic regarding javascript and indexing (for example http://www.seroundtable.com/google-ajax-execute-15169.html, https://twitter.com/mattcutts/status/131425949597179904). Currenlty we have (for example):
<html>
<body>
<div class='main-container'>
<script src='http://www.other.com/page.js'></script>
</div>
</body>
</html>
and
// at http://www.other.com/page.js
document.write('blue fish and green grass');
but it looks like google indexes this type of content only sometimes based upon 'Fetch As Google' used in Google's webmaster tools. Since it does sometimes work, I know it's possible for this indexing to be ok. More specifically, if we isolate our content to something like the above and remove extraneous content, it will index it each time (as opposed to the EXACT SAME Javascript in a regular customer html page). If we have our content in a customer's html file it doesn't seem to get indexed.
What would be a better option to ensure that Google has indexed the content (remote isn't any better)? Ideas I have tried / come across would be to load a remote file in for example PHP, something like:
echo file_get_contents('http://www.other.com/page');
This is obviously blocking but possibly not a deal-breaker.
Given the above requirements, would there be any other solution?
thx
This is a common problem and I've created a JS plugin that you can use to solve this.
Url: https://github.com/kubrickology/Logical-escaped_fragment
Make sure to use the: __init() function instead of standard DOM ready functions and you know for sure that Google is able to index.
I am developing a Chrome application on a 3rd party website.
The document that I am trying to alter has the following page format:
<head>
.
.meta data here
.
</head>
<body>
<script type="text/javascript">
function a1(){
..contents of function a1
}
</script>
.
.other contents of body
.
<script type="text/javascript">
a1(); // <-- I don't want this to be executed
</script>
</body>
My problem is I want the entire page to be loaded, except the function call a1();
So I thought of a simple solution: BEFORE the function definition ie; function a1(){..}, I want to create a1 as a CONSTANT function which does nothing, therefore rendering the a1() call useless.
Problem is that if I define the function to be constant in my js which run_at document_start, the execution environment is different, hence it wont affect the page.
So the alternate to run in the same execution environment is by INJECT the code using innerHTML+=" ... "
Another alternate is to construct a script element using "createElement" and the src is an external js file which has to be loaded before execution.
Both the alternatives does not work, as the parse tree isn't created in the run_at document_start script.
So, I thought of an other solution. I added the listeners for DOMModifySubTree event, hoping to alter the parse sequence(I know, it sounds funny :)) so that the function isn't called. Doesn't help.
So, my question is how do I prevent the call to a1() function??
P.S - I cannot contact the 3rd party website developer.
Greasemonkey is what you're looking for.
It's a Firefox extension that injects your own scripts in webpages of your choice.
Visit wiki.greasespot.net to get started on writing scripts.
Fontunately, Chrome natively supports Greasemonkey scripts. See this page for more info.
Sorry for the late late reply. What I had originally done to skip the function call is that I ran a script at document start and injected it into the "document" (as JS runs in separate envi). In that injected script I used
const a1=function(){};
This way, when the function is being declared again, it is NOT overwritten, hence the function is not executed (ie; it is executing our dummy function), thus essentially breaking the code. :)
My issue is that multiple websites are going to include my JS file and when calling something like this:
<script src="..."></script>
hello.say("yay");
there going to be a race issue so sometimes it could make it sometimes not. i know that i can solve that easily by putting every function in a window.onload but that wouldn't be clean as i've seen other websites magically solve that like google analytics:
.. Calling google analytics JS..
<script type="text/javascript">
try{
var pageTracker = _gat._getTracker("UA-xxxxxx-x"); <-- this an object !
pageTracker._trackPageview();
} catch(err) {}
</script>
How to do that?
Google Analytics uses a trick that's a perfect example of something that can only be done in duck typed languages. The main object is an array if the GA-script hasn't loaded, but if it has it changes behaviour. Let's see if I can explain it.
I pulled this piece from the source here at stackoverflow:
var _gaq = _gaq || [];
_gaq.push(['_setAccount','UA-5620270-1']);
_gaq.push(['_trackPageview']);
It looks like an array with some values being pushed to it. In fact, if _gaq is falsy when this code is run (as it is if no other analytics-JavaScript has run yet), it as an array. Then, when the main analytics script (included with a script-tag anywhere on the page) loads it examines this array and performs some task based on the contents of the array.
So, if this happens in opposite order (the main script is loaded first, and then the snippet above) the main script set _gaq to an object with a push-method that does whatever google wants it to do. Later, when the code above runs, _gaq.push doesn't just add values to an array; it actually executes arbitrary code (and doesn't push to an array at all).
Hence, regardless of which script runs first, the end result will be the same when both have finished.
If you include your file in the header part of the html page, it will be loaded before any other javascript in the page is run.
UPD: Also, I think even files included in the text of thml are downloaded before the processing of the rest of javascripts. Are you sure that is your problem in the first place?
You will need to delay the execution of any javascript code that depends on the external javascript until after the page has been fully loaded and you can do that by attaching the execution to the window.onload event.
window.onload = function() {
hello.say('yay');
}
But this has the disadvantage of only working for one function and will override any functions that have been initially attached to that event. You would want to read Simon Wilson's post and the comments for solutions on how to handle this situation.
http://simonwillison.net/2004/May/26/addLoadEvent/
I'm a bit unsure as to what you think the race issue is here.
Browsers always* execute script tags in the order they see them. If this were not true, a good portion of the internet would break - as well as pretty much every common JavaScript library in existence.
So, as long as users load your script earlier in the document than when they call its APIs, you should have no problem. The only potential exception to this I can think of is if your script's initialization relies on asynchronous logic, in which case you should think about providing a standard callback mechanism of your own for users to write their code in.
If users might load your script AFTER the point they use its APIs, then the trick Jakob alludes to in his answer would work. But I'm not even sure you're aiming for that level of complexity.
*edit: I should note, to be perfectly honest, there are specific exceptions to this, but not so long as you're simply dealing with standard non-deferred usage of <script> tags within an HTML document.
I've been trying to get this sorted all day, but really cant figure it out. I've got a page with <div id="ajax"></div> that is populated off a tab based menu pulling in a snippet of HTML code stored in an external file that contains some javascript (mainly form validation).
I've seen in places that I need to eval() the code, but then on the same side of things people say its the last thing to do.
Can someone point me in the right direction, and provide an example if possible as I am very new to jQuery / JavaScript.
Many thanks :)
pulling in a snippet of HTML code stored in an external file that contains some javascript (mainly form validation).
Avoid doing this. Writing <script> to innerHTML doesn't cause the script to get executed... though moving the element afterwards can cause it to get executed, at different times in different browsers.
So it's inconsistent in practice, and it doesn't really make any sense to include script anyway:
when you load the same snippet twice, you'd be running the same script twice, which might redefine some of the functions or variables bound to the page, which can leave you in extremely strange and hard-to-debug situations
non-async/defer scripts are expecting to run at parse time, and may include techniques which can't work when inserted into an existing document (in the case of document.write this typically destroys the whole page, a common symptom when trying to load third-party ad/tracking scripts).
Yes, jQuery tries to make some of this more consistent between browsers by extracting script elements and executing them. But no, it doesn't manage to fix all cases (and in principle can't). So don't ask it to. Keep your scripts static, and run any binding script code that needs to happen in the load callback.
If you fetch html via Ajax, and that html has a <script> tag in it, and you write that html into your document via something like $('#foo').append(html), then the JS should run immediately without any hassle whatsoever.
jquery automatically processes scripts received in an ajax request when adding the content to your page. If you are having a particular problem then post some code.
See this link
dataType: "html" - Returns HTML as
plain text; included script tags are
evaluated when inserted in the DOM.