0

I am building a chrome extension which needs to be able to change the value of a text field in what I believe is a ReactJS webpage (I'm writing the content script which executes in the page), but the traditional ways that would do so won't work (they change the value of the input, but they don't seem to trigger whatever event the website uses to update its (likely) useState variable. i've thrown the kitchen sink at it:

field.value = 'new value";
field.setAttribute('value', field.value);
field.dispatchEvent(new KeyboardEvent('keypress', {'key': 'H', bubbles: true}));
field.dispatchEvent(new KeyboardEvent('keydown', {'key': 'H', bubbles: true}));
field.dispatchEvent(new KeyboardEvent('keyup', {'key': 'H', bubbles: true}));
field.dispatchEvent(new Event('change', {bubbles: true}));
field.dispatchEvent(new Event('blur', {bubbles: true}));
field.dispatchEvent(new Event('textInput', {bubbles: true}));
field.dispatchEvent(new Event('hashchange', {bubbles: true}));
field.dispatchEvent(new Event('selectionchange', {bubbles: true}));
field.dispatchEvent(new InputEvent('input', {bubbles: true}));
document.dispatchEvent(new KeyboardEvent('keypress', {'key': 'H', bubbles: true}));
document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'H', bubbles: true}));
document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'H', bubbles: true}));
document.dispatchEvent(new Event('change', {bubbles: true}));
document.dispatchEvent(new Event('blur', {bubbles: true}));
document.dispatchEvent(new Event('textInput', {bubbles: true}));
document.dispatchEvent(new Event('hashchange', {bubbles: true}));
document.dispatchEvent(new Event('selectionchange', {bubbles: true}));
document.dispatchEvent(new InputEvent('input', {bubbles: true}));

I've also tried building a formal KeyboardEvent as follows:

  field.dispatchEvent(new KeyboardEvent("keypress", {
    key: "e",
    keyCode: 69, // example values.
    code: "KeyE", // put everything you need in this object.
    which: 69,
    shiftKey: false, // you don't need to include values
    ctrlKey: false,  // if you aren't going to use them.
    metaKey: false   // these are here for example's sake.
  }));

and with

  var keyboardEvent = document.createEvent('KeyboardEvent');
  var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? 'initKeyboardEvent' : 'initKeyEvent';

  keyboardEvent[initMethod](
    'keydown', // event type: keydown, keyup, keypress
    true, // bubbles
    true, // cancelable
    window, // view: should be window
    false, // ctrlKey
    false, // altKey
    false, // shiftKey
    false, // metaKey
    40, // keyCode: unsigned long - the virtual key code, else 0
    0, // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
  );
  field.dispatchEvent(keyboardEvent);

with no success. When the field is blurred, it returns to the original non-changed stat, although manually changing it works.

I have noticed that

  document.execCommand('insertText', false, "new value");

does work, but I don't want to use it as it appears to be deprecated.

Any suggestions?

I'd prefer to do this in vanilla JS, but open to libraries if necessary.

umop
  • 2,122
  • 2
  • 18
  • 22
  • execCommand is the best solution. It was marked as deprecated prematurely several years ago, but the new Editing API is still being specified and will take a long time (probably several years more) to implement. – wOxxOm Mar 10 '23 at 23:02

1 Answers1

0

You need to get the actual value setter using Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set and call it, see the example below (where the react code sets the input value to 'hi', and we (outside of react) set it to whatever, which changes both the input we set, and the label which is also rendering the value):

// Example stateless functional component
const SFC = props => {
  const [v, setV] = React.useState('hi');
  return (
      <div>{v}:<br/> <input value={v} onChange={(e) => setV(e.target.value) } /></div>
  );
}

// Render it
ReactDOM.render(
    <SFC />,
    document.getElementById("root")
);

const newValue = 'whatever';
var input = document.querySelector("input");
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, newValue);

var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
dave
  • 62,300
  • 5
  • 72
  • 93
  • That handled the updating of the field such that if I manually blur it, the value stays persistent. One issue which still persists, though, is that I still can't seem to blur the field or indicate that the value has been entered (I can simulate a click on the OK button, but unless I manually blur the field, the value doesn't take). – umop Mar 11 '23 at 00:48
  • Follow-up question: What is the relevance to the libraries (second) code snippet that was in your answer? – umop Mar 11 '23 at 00:50
  • Those are just the react libraries so I can run react in the snippet – dave Mar 13 '23 at 21:53
  • to blur, you should be able to add `var ev3 = new Event('blur', { bubbles: true}); input.dispatchEvent(ev3);` – dave Mar 13 '23 at 21:54
  • Nope. They appear to be doing something different to detect blur. If I manually click on the OK button, the changes get captured, but if I do it via .click() after a blur event as you suggested, it gets discarded. – umop Apr 04 '23 at 02:56