Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

change value of input made with react from chrome extension

I work on Chrome extension, i need to update lot of inputs of an html page made with React from numbers readed from CSV. I cannot update the web site.

- An example of input copied from the rendered website :

  <td><input class="input input_small fpInput" value="29,4"></td>

- How it's made (not sure 100% about that, had to read the uglified js source)

{
    key: "render",
    value: function () {
        return s.a.createElement("input", {
            className: "input input_small fpInput",
            value: this.state.value,
            onChange: this.handleChange,
            onBlur: this.handleSubmit,
            onFocus: this.handleFocus
        })
    }
}

- Each time you change the input value a function is called and a POST is made to save it.

I want to trigger the onBlur() or onChange() from my extension after i changed the input value to trigger the POST

I tried this :

var el = document. ... .querySelector('input'); // the selector is simplied of course
el.value = 321;
el.onChange();  // ERROR onChange is not a function
el.onchange();  // ERROR onchange is not a function
el.handleChange(); // ERROR handleChange is not a function

Any idea please ?

like image 875
AlainIb Avatar asked Jan 10 '19 22:01

AlainIb


3 Answers

To elaborate a bit more on @varoons answer, which is factually correct albeit a bit short on explanation.

You can do so by injecting (dispatching, in browser terms) the event into the dom:

// Needs setAttribute to work well with React and everything, just `.value` doesn't cut it
// Also changed it to a string, as all attributes are strings (even for <input type="number" />)
el.setAttribute("value", "321"); 

// As @wOxxOm pointed out, we need to pass `{ bubbles: true }` to the options,
// as React listens on the document element and not the individual input elements
el.dispatchEvent(new Event("change", { bubbles: true }));
el.dispatchEvent(new Event("blur", { bubbles: true }));

This will actually call all the listeners, even those made with React (as is the case in your de-uglyfied code ;)) or made with simple element.onChange = () => {...} listeners.

Example: https://codesandbox.io/s/kml7m2nn4r

like image 103
Michiel Dral Avatar answered Sep 30 '22 19:09

Michiel Dral


You can't call a React component's method directly from the DOM element it has rendered. You need to trigger an event that bubbles up so that React can catch it at the document level and process it normally, as it would do with a real one.

✨ Document.execCommand():

As pointed out by @woxxom in the comments, the easiest way to do that might be to focus the inputs and then use Document.execCommand():

const input1 = document.getElementById('input1');
const input2 = document.getElementById('input2');

input1.focus();
document.execCommand('insertText', false, 'input1');

input2.focus();
document.execCommand('insertText', false, 'input2');
<input id="input1" />
<input id="input2" />

⚒️ Manually Dispatching Events:

Otherwise, you might try manually dispatching a change, input and/or blur event using the Event() constructor in those fields after you change their value.

Also, note Event()'s second optional argument contains a field, bubbles, that is false by default. You need that one to be true. Otherwise, this won't work, as React is listening for events on the document.

Additionally, you might need to use Element.setAttribute() too, as that will update the value attribute on the DOM (the initial value on the field), while element.value will update the current value (and so the display value). Usually, though, it's not needed. For more on this see What is the difference between properties and attributes in HTML?.

This approach might have some timing issues you might need to handle using setTimeout when updating multiple inputs, so if the first one works, I'd go for that one instead.

const input1 = document.getElementById('input1');

// This updates the value displayed on the input, but not the DOM (you can do that with setAttribute,
// but it's not needed here):
input1.value = 'input1';

// Dispatch an "input" event. In some cases, "change" would also work:
input1.dispatchEvent(new Event('input', { bubbles: true }));

// It looks like only one field will be updated if both events are dispatched
// straight away, so you could use a setTimeout here:

setTimeout(() => {
  const input2 = document.getElementById('input2');
  input2.value = 'input2';
  input2.dispatchEvent(new Event('input', { bubbles: true }));    
});
<input id="input1" />
<input id="input2" />
like image 23
Danziger Avatar answered Sep 30 '22 19:09

Danziger


el.dispatchEvent(new CustomEvent("change"))
like image 24
varoons Avatar answered Sep 30 '22 18:09

varoons