JavaScript Interop
Tesserae compiles C# to JavaScript with the h5
compiler. Most of the time you work with the strongly-typed browser bindings in
H5.Core.dom, but when you need raw JavaScript — a browser API h5 doesn't
surface, or a global exposed by a bundled library — h5 provides several escape
hatches.
Reaching the DOM and browser globals
Import the DOM bindings and use document, window, console, alert,
navigator, and the rest as typed C#:
using static H5.Core.dom;
document.body.style.overflow = "hidden";
window.setTimeout((_) => DoThing(), 16);
console.error("something went wrong");
Prefer these typed bindings whenever they exist — they are checked at compile time, unlike inline script.
Script.Write — inline JavaScript
H5.Script.Write emits JavaScript verbatim. The generic overload returns a
value. Positional placeholders ({0}, {1}, …) are replaced with the compiled
form of the C# arguments — real JavaScript references, not string concatenation:
using H5;
// no return value
Script.Write("{0}.scrollIntoView()", element);
// typed return value
double now = Script.Write<double>("Date.now()");
bool isArray = Script.Write<bool>("Array.isArray({0})", children);
object lib = Script.Write<object>("new SomeLib({0}, { gutter: {1} })", element, 10);
Pass C# values as {n} placeholders rather than interpolating them into the
string — that way they compile correctly, including under minification. Keep the
JavaScript short, and guard calls that might throw:
Script.Write("try { {0}.setPointerCapture({1}); } catch (e) { }", element, pointerId);
This is exactly how the toolkit drives tippy tooltips, masonry relayout, and pointer capture for gestures.
Typed bindings with attributes
When you want a typed C# surface over existing JavaScript instead of scattering
Script.Write calls, h5 offers a set of attributes:
[H5.Name("globalName")]maps a C# type to a JavaScript global or namespace. Tesserae uses it on every component ([H5.Name("tss.Button")]) to keep generated class names stable.[External]withexternmembers declares a binding that has no C# body — the call compiles straight to the underlying JavaScript member.[H5.Template("…")]emits a specific JavaScript expression for a member, using placeholders like{this}and the argument names.
[H5.Name("tss.ROA")]
public class ReadOnlyArray<T>
{
public extern ReadOnlyArray(T[] data);
[External]
public extern T this[int index] { [Template("{this}[{index}]")] get; }
public extern int Length { [Name("length")] get; } // compiles to .length
}
Converting values
Use .As<T>() to reinterpret a value between its C# and JavaScript views when
the type system needs a nudge — for example passing either an element or a string
to the same JavaScript option.
Caveats
Script.Writestrings are opaque to the compiler, so typos surface only at runtime. Keep them minimal and lean on typed bindings where possible.- Any external global you call must actually exist at runtime. Bundle the script
through
h5.jsonfirst — see Wrapping a JavaScript Library.