0

I know that Blazor does not support data-binding for contenteditable div. What I have working is the following that allows text to be written and accessed through onInput.

<div role="textbox" contentEditable="true" @oninput="(e) => HandleInput(e, item.TODO_ID)">@item.TODO_TEXT</div>

The issue is I can't figure out how to update TODO_TEXT with this new text. Anyone has any workarounds to suggest? Even recommendations for other controls that would support markup text.

Note: The above uses JSInterop code from here to ensure that any edits are passed along through oninput:

document.addEventListener("input", onInput);
function onInput(e) {
 let target = e.target;
 if (target.localName == "div") {
   if (!target.value && !target.__contenteditable) target.__contenteditable = true;
   if (target.__contenteditable) target.value = target.innerText;
 }
}
1
  • Please consider marking the answer as accepted if it was helpful to you. Commented Dec 25, 2024 at 12:47

1 Answer 1

0

As Blazor works with its own virtual copy of the DOM it is a bit tricky (and should be avoided) to combine DOM manipulations via JavaScript with Blazor. Nevertheless, there are some ways to proceed.

By default, you are not able to trigger any input event (keydown, etc.) before Blazor itself has inserted the input into the contenteditable div. You could herefore add an event listener via JavaScript (call it once within OnAfterRender()) and thereby keep the div as global JavaScript variable for further access:

JavaScript:

let _inputElement;

function OnBeforeInput(event) {
  // ... put your own 'before logic' to handle event.key
  // to manipulate _inputElement here ...
}

function SetListenToInput(elementId) {
  _inputElement = document.getElementById(elementId);

  _inputElement.addEventListener(
    "keydown",
    (event) => OnBeforeInput(event),
    false
  );
}

Blazor:

using Microsoft.JSInterop;
@inject IJSRuntime JSRuntime

<div id="inputEditor" contenteditable="true" tabindex="1" @ref=_inputReference @onkeydown="e => OnKeyDown(e)"></div>

@code {
  private ElementReference _inputReference; // can be used for focus, etc.

  protected override void OnAfterRender(bool firstRender) {
    base.OnAfterRender(firstRender);

    if (firstRender) {
      _ = JSRuntime.InvokeVoidAsync("SetListenToInput", "inputEditor");
    }
  }

  internal async Task OnKeyDown(KeyboardEventArgs e) {
    // ... handle logic for OnAfterInput ....
  }
}

Now, you could manipulate the DOM via JavaScript as usual and, depending on the particular key, get/set/clear the selection range and caret position before Blazor additionally inserts the key to the contenteditable div and then also adjust it (again) after the key has been entered.


To prevent Blazor (additionally) entering the key but still triggering the input event you need to set the following to the div:

<div ... @onkeydown:preventDefault="true" @onkeydown:stopPropagation="true">@_input</div>

Thereby you do not need a JavaScript event listener anymore as you do not have to cope with an already entered key by Blazor. But now you have to handle all possible inputs and the navigation yourself. Therefore, you could either use JavaScript (as usual) or manipulate the content directly within Blazor.

You can manipulate text:

<div ...>@_input</div>

@code {
  private string _input = string.Empty;

  internal async Task OnKeyDown(KeyboardEventArgs e) {
    _input += e.Key; // just a simple example
    // it's far more complicated as you have to write your own editor logic!
  }
}

... or manipulate the text as raw HTML (for styling/formatting):

<div ...>@((MarkupString)_input)</div>

... or use a RenderFragment/Component and call StateHasChanged() afterwards to inform Blazor about your update. E.g.:

<div ...><MyFragment @ref=_inputFragment /></div>
  
@code {
  private MyFragment? _inputFragment;

  internal async Task OnKeyDown(KeyboardEventArgs e) {
    // ... do whatever you want to do to handle the input
    // and adjust your fragment/component ...
    StateHasChanged();
  }
}

Last, but not least: don't forget to also handle any relevant mouse and clipboard events accordingly.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for this detailed response! I started using textarea (which has its own issues) but I will experiment with this and get back. Thanks much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.