Add emojis to input

Viewed 5325

I am building a chat and I am using a library called emoji-picker-react and I want to add emojis to my text field and then send it as a message. I am using react so I will simplified the code for convinience. My messages are working but my emojis not so much. So far, if I click the emojis first and follow with some text, is fine. But if I type first and I try to add the emojis it won't work. Also, if I try to send emojis on its own, it won't work either. This is what I used to add emojis on my text field and both do the same.

This is where I try to add the emojis into my text field and is doing what I just explained:

const onEmojiClick = (e, emojiObject) => {
document.querySelector("#text").value += emojiObject.emoji
  // document.getElementById("text").value += emojiObject.emoji
};

And this is my input field:

<input
 id="text"
 style={inputStyles}
 type="text"
 placeholder="Type your message"
 value={message}
 onKeyPress={e => {
  if (e.key !== 'Enter') return;
    sendMessage(message);
  }
 }
 onChange={e => setMessageForm(e.target.value)}
/>

I am sure is something simple but can't get my head around it. I hope someone can guide on this.

Thank you!

3 Answers

A little update to @lissettdm's answer.

Common issue when inserting emojis.

after inserting emoji, the cursor will move to the end of the line.

Improved answer

The cursor stays where the emoji is so the user can input multiple emojis.

 const ref = useRef(null);
  const onEmojiClick = (event, emojiObject) => {
    const cursor = ref.current.selectionStart;
    
    const text = message.slice(0, cursor) + emojiObject.emoji + message.slice(cursor);
    setMessageForm(text);
    
    //Codes added for the new cursor
    const newCursor = cursor+emojiObject.emoji.length
    setTimeout(() => ref.current.setSelectionRange(newCursor,newCursor), 10)
  };

Code explanation

Depending on what emoji package you use, emojiObject.emoji may be unified, native or id. The common mistake and biggest mistake is to presume 1 emoji = 1 character (cursor+1), which is not the case.

For example if you are using emoji-mart, and you use emoji.native, the length is 2 ( not 1 ).

const newCursor = cusor+emojiObject.emoji.length

The following set the new cursor position to be after the emoji. Why the timeout? It's to allow setMessage(text) ample time to update the dom with the new value, before you use the ref to set cursor.

If you do not use setTimeout, the cursor may not be updated correctly.

setTimeout(() => ref.current.setSelectionRange(newCursor,newCursor), 10)

Create a reference to the input node to get the cursor position and update the message value:

  const ref = useRef(null);
  const onEmojiClick = (event, emojiObject) => {
    const cursor = ref.current.selectionStart;
    const text = message.slice(0, cursor) + emojiObject.emoji + message.slice(cursor);
    setMessageForm(text);
  };

jsx

<input
  id="text"
  ref={ref}
  ...
/>

https://stackblitz.com/edit/react-gg2akb?file=src%2FApp.js

Explicitly setting the value of your text input won't trigger an onChange event. Include the Emoji Picker in the component that holds the input's state and call it's setter (assuming you use functional components, call this.setState otherwise) when an Emoji is picked, appending it to the current state.

You may want to read the cursor position using a ref on the input element and its selectionStart property to insert the emoji there.

for example, you could write your input component like this

import React, { useState, useRef } from 'react'
import /* your EmojiPicker component */

const InputWithEmojiPicker = () => {
  const [value, setValue] = useState('')
  const inputRef = useRef(null)
  
  const onEmojiClick = (e, emojiObject) => {
    const { selectionStart, selectionEnd } = inputRef.current
    // replace selected text with clicked emoji
    const newVal = value.slice(0, selectionStart) + emojiObject.emoji + value.slice(selectionEnd)
    setValue(newValue)
  }

  return (
    <div>
      <input type="text" ref={inputRef} value={value} onChange={setValue} />
      {/* your EmojiPicker component with onEmojiClick */}
    </div>
  )
}

export default InputWithEmojiPicker
Related