4
votes

I want to prevent the default behavior of an input (html input tag with type="text") only when enter is pressed

protected void HandleKeyPress(KeyboardEventArgs keyboardEventArgs)
{
    if (keyboardEventArgs.Key == "Enter")
    {
        // prevent default
    }     

    // rest of function  
}

I know that I can do things like @on{EVENT}:preventDefault but how to do this conditionally inside the event handler?

In javascript I can do something like

function(e){
    if(e.keyCode == 13) {
      e.preventDefault();
    }
    // rest of function
}

How to do the same in blazor?

Edit:

I'm making an input for a library and I can't change the button of the submit, I need to prevent this inside my input component.

5
What type of input ?you mean if (keyboardEventArgs.Key == "Enter") and not if (keyboardEventArgs.Key != "Enter"), right ? - enet
@enet Sorry, my typo. I just fixed the question - Vencovsky
Again, What type of input ? text, button............. - enet
@enet html input tag - Vencovsky
<input type="text" /> <input type="button"/> <input type="checkbox" /> The list is long... What type of input ? - enet

5 Answers

4
votes

To achieve what you need you need to set html of your component like this

<input @onkeydown="KeyDown" @onkeydown:preventDefault="shouldPrevent" type="text" />
<p>@eventText</p>

You can set preventDefault as a variable and then change it in your C# code

@code {
    private bool shouldPrevent;
    private string eventText = "";

    private void KeyDown(KeyboardEventArgs e){
        if(e.Code is "Enter"){
            eventText = "Enter pressed";
            shouldPrevent = true;
            return;
        }
        eventText = "";
        shouldPrevent = false;
    }
}

Here you can check blazorfiddle with working code.

2
votes

As mentioned earlier, you have to do this in JavaScript. If you execute this JavaScript code:

window.addEventListener("load", function () {
    //This will be called when a key is pressed
    var preventDefaultOnEnterCallback = function (e) {
        if (e.keyCode === 13 || e.key === "Enter") {
            // console.log("Prevented default.")
            e.preventDefault();
            return false;
        }
    };
    //This will add key event listener on all nodes with the class preventEnter.
    function setupPreventDefaultOnEnterOnNode(node, add) {
        if (node instanceof HTMLElement) {
            var el = node;
            //Check if main element contains class
            if (el.classList.contains("prevent-default-on-enter") && add) {
                // console.log("Adding preventer: " + el.id);
                el.addEventListener('keydown', preventDefaultOnEnterCallback, false);
            }
            else {
                // console.log("Removing preventer: " + el.id);
                el.removeEventListener('keydown', preventDefaultOnEnterCallback, false);
            }
        }
    }
    //This will add key event listener on all nodes with the class preventEnter.
    function setupPreventDefaultOnEnterOnElements(nodelist, add) {
        for (var i = 0; i < nodelist.length; i++) {
            var node = nodelist[i];
            if (node instanceof HTMLElement) {
                var el = node;
                //Check if main element contains class
                setupPreventDefaultOnEnterOnNode(node, add);
                //Check if any child nodes contains class
                var elements = el.getElementsByClassName("prevent-default-on-enter");
                for (var i_1 = 0; i_1 < elements.length; i_1++) {
                    setupPreventDefaultOnEnterOnNode(elements[i_1], add);
                }
            }
        }
    }
    // Create an observer instance linked to the callback function
    // Read more: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
    var preventDefaultOnEnterObserver = new MutationObserver(function (mutations) {
        for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
            var mutation = mutations_1[_i];
            if (mutation.type === 'childList') {
                // A child node has been added or removed.
                setupPreventDefaultOnEnterOnElements(mutation.addedNodes, true);
            }
            else if (mutation.type === 'attributes') {
                if (mutation.attributeName === "class") {
                    console.log('The ' + mutation.attributeName + ' attribute was modified on' + mutation.target.id);
                    //class was modified on this node. Remove previous event handler (if any).
                    setupPreventDefaultOnEnterOnNode(mutation.target, false);
                    //And add event handler if class i specified.
                    setupPreventDefaultOnEnterOnNode(mutation.target, true);
                }
            }
        }
    });
    // Only observe changes in nodes in the whole tree, but do not observe attributes.
    var preventDefaultOnEnterObserverConfig = { subtree: true, childList: true, attributes: true };
    // Start observing the target node for configured mutations
    preventDefaultOnEnterObserver.observe(document, preventDefaultOnEnterObserverConfig);
    //Also check all elements when loaded.
    setupPreventDefaultOnEnterOnElements(document.getElementsByClassName("prevent-default-on-enter"), true);
});

