javascriptc#quill.net-9.0blazor-webapp

How to adapt @onblur="MethodBlur" behavior and integrate it with QuillJS editor?


In my Blazor Web App with .NET 9, I was trying to monitor if the editor loses focus. While I can monitor the editor itself, I have no idea how to properly integrate QuillJS's hasFocus event with a C# approach to handle this. For this reason, I've decided to mimic the behavior of the @onblur event in a <div id="editor"></div>, assuming it's possible...


My initial attempt was to see if @onblur would work, but it didn't:

<div id="editor" @onblur="FormatOnBlurQuill"></div>
@code {
    private void FormatOnBlurQuill(FocusEventArgs e)
    {
        Console.WriteLine("Test focus blur"); // not working
    }
}

I suspected that this wasn't the same as an InputText or a related form field that can handle @onblur events.


However, I tried to see if I could get the editor's focus status this way:

JavaScript code

window.previewContent = function () {
    const quill = new Quill('#editor', {
        modules: {
            toolbar: '#toolbar-container'
        },
        placeholder: 'Compose a product description here...',
        theme: 'snow'
    });
    window.quillEditor = quill;
};

window.getQuillFocusState = function () {
    if (window.quillEditor) {
        return window.quillEditor.hasFocus();
    }
}

C# code:

@inject IJSRuntime JS

@* Client-side code here... *@

@code {
    private async Task SomeMethod()
    {
        await JS.InvokeVoidAsync("previewContent");
        bool isQuillFocused = await JS.InvokeAsync<bool>("getQuillFocusState");
        Console.WriteLine($"Is quill focused: {isQuillFocused}"); // Is quill focused: False
    }
}

I'm not familiar with how to handle events with JavaScript in a Blazor Web App to continuously monitor the focus status of the QuillJS editor.


My question for this context:


Solution

  • I came up with two workarounds (i.e., on the JavaScript side codebase), each with a different concept that requires fine-tuning depending on my use case. However, these two workarounds share the same server-side concept, which relies on the DotNetObjectReference Class and Microsoft.JSInterop.JSInvokableAttribute.

    Here's the server-side concept (refactored version):

    @inject IJSRuntime JS
    
    @* Client-side code here... *@
    
    @code {
        private bool quillFocusState;
        private async Task SomeMethod()
        {
            await JS.InvokeVoidAsync("previewContent");
            var dotNetObjRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("getQuillFocusState", dotNetObjRef);        
        }
    
        [JSInvokable]
        public void QuillStateFocusChanged(bool focused)
        {
            quillFocusState = focused;
            Console.WriteLine($"Quill focus state changed: {quillFocusState}"); // to test
            StateHasChanged(); // Depending on my use case...
        }
    }
    

    Here are the two sets of workarounds (i.e., on the JavaScript side codebase):

    Using addEventListener

    window.getQuillFocusState = function (dotNetObjRef) {
        if (window.quillEditor) {
            window.quillEditor.root.addEventListener('focus', () => {
                dotNetObjRef.invokeMethodAsync('QuillStateFocusChanged', true);
            });
    
            window.quillEditor.root.addEventListener('blur', () => {
                dotNetObjRef.invokeMethodAsync('QuillStateFocusChanged', false);
            });
        }
    }
    

    The code above works as expected but has some focus state glitches. I need to fine-tune this snippet because it conflicts with another addEventListener call.


    Using QuillJS selection-change event

    window.getQuillFocusState = function (dotNetObjRef) {
        if (window.quillEditor) {
            window.quillEditor.on('selection-change', (range, oldRange, source) => {
                if (range) {
                    if (range.length == 0) {
                        console.log('User cursor is on', range.index); // to test
                    } else {
                        const text = quill.getText(range.index, range.length);
                        console.log('User has highlighted', text); // to test
                    }
                    dotNetObjRef.invokeMethodAsync('QuillStateFocusChanged', true); // actual integration
                } else {
                    console.log('Cursor not in the editor'); // to test
                    dotNetObjRef.invokeMethodAsync('QuillStateFocusChanged', false); // actual integration
                }
            });
        }
    }
    

    The only challenge with the code snippet is that I need to enforce a reasonable execution delay before executing the submit button, for example. This algorithm is responsible for handling customized validation messages (or any other server-side processes) whenever the QuillJS editor's focus changes.