PardusLabs.JSVaporizer.NET.8 1.0.9.2

There is a newer version of this package available.
See the version list below for details.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package PardusLabs.JSVaporizer.NET.8 --version 1.0.9.2
                    
NuGet\Install-Package PardusLabs.JSVaporizer.NET.8 -Version 1.0.9.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PardusLabs.JSVaporizer.NET.8" Version="1.0.9.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PardusLabs.JSVaporizer.NET.8" Version="1.0.9.2" />
                    
Directory.Packages.props
<PackageReference Include="PardusLabs.JSVaporizer.NET.8" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PardusLabs.JSVaporizer.NET.8 --version 1.0.9.2
                    
#r "nuget: PardusLabs.JSVaporizer.NET.8, 1.0.9.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=PardusLabs.JSVaporizer.NET.8&version=1.0.9.2
                    
Install PardusLabs.JSVaporizer.NET.8 as a Cake Addin
#tool nuget:?package=PardusLabs.JSVaporizer.NET.8&version=1.0.9.2
                    
Install PardusLabs.JSVaporizer.NET.8 as a Cake Tool

JSVaporizer

JSVaporizer is a minimal, opinionated .NET-to-JavaScript interop layer for WebAssembly-based apps. It provides two-way bindings:

  1. .NET → JavaScript through [JSImport] wrappers.
  2. JavaScript → .NET via [JSExport] methods and delegate pools for event handling and generic function invocation.

The code in this repository allows you to:

  • Dynamically create and manage DOM elements from the .NET side.
  • Call existing JavaScript functions (and function properties) by name.
  • Register C# delegates so that JavaScript can call into them (useful for event handlers, utilities, etc.).
  • Maintain dictionaries of these delegates (function pools), avoiding direct references in JS while still enabling dynamic binding.

Table of Contents

  1. Requirements
  2. Key Concepts
  3. Examples
    1. Creating a DOM Element in .NET
    2. Calling JavaScript Functions from .NET
    3. Adding Event Handlers
    4. Registering a Generic Function for JS to Call
  4. Project Structure
  5. Thread Safety
  6. Performance Considerations
  7. License

Requirements

  • .NET 8 or higher.
  • WebAssembly-compatible runtime (e.g., Sdk.WebAssembly).
  • A build or project setup that supports C#-to-JS interop using [JSExport] and [JSImport] (e.g., an ASP.NET Core WebAssembly project referencing System.Runtime.InteropServices.JavaScript).

Key Concepts

1. Partial Classes for Interop

All JS-bound methods are grouped into nested partial classes under a common JSVapor static class. For example, WasmElement, WasmDocument, and WasmWindow are partial classes that each contain [JSImport] methods to call JavaScript functions.

Similarly, WasmExports is where we place [JSExport] methods—these are .NET methods callable from JavaScript.

2. Event Handler and Generic Function Pools

We keep dictionaries to store .NET delegates:

  • WasmJSVEventHandlerPool: Holds delegates for DOM event handlers.
  • WasmJSVGenericFuncPool: Holds delegates for arbitrary functions that JavaScript can invoke by key.

This approach avoids creating direct named methods for every single handler in [JSExport]. Instead, JavaScript only needs a “function key,” and we look up the actual C# method in the dictionary.

3. Ephemeral JSObject References

When you request a DOM JSObject for an element, the code automatically disposes it if it’s connected (isConnected == true). This ensures we don’t hold on to many JSObject instances in .NET, which can hurt memory usage. On subsequent calls, the system retrieves a fresh reference by calling document.getElementById or similar.

4. Custom Exceptions and Error Handling

We use a custom JSVException class for domain-specific errors (e.g., attribute name is not lowercase, key collisions, missing event handlers). This is to keep interop errors well-defined rather than throwing generic exceptions.


Examples

Creating a DOM Element in .NET

// Create a new <div> element with id="myDiv"
var newDiv = JSVapor.Document.CreateElement("myDiv", "div");

// Set an attribute
newDiv.SetAttribute("class", "myClass");

// Append it to another element
var parentElement = JSVapor.Document.AssertGetElementById("parentContainer");
parentElement.AppendChild(newDiv);

Calling JavaScript Functions from .NET

// Using the WasmJSFunctionPool (which calls JS by function key)
var result = JSVapor.JSFunctionPool.CallFunc("myJsFunctionKey", new object[] { "arg1", 42 });

Here, "myJsFunctionKey" corresponds to a JavaScript-side registration (for example, your JS might have something like window.jsFunctionPool.myJsFunctionKey = function(a, b) { ... }).

Adding Event Handlers

// Suppose you want to handle a "click" event on your newly created div.
newDiv.AddEventListener(
    "click", 
    funcKey: "myClickHandler",
    handler: (elem, eventType, evnt) =>
    {
        // do your .NET side logic
        // return an int that indicates whether to stopPropagation or preventDefault
        return 0; // e.g., 0 = default prop, 1 = default no prop, etc.
    }
);

When JavaScript triggers the click event, it calls into .NET by using the [JSExport] method CallJSVEventHandler, which looks up your handler in WasmJSVEventHandlerPool.

Registering a Generic Function for JS to Call

// In .NET, define a function you want JavaScript to call:
JSVGenericFunction myFunc = (object[] args) =>
{
    // Implementation here...
    return "Hello from .NET!";
};

// Register it by key
JSVapor.JSVGenericFunctionPool.RegisterJSVGenericFunction("myGenericFuncKey", myFunc);

// On the JavaScript side, you'd do something like:
//   const result = Module.jsvGenericFunctionPool.callJSVGenericFunction("myGenericFuncKey", ["any", "args"]);

Project Structure

JSVaporizer/
|
├── JSVapor.cs
|    ├── public static partial class JSVapor
|    │     ├── internal partial class WasmExports        // [JSExport] methods
|    │     ├── internal partial class WasmElement        // [JSImport] "element"...
|    │     ├── internal partial class WasmDocument       // [JSImport] "document"...
|    │     ├── internal partial class WasmWindow         // [JSImport] "window"...
|    │     ├── internal partial class WasmJSFunctionPool // [JSImport] "jsFunctionPool"...
|    │     ├── internal partial class WasmJSVEventHandlerPool
|    │     ├── internal partial class WasmJSVGenericFuncPool
|    │     └── (etc.)
|
├── Element.cs    // The JSVapor.Element class, a higher-level wrapper for DOM elements
├── Document.cs   // The JSVapor.Document class, includes the dictionary of known Elements, creation, getById, etc.
├── Other .cs files for exceptions, shared enumerations, etc.
|
└── README.md
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.2.0.8 78 4/19/2025
1.2.0.7 70 4/19/2025
1.2.0.6 62 4/19/2025
1.2.0.5 101 4/18/2025
1.2.0.4 97 4/18/2025
1.2.0.3 120 4/18/2025
1.2.0.2 171 4/17/2025
1.2.0.1 172 4/17/2025
1.2.0 167 4/15/2025