Then you only need to add (or remove) the CSS class prevent-default-on-enter to your inputs where you want to have this behavior.

<input type="text" class="prevent-default-on-enter" />

<button type="submit" class="prevent-default-on-enter">Submit</button>

You can do this on the fly. No need to interact with JavaScript in your razor code :-)

0
votes

In short, you can't (in C#).

This is because all JavaScript Interop is done asynchronously, and conditional cancelling of events must always be synchronous.

It seems odd you'd want to prevent your library users from being able to press enter to submit their form when your input control is focused.

However, you could write a bit of javascript to do it for you. You can use @ref=SomeVariable in your html input control, which will give you an HtmlElementReference that you can pass as a parameter to your javascript from your components OnAfterRender method when firstRender == true

You can read how to do that kind of thing here https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/passing-html-element-references/

0
votes

Thanks to enet and Peter Morris for helping with this.

After a lot of tries, looks like the only way of doing this is with javascript

.js

window.preventDefaultOnEnter = function (element, remove = false) {
    var preventDefaultOnEnterFunction = function (e) {
        if (e.keyCode === 13 || e.key === "Enter") {
            e.preventDefault()
            return false
        }
    }
    if (remove) {
        element.removeEventListener('keydown', preventDefaultOnEnterFunction, false);
    }
    else {
        element.addEventListener('keydown', preventDefaultOnEnterFunction, false);
    }
}

.razor

<input @ref="InputRef" />

@code {
    [Inject]
    private IJSRuntime JSRuntime { get; set; }

    public ElementReference InputRef { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        if (PreventDefaultOnEnter)
        {
            await JSRuntime.InvokeVoidAsync("preventDefaultOnEnter", InputRef);
        }
    }

    public override async void Dispose()
    {
        base.Dispose();
        if (PreventDefaultOnEnter)
        {
            await JSRuntime.InvokeVoidAsync("preventDefaultOnEnter", InputRef, true);
        }
    }
}
0
votes

The solution I have taken in this scenario is:

  1. created a javascript function that handles events occurring in input element
  2. created methods in C# which are invoked depending on what JS condition is met.

2.1. if keyup event is triggered we want to set the final value to the object's property in C#

2.2. on the other hand if keydown is triggered AND the key is one of those that we are particularry interested in (enter/up/down/etc..) then we want some specific logic to be triggered.

here is how the function looks like:

<script>
    function subscribeToChange(componentRef, inputRef) {
        console.log("subscribeToChange!");

        inputRef.onkeydown = function (event) {
            if (event.keyCode == "38") {
                event.preventDefault();

                console.log("Up key pressed");
                componentRef.invokeMethodAsync('invokeFromJS');
            }
            else if (event.keyCode == "40") {
                event.preventDefault();

                console.log("Down key pressed");
                componentRef.invokeMethodAsync('invokeFromJS');
            }
        };

        inputRef.onkeyup = function (event) {
            componentRef.invokeMethodAsync('setValueFromJS', inputRef.value);
        };
    }
</script>

and in C# we have:

[JSInvokable("invokeFromJS")]
public async Task HelloFromJS()
{
    //HelloFromJSStr += "A";
    //StateHasChanged();
    // do something here on UP or DOWN button presses..
}

[JSInvokable("setValueFromJS")]
public async Task SetValueFromJS(string newValue)
{
    HelloFromJSStr = newValue;
    StateHasChanged();
}

I don't feel like I should be pasting my own answer from another thread, so just check the remaining answer with full code example here: https://stackoverflow.com/a/66290595/1215913