How to add an Emoji Picker to Sveltekit

· 523 words · 3 minute read

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