You need to use a different name for that function.
translate()
is already an existing method used for 2D Canvas drawings.
The translate() is a method of a 2D drawing context. The translate(x,y) method moves the canvas and its origin x units horizontally and y units vertically. source.
Solution
Avoid this by defining a JS event handler property with:
document.querySelector("input[type=button]").addEventListener("click", translate)
// or
jQuery('input[type="button"]').on("click", translate)
which will create a handler with a more predictable (probably global) scope. For best practice, also use event delegation.
Explanation
The lexical scope of a handler function defined as an html element attribute already has a translate
property.
JS functions are bundled together with references to their surrounding state (the lexical environment). When you set an HTML attribute as a string of js code, you are implicitly defining a function within the scope of the HTML element.
Your <input>
element will inherit properties from these interfaces: HTMLElement
-> HTMLInputElement
-> Node
-> Event Target
. We can check to see if any of them have a translate
property:
console.log(Object.getOwnPropertyNames(EventTarget.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(Node.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(HTMLInputElement.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(HTMLElement.prototype).includes("translate"))
// > true
The HTMLElement interface includes a “translate” property, which is a Boolean value. The translate
reference in your handler therefore gets resolved to that value, and you will get a TypeError
telling you “translate is not a function” because you are trying to call a Boolean
More Explanation
If I run this in the Chrome DevTools console:
const div = document.createElement("div");
function translate() {
console.log(".")
};
div.setAttribute("onclick", "translate()");
getEventListeners(div).click[0]
I can inspect the listeners, and see the scope chain:
listener: ƒ onclick(event)
arguments: null
caller: null
length: 1
name: "onclick"
prototype: { constructor: ƒ }
__proto__: ƒ()
[[FunctionLocation]]:
[[Scopes]]: Scopes[4]
0: With Block { align: "", title: "", lang: "", translate: true, dir: "", ... }
1: With Block { location: Location, jQuery321081306834416800691: {…}, implementation: DOMImplementation, URL: "https://....com/", … }
2: Script { div: div }
3: Global { window: Window, self: Window, document: document, name: "", location: Location, … }
once: false
passive: false
type: "click"
useCapture: false
The first object in that scope chain list is the <div>
that triggers the handler. Generally, the scope chains of functions defined in html element attributes way will be something like: (1) the call object -> (2) the HTML element -> (3) . . . stuff in between . . . -> (4) Document -> (5) Global.
When a variable is being resolved or looked up, js will go up the scope chain until it finds the definition. If you declare an un-nested function in your script the usual way, it will be a property of the global object. If anything between the global scope and the HTML element where the handler is defined has a property with the same name as your function, references to your function will be resolved to that property instead of your function.
Screenshot of DevTools comparing scopes of handlers defined as HTML attributes vs. with addEventListener()
Keep getting an uncaught type error when I click the button that is supposed to trigger the translate function. Unsure what is causing this. I’ve tried changing the element from a button element to an input element with type button. Whatever I do I still get this error. Anyone able to help?
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="en-us">
<title>Example Web Editor</title>
<link rel="stylesheet" type="text/css" href="xtext/2.24.0/xtext-ace.css"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script src="webjars/requirejs/2.3.6/require.min.js"></script>
<script type="text/javascript">
var baseUrl = window.location.pathname;
var fileIndex = baseUrl.indexOf("index.html");
if (fileIndex > 0)
baseUrl = baseUrl.slice(0, fileIndex);
require.config({
baseUrl: baseUrl,
paths: {
"jquery": "webjars/jquery/3.5.1/jquery.min",
"ace/ext/language_tools": "webjars/ace/1.3.3/src/ext-language_tools",
"xtext/xtext-ace": "xtext/2.24.0/xtext-ace"
}
});
require(["webjars/ace/1.3.3/src/ace"], function() {
require(["xtext/xtext-ace"], function(xtext) {
xtext.createEditor({
baseUrl: baseUrl,
syntaxDefinition: "xtext-resources/generated/mode-logic"
});
});
});
</script>
<script>
function translate() {
console.log("function being triggered.");
let req = new XMLHttpRequest();
console.log("request created");
req.open( "POST", "localhost:8003/translate", false);
req.send(ace.edit('xtext-editor').getSession().getValue());
return req.responseText;
}
</script>
</head>
<div class="container">
<div class="header">
<h1>Example LogicLang Web Editor</h1>
</div>
<div class="content">
<div id="xtext-editor" data-editor-xtext-lang="logic"></div>
</div>
</div>
<form onsubmit = "return false">
<input type="button" onclick="translate()"> Compile </input>
<button class="save-button" onclick="console.log(ace.edit('xtext-editor').getSession().getValue());"> Test </button>
</form>
</body>
</html>
Are you encountering any other errors before the error you’ve presented? Sidenote, don’t use synchronous HTTP requests as they will prevent other code to run while making the request.