make document.execCommand('insertText', fa

2019-07-10 04:57发布

问题:

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:

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