Insert text into an existing / external draftjs textfield
Asked Answered
E

1

11

I'm working on an application which needs to insert text into a contenteditable="true" div (a Draftjs based textfield to be precise).

Now I am aware that Draft.js uses react and that it should be used that way, but in this case, the application already exists and this is a third party electron app that works with it.

I'm working on in-line notification replying on macOS, so I need that reply text to be pasted inside the draftJS field, however, when I do so using:

document.querySelector('div[contenteditable="true"]').focus();
document.execCommand('insertText', false, 'message');

It throws an error: error image

I was able to make it work using:

const event = document.createEvent('TextEvent');
event.initTextEvent('textInput', true, true, window, 'message', 0, locale);

but this API is deprecated and doesn't work properly if the message contains an emoji.

Is there any way to do this that doesn't cause an error? I found out that the new API that is supposed to replace initTextEvent is just new Event() (see docs), but I can't find out if it supports textInput events.

To play around with it you can just go to https://draftjs.org/ and play with it in chrome dev tools.

I would really appreciate some help here as I don't know what to do to make it work anymore. Also, I know people are a fan of jquery, but I'd prefer a native js solution (although any solution is welcome).

edit:

Please note: I'm not using react, the input field I want to modify (draftjs) is using react and I want to input text into it using native js.

edit 2:

For anyone else coming across this issue, I wanted to insert text into the Facebook messenger text field (which uses Draftjs).

I managed to find a working workaround. It does use the deprecated API (event.initTextEvent), but it's the only way that I've found that works, even with emoji. Please do post an answer if you have a better solution to this. It works like this:

async function sendReply(message: string): Promise<void> {
       const inputField = document.querySelector('[contenteditable="true"]') as HTMLElement;
       if (inputField) {
               const previousMessage = inputField.textContent;
               // Send message
               inputField.focus();
               await insertMessageText(message, inputField);
               (await elementReady('._30yy._38lh._39bl')).click();
               // Restore (possible) previous message
               if (previousMessage) {
                       insertMessageText(previousMessage, inputField);
               }
       }
}

function insertMessageText(text: string, inputField: HTMLElement): void {
       // Workaround: insert placeholder value to get execCommand working
       if (!inputField.textContent) {
               const event = document.createEvent('TextEvent');
               event.initTextEvent('textInput', true, true, window, '_', 0, '');
               inputField.dispatchEvent(event);
       }

       document.execCommand('selectAll', false, undefined);
       document.execCommand('insertText', false, text);
}

This is typescript code, so you might want to change it up to use js.

It works by inserting a placeholder value inside the textField using event.initTextEvent, and then replacing that text with:

document.execCommand('selectAll', false, undefined);
document.execCommand('insertText', false, 'text');

tested in Chrome: Version 71.0.3578.98

Epitomize answered 18/1, 2019 at 15:0 Comment(2)
Why don't you add a custom event and than listen for it in the react code and than call the proper Modifier methods?Wallenstein
I'm not using react, that's the thing, if I was using react to do this it would be easy. This is an existing react application (using the draftjs textfield system) that I need to input text into using regular javascript. And I know that react uses a virtual DOM, but I should be able to just somehow get that notification text into that field. It works using the second method, but that's deprecated and has emoji issues. The execCommand method works but throws an error that prevents it from working properly.Epitomize
I
16

Although the question was asked a long ago and @JoniVR found a workaround, this may help someone else.

I was also having a similar problem while working on an extension. I also tried the method document.execCommand('insertText', false, text). It worked on LinkedIn but not on Facebook. It was inserting text in the wrong node. Although document.execCommand API works in some places, it's obsolete now.

For Facebook and any other sites using drafjs editor, We need to dispatch a paste event using dataTransfer and clipBoardEvent APIs to make draftjs think that the text is pasted and process accordingly.

const dataTransfer = new DataTransfer();

function dispatchPaste(target, text) {
  // this may be 'text/html' if it's required
  dataTransfer.setData('text/plain', text);

  target.dispatchEvent(
    new ClipboardEvent('paste', {
      clipboardData: dataTransfer,

      // need these for the event to reach Draft paste handler
      bubbles: true,
      cancelable: true
    })
  );

  // clear DataTransfer Data
  dataTransfer.clearData();
}

Check this link in case more info needed.

Interviewer answered 29/8, 2020 at 3:39 Comment(6)
Worked like charm. Thanks for sharing!Poriferous
@Poriferous I'm glad it helped you.Interviewer
How does this function get called? Does my code call dispatchPaste?Airframe
@DanielKaplan Yes. You need to call this function dispatchPaste.Interviewer
Thank you! This works in other editors, FWIW. Much easier than document.execCommand('insertText', false, text);Airframe
It's not working on reddit though.Calie

© 2022 - 2024 — McMap. All rights reserved.