Search code examples
c#blazorwebassemblyblazor-jsinterop

How can I get an HTML element from a blazor ElementReference


OK, I'm sure I'm going mad and missing something simple here...

if I have an ElementReference object in c# in a Blazor WASM app, how can I get any details about the html element which it is associated with?

I'm working on a Blazor WASM application, using .Net 6.0.

I am using a component library (SyncFusion) and with one of the components I need to determine which ListBox I'm dropping something into.

all I have is a Blazor ElementReference.

How do I get an HTML element from this object? I'm perfectly happy to use JSInterop for this, but since the ElementReference doesn't seem to have anything matching the generated HTML it's not really useful.

The component is rendering fine, and renders some stuff like:

<div id="listbox-a13426bc-6182-4b64-9c27-2fa57e97bcd2".....> </div>

In the events I'm getting from the component in question, I can get a Target property which is a Blazor ElementReference. This has an Id which appears to be some automatically generated int value and is unique for each "List Box" generated, but I cannot work out how to match this to the specific component being referenced.

I have to be able to work out whether this refers to the right-side or left-side of two ListBoxes.

I can add additional Attributes or CSS classes to the controls in question but this doesn't help me as I can't work out how to match the ElementReference or its Id property to anything, when that's all I have to start with. I can also match the components up to a property reference with @ref="xxx" but I have the same problem there.

The Component is an SfListBox and that class doesn't seem to expose any way to retrieve the ElementReference for the associated HTML either so I can't pick it up and store it for later comparison either.

The ElementReference doesn't seem to have an obvious method or property to get the unique GUID it generated.

I've searched for a few hours trying to find the simple method I want, but I cannot find any obvious way to work this out....


Solution

  • Right, as hinted at by @fuzzybear the obvious solution to this is just to directly pass the ElementReference through to a JS function via JSInterop, which will happily accept it as an HTMLElement

    First, Define a JS function, in a nice unique namespace in index.html.

    This function could check Id or other attributes of course, it's just JS stuff. In my case because I could quickly add CSS classes to the lists (see next step) I chose to write a function to check if a particular class is in the css classes for the element

    <script>
            var MyUtils = MyUtils || {};
            MyUtils.hasClass = function (element, className) {
                return element.classList.contains(className);
            };
    </script>
    

    Next, in the blazor component....

    1. Inject an IJSRuntime instance
    2. In whatever code you have in which you have an ElementReference you then need to just pass this to your JS function (in my case it was a DropEventHandler from my SyncFusion component)

    So my example code now looks something like this:

    @inject IJSRuntime JS;
    
    ...(snip a bunch of other stuff)
    
    <SfListBox
        DataSource="@ItemList"
        CssClass="left-box"
        Scope="@Scope"
        ... (snip more properties etc)
    >
        <ListBoxEvents
            TValue="TValue"
            TItem="TItem"
            OnDrop="SelectListDrop"
        ></ListBoxEvents>
    </SfListBox>
    
    
    @code{
        ...(snip other code)
        
        async Task SelectListDrop(DropEventArgs<TItem> args)
        {      
            //Here I can get an ElementReference from the passed-in args object
            //Via the 'Target' property, so I pass this to my JS
            //function via InvokeAsync.
            var isLeft = await JS.InvokeAsync<bool>(
                "MyUtils.hasClass", 
                new object[] { args.Target, "left-box" });
    ​
            var targetBoxDesc = isLeft ? "left-box" : "right-box";
            Console.WriteLine($"Target is {targetBoxDesc}");
        }
    }