After deploying my web app in Apps Script with bookmarklet, it works fine on my end however not when I tried to share my web app, it throws an 'unsafe javascript' error like its being clicked from the web app and not as a bookmarklet.
The web app is run as user accessing and can be accessed by anyone in our organization. What bookmarklet does is run a prompt for input and find it on the current page.
The code goes like this:
<a id="bkmark">Link</a>
<script>
document.body.onload=()=>{
google.script.run.withSuccessHandler(rep).getLink();
function rep(e){
document.querySelector('#bkmark').href = e; // return a javascript: IIFE wrapped in ``
}
}
</script>
And my gs returns Html output from a file. Any help appreciated, Thanks!
Instead of adding the JavaScript as a booklet, you can directly import using ContentService
and templates:
Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="<?= ScriptApp.getService().getUrl() + '?getAction=true' ?>" type="text/javascript" defer async></script>
</head>
<body>
<button id="trigger" onclick="doAction()" disabled>Click here to do something</button>
</body>
</html>
Code.js
function doGet(e) {
const { getAction } = e.parameter
if (getAction !== undefined) {
return genAction()
}
return HtmlService.createTemplateFromFile('Page').evaluate().setTitle("Booklet webapp")
}
function genAction() {
return ContentService
.createTextOutput(`
function doAction() {
alert("Hello world!")
}
(function loaded() {
document.getElementById("trigger").disabled = false
})();
`)
.setMimeType(ContentService.MimeType.JAVASCRIPT)
}
Notice that the script tag has defer
and async
. This makes it so the script doesn't start loading after the DOM has been fully loaded and it starts doing so asynchronously (without blocking the main thread).
On the Apps Script side, we use a template so we can dynamically get the script execution URL (with ScriptApp.getService().getUrl()
) and we add a query parameter (getAction
) so we can detect it on the doGet(e)
function. There we check for that parameter and we have it we return a JavaScript content instead.
Also made the button disabled by default and I added a small code so the button get enabled when the script has finished loading (it's asynchronous so it can take some time to fully load).
This technique can also be used to dynamically add an script tag by fetching the result, creating the script element, and adding the contents manually.