20

I've two react events:

  1. Form submit event

    sendMessage: function(event){
        console.log(event);
        event.preventDefault();
    },
    
  2. keyPress Event

    textareaKeypress: function(event){
        if (event.which == 13 && !event.shiftKey){
            document.getElementById('messagebox').submit();
        }
    },
    

but the reactJS is not capturing the form submit triggered by textareaKeypress. How can I call the sendMessage from textareaKeypress with proper event?

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Dheerendra
  • 1,488
  • 5
  • 19
  • 36
  • My question is not about why this code isn't working, the question is about how to implement form submit trigger which can be catched by reactjs events. – Dheerendra Mar 06 '15 at 18:22
  • 3
    I think you misunderstood my comment. I'm only talking about the formatting of your question, not your code/problem. You used "stack snippets" in your question, which allow us to directly run code on Stack Overflow, by adding big "run code snippet" buttons (see http://stackoverflow.com/revisions/28904875/1). But if your code examples are not excutable on their own, there is no need to use them. It will just clutter your question with those buttons. – Felix Kling Mar 06 '15 at 18:23
  • i.e. Try using a Code Sample (curly braces in message editor) instead of Code Snippets (page with brackets icon). – Sean O Mar 06 '15 at 18:25
  • 1
    @FelixKling ohh, sorry, totally misunderstood that. SeanO Thanks. I'll keep remember that from next time – Dheerendra Mar 06 '15 at 18:27

4 Answers4

32

I learned something new today: this is just how the DOM works.

From the MDN site:

The form's onsubmit event handler (for example, onsubmit="return false;") will not be triggered when invoking this method from Gecko-based applications. In general, it is not guaranteed to be invoked by HTML user agents.

According to the HTML spec:

The submit() method, when invoked, must submit the form element from the form element itself, with the submitted from submit() method flag set.

followed by (section 4.10.22.3)

If the submitted from submit() method flag is not set, then fire a simple event that bubbles and is cancelable named submit, at form.

So, the event is not fired if the submit() method flag is set.


I was able to get the behavior you (and I) were expecting via the following code (JSFiddle example):

<form onSubmit={this.handleSubmit} ref="form">
  <textarea onKeyPress={this.handleKeyPress} />
</form>

// ...

this.refs.form.getDOMNode().dispatchEvent(new Event("submit"));

(You'll need more verbose code in IE.)

jQuery's $(form).submit() delegates to $(form).trigger("submit"), so I expect it's a similar workaround.

Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • 1
    Just wanted to say thanks for your answer. Had a hard time finding it (was trying to do something similar with auto-submitting a form when a checkbox was clicked), but this solved my problem. – sidoh Sep 20 '15 at 02:51
  • 5
    Use `this.refs.form.submit();` instead of `this.refs.form.getDOMNode().dispatchEvent(new Event("submit"));` – frevib Dec 14 '16 at 13:48
  • 5
    @frevib As stated in both the question and this answer, `submit()` doesn't trigger **React**'s `submit` event, thus the workaround – Phu Ngo Jul 11 '17 at 16:11
  • If above does not work for anyone then try this - ReactDOM.findDOMNode(this.form).dispatchEvent(new Event("submit")); And import ReactDOM from 'react-dom'; – Niraj Feb 19 '19 at 10:55
10

New method (24.08.2022)

Form element now has a requestSubmit() method, which would trigger submit event by itself without workarounds, as stated in submit() MDN docs:

The HTMLFormElement.submit() method submits a given <form>.

This method is similar, but not identical to, activating a form's submit <button>. When invoking this method directly, however:

The HTMLFormElement.requestSubmit() method is identical to activating a form's submit <button> and does not have these differences.

However, this method is not supported well enough on IE and Safari 15.6. As of 02.09.2022, this is about 76.49% global usage. If you need a more browser-compatible solution, keep reading until the end.

No need to use refs

Every answer I've yet seen uses refs when you actually don't need them. Most (if not all) of the form controls have a reference to a parent form in their form property:

textarea.form.requestSubmit();

Just make sure to attach a handler on one of the form controls, for example: <button>, <input>, <textarea>, <select>, <option>.

With all that said, you can write your handlers like that:

<form onSubmit={this.sendMessage}>
    <textarea onKeyPress={this.textareaKeypress}/>
</form>
sendMessage: function(event) {
    console.log(event);
    event.preventDefault();
},

textareaKeypress: function(event) {
    if (event.which == 13 && !event.shiftKey) {
        event.target.form.requestSubmit();
    }
},

Or if you care about browser compatibility, dispatch an event manually (thanks to Karol Dabrowski answer):

textareaKeypress: function(event) {
    if (event.which == 13 && !event.shiftKey) {
        event.target.form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
    }
},
Vasiliy Artamonov
  • 939
  • 2
  • 8
  • 24
  • 1
    this is ace, thanks @Vasiliy Artamonov, for anyone wanting to add types to this, see my answer which extends upon this: https://stackoverflow.com/a/74558120/827129 – fredrivett Nov 24 '22 at 09:06
4

It's your component, so you don't need to worry about passing actual events around. You have some event handlers, and then you have the actual action.

sendMessage: function(){
    console.log('message:', this.state.message);
},
handleFormSubmit: function(event){
    event.preventDefault();
    this.sendMessage();
},
handleTextareaKeypress: function(event){
    if (event.which == 13 && !event.shiftKey){
        this.sendMessage();
    }
}
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • 1
    This is the wrong answer. Cause I'm working with some thirdparty module and there are exist inner onSubmit handler. If I trigger form.submit() manually this handler won't be triggered. – Velidan Apr 11 '18 at 13:55
4

Expanding on @VasiliyArtamonov's answer, here's how I got it working with Typescript:

const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>): void => {
  const commandKeyPressed = e.metaKey;
  const enterKeyPressed = e.key === "Enter";
  if (commandKeyPressed && enterKeyPressed) e.currentTarget.form?.requestSubmit();
};

Updated to include the tweak in @VasiliyArtamonov's comment below.

fredrivett
  • 5,419
  • 3
  • 35
  • 48
  • 2
    For avoiding issues with TypeScript, you have to use [`event.currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget) instead of [`event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target). That way you can get rid of type assertion (`as ...`). The element inside `currentTarget` is where the handler is **attached**; the element inside `target` is the one that **triggered** the event, which is not always the same as `currentTarget`. [Comparison of Event Targets](https://developer.mozilla.org/en-US/docs/Web/API/Event/Comparison_of_Event_Targets). – Vasiliy Artamonov Jan 14 '23 at 18:57
  • 1
    Thanks @VasiliyArtamonov, I've gone ahead and updated my answer to reflect that. – fredrivett Jan 17 '23 at 11:35