When using document.createElement and appendChild in Nim compiled to JavaScript, the created elements don't have the expected DOM methods available, resulting in "appendChild is not a function" errors.
import std/jsffi
import std/dom
import std/cstrutils
proc onLoadAction() =
let container = document.createElement("div")
let selectElement = document.createElement("select")
container.appendChild(selectElement)
document.body.appendChild(container)
when isMainModule:
window.addEventListener("DOMContentLoaded".cstring, proc(event: Event) =
onLoadAction()
)
The code should create a div element with a select element inside and append it to the document body when the DOM is loaded.
Runtime error in browser console:
VM3489:1 Uncaught TypeError: container_1140850693.appendChild is not a function
at eval (eval at onLoadAction__error_u4 (error.js:808:5), <anonymous>:1:22)
at onLoadAction__error_u4 (error.js:808:5)
at HEX3Aanonymous__error_u7 (error.js:828:5)
Debugging check shows:
container_1140850693 instanceof Node // returns false
The current workaround is to use raw JavaScript via {.emit.} pragma:
proc onLoadAction() =
var container: Element
var selectElement: Element
{.emit: """
container = document.createElement('div');
selectElement = document.createElement("select");
container.appendChild(selectElement);
document.body.appendChild(container);
""".}
Compiled at 2025-04-22
The issue appears to be that elements returned by document.createElement in the Nim JavaScript backend are not proper DOM elements with their expected prototype chain and methods. The objects seem to be wrapped in a way that loses access to native DOM methods like appendChild.
This affects the usability of Nim for web development where direct DOM manipulation is required.
I fail to reproduce this on either brave or firefox.
On both your original script compiled to js loads just fine with a minimal html file like this:
Are you loading additional javascript or modules?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>