I'm building a Google Sheets-bound Apps Script add-on with a custom sidebar (FilterUI.html). I'm trying to factor out the JavaScript into a separate file (FilterUICode.html) and include it like this:
<script>
const raw = <?= JSON.stringify(include("FilterUICode")) ?>;
document.getElementById("dump").textContent = raw;
eval(raw);
</script>
i also tried escaping with '!'>
const raw = <?!= JSON.stringify(include("FilterUICode")) ?>;
In FilterUICode.html I have something very simple:
console.log("✅ Script loaded");
document.addEventListener("DOMContentLoaded", () => {
const dbg = document.getElementById("debug");
if (dbg) {
dbg.style.display = "block";
dbg.textContent += "✅ JS ran\n";
}
});
when i run my script, i consistently get:
Uncaught SyntaxError: expected expression, got '<'
There are no or HTML tags in the external FilterUICode.html, just plain JavaScript.
I've already tried:
Forcing clasp push --force from a clean local copy
Verifying include() uses HtmlService.createHtmlOutputFromFile(...)
Confirming that raw does not contain any leading <
Ctrl+F5 to bypass browser cache
Reloading the script editor manually
This setup is minimal and should work in principle. The bug only occurs when using include() + eval(...). If I inline the JS in FilterUI.html, everything works fine.
Has anyone encountered this or found a workaround?
-------------------- complete src for mini project----
----Code.js
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('🔧 Tools')
.addItem('Open Filter UI', 'showSidebar')
.addToUi();
}
function showSidebar() {
const html = HtmlService.createHtmlOutputFromFile('FilterUI')
.setTitle('Filter by Distance')
.setWidth(400);
SpreadsheetApp.getUi().showSidebar(html);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
---FilterUI.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
body {
font-family: sans-serif;
padding: 1em;
max-width: 400px;
box-sizing: border-box;
}
pre#debug {
background: #f0f0f0;
padding: 10px;
font-size: 12px;
white-space: pre-wrap;
margin-top: 20px;
border-radius: 4px;
}
</style>
</head>
<body>
<h2>Minimal Factored JS Test</h2>
<pre id="debug">[INIT] HTML loaded\n</pre>
<script>
const raw = <?= JSON.stringify(include("FilterUICode")) ?>;
document.getElementById("debug").textContent += "\n[DEBUG] Injected script length: " + raw.length;
try {
eval(raw);
} catch (err) {
console.error("❌ Eval failed:", err);
alert("❌ Error evaluating script: " + err.message);
}
</script>
</body>
</html>
---- FilterUICode.html
function log(msg) {
const dbg = document.getElementById("debug");
if (dbg) dbg.textContent += `\n[LOG ${new Date().toISOString()}] ${msg}`;
console.log(msg);
}
log("FilterUICode: TOP LEVEL start");
document.addEventListener("DOMContentLoaded", () => {
log("✅ DOMContentLoaded fired");
});
window.addEventListener("load", () => {
log("✅ window load fired");
});
log("FilterUICode: BOTTOM LEVEL end");
If my understanding of your expected result is correct, it is required to modify createHtmlOutputFromFile
to createTemplateFromFile
. So, please modify your showing script as follows and test it again.
Code.js
:function showSidebar() {
const html = HtmlService.createHtmlOutputFromFile('FilterUI')
.setTitle('Filter by Distance')
.setWidth(400);
SpreadsheetApp.getUi().showSidebar(html);
}
function showSidebar() {
const html = HtmlService.createTemplateFromFile('FilterUI').evaluate()
.setTitle('Filter by Distance')
.setWidth(400);
SpreadsheetApp.getUi().showSidebar(html);
}
FilterUI.html
:const raw = <?= JSON.stringify(include("FilterUICode")) ?>;
const raw = <?!= JSON.stringify(include("FilterUICode")) ?>;