How to add an Emoji Picker to Sveltekit

I'm working with sveltekit for all my recent projects, and for the last one, I needed to add an emoji picker to a textarea. I searched for a simple solution, and it appears that there is none - or I didn't find it. So here is what I did. Hopefully, it will help the svelte community:

I choose Picmo as my emoji picker, it's pretty easy to set up, and it does the job perfectly.

Create the Emoji component

The first step is to create the Emoji component. Let's create a \$lib/emoji.svelte:

<script>
    import { createEventDispatcher } from 'svelte';
    import { onMount } from 'svelte';
    const dispatch = createEventDispatcher();

    onMount(async () => {
        const { createPopup } = await import('@picmo/popup-picker');
        const trigger = document.querySelector('#emojiTrigger');

        const picker = createPopup(
            {},
            {
                referenceElement: trigger,
                triggerElement: trigger
            }
        );

        trigger.addEventListener('click', () => {
            picker.toggle();
        });
        picker.addEventListener('emoji:select', (selection) => {
            dispatch('change', selection);
        });
    });
</script>

When the component is mounted, we import the Picmo popup lib. Then we create the Popup object, and we pass the trigger object (the trigger is basically the button you will use to open the popup).

On the click on the trigger, we toggle the picker, and when an emoji is selected, we dispatch the selection to the parent element (i.e.: our main page).

Now we need to catch the new event and add it to our textarea.

Add it to the view

Here is what I did:

page.svelte:

<script>
    import EmojiPicker from '$lib/emoji.svelte';

    let message;
    function onEmoji(event) {
        message = newMessage;
    }
</script>

<textarea
    bind:value={message}
    placeholder="Start writing your post…" />

<button id="emojiTrigger"> </button>
<EmojiPicker on:change={onEmoji} />
<div id="pickerContainer" />

We import the EmojiPicker component and add a "#pickerContainer" to the page. When the EmojiPicker change (when a new emoji is selected), we update the message variable. As the textarea value is bound to the message variable, we just have to update the message to display the emoji!

Add the emoji at the right place

With the code above the emoji will be added at the end of the text. That's not what we want. Here is how to fix that.

We will update the onEmoji() function

function onEmoji(event) {
    const idxPosition = doGetCaretPosition(document.getElementById('comment'));
    let newMessage =
        message.slice(0, idxPosition) + event.detail.emoji + message.slice(idxPosition);
    $message = newMessage;
}

Instead of adding the emoji at the end of the message, we get the position of the cursor and slice the message in two part and add the emoji inside.

To get the cursor position is used a function doGetCaretPosition():

function doGetCaretPosition(oField) {
    // Initialize
    var iCaretPos = 0;

    // IE Support
    if (document.selection) {
        // Set focus on the element
        oField.focus();

        // To get cursor position, get empty selection range
        var oSel = document.selection.createRange();

        // Move selection start to 0 position
        oSel.moveStart('character', -oField.value.length);

        // The caret position is selection length
        iCaretPos = oSel.text.length;
    }

    // Firefox support
    else if (oField.selectionStart || oField.selectionStart == '0')
        iCaretPos =
            oField.selectionDirection == 'backward' ? oField.selectionStart : oField.selectionEnd;

    // Return results
    return iCaretPos;
}

Now you should have a nice emoji picker on your sveltekit page! I hope it helps!

If you have any question, ping me on Twitter