DEV Community 👩‍💻👨‍💻

Cover image for An emoji dictionary in Svelte
Bryce Dorn
Bryce Dorn

Posted on • Updated on

An emoji dictionary in Svelte

As an avid user of the 🅱️ emoji, I get (too much) enjoyment out of the few alphabetical characters in the emoji alphabet.

But we can do more than just substitute a b with a 🅱️; I wanted to know many words can be written entirely with emoji. Let's find out!

First I found an (English) dictionary and wrote a quick & dirty Rust script to generate the words. Just a list of words isn't fun though, it needs interactivity! I chose Svelte for this to get some hands-on with its dev experience (it's pretty good!) and performance.

To start, I made a basic webpack config with svelte-loader and three files:

Note: if you want to skip to the end, the source is here.
  • index.html, with a <body> where the Svelte app will be mounted to (just like ReactDOM)
  • main.js, where the app is mounted & passed props
  • App.svelte, for the component & filtering logic

JavaScript

In main.js, the words are imported and prepared for the component:

import words from 'output.txt';

// Associate letters & sequences with their 
// emoji equivalents
const emojiHash = {
    "id": "🆔",
    "a": "🅰️",
        ...
    "soon": "🔜"
};

// Replace the letters/sequences in a string as 
// their respective emoji
const convertToEmoji = (word) => {
    let emojified = String(word);
    regexKeys.forEach((key, i) => {
        emojified = emojified.replace(key, emojiHash[sortedKeys[i]];
    }));
    return emojified;
};

// Render letters/sequences as emoji by running 
// the above function until there are no letters
// remaining
function emojify(word) {
    let emojified = String(word);
    do {
        emojified = convertToEmoji(emojified);
    } while (emojified.split('').some(e => /^[a-zA-Z]+$/.test(e)));
    return emojified;
};

Enter fullscreen mode Exit fullscreen mode

Then the component is mounted to the DOM:

const app = new App({
    target: document.body,
    props: {
        emoji: Object.values(emojiHash),
        sort: 'default',
        words: words.split('\n').map(emojify)
    }
});
Enter fullscreen mode Exit fullscreen mode

Svelte

Great! Now we have formatted data coming into the component, let's do something with it.

*.svelte files are HTML files with some syntactic sugar. The basic structure is as follows:

<script>
  // Functions, variables
  export let words;

  function clicky(e) {
    console.log(e.target.innerText);
  }
</script>

<!-- Any styles associated with the component -->
<style>
  .container { 
    background: palevioletred; 
  }
</style>

<!-- The rendered markup -->
<div class="container">
  <ul>
    {#each words as word}
      <li>
        <p on:click={clicky}>
          {word}
        </p>
      </li>
    {/each}
  </ul>
</div>
Enter fullscreen mode Exit fullscreen mode

🎉 ta-da! 🎉 A list of words rendered with Svelte! Note that since words is being passed in as a prop, the export keyword is needed.

For the sake of brevity I'll just go through adding filtering (sorting is in the repo if you want to take a look).

Somewhere in the component, let's render a list of checkboxes for each emoji:

Filter:
{#each emoji as moji}
  <label>
    <input on:change={handleFilterClick} type="checkbox" checked={!filteredEmoji.includes(moji)} value={moji}>
    <span>{moji}</span>
  </label>
{/each}
Enter fullscreen mode Exit fullscreen mode

Since we're rendering the list via the words variable, we'll need to update it to reflect the filter.

<script>
  export let words;

  // Keep an immutable version of words in memory
  export let wordsImm = Array.from(words);

  function handleFilterClick(e) {
    const { checked, value } = e.target;

    // Keep immutable version of original prop & make a copy 
    // to apply filters to
    let wordsMut = Array.from(wordsImm);

    // Either add or remove the current emoji from the filters
    if (checked) {
      filteredEmoji.splice(filteredEmoji.indexOf(value), 1);
    } else {
      filteredEmoji.push(value);
    }

    // If there are filters, apply them to list of words
    if (filteredEmoji.length > 0) {
      filteredEmoji.forEach(emoji => {
        wordsMut = wordsMut.filter(word => !word.includes(emoji));
      });
    }

    // Set variable to new list
    words = wordsMut;
  }
</script>
Enter fullscreen mode Exit fullscreen mode

When words is updated to the filtered (mutated) version after selecting a filter, it will trigger an update and the DOM will render the new list.

Side-note: this could be refactored to have the filtering in the `{each}`, (preventing the need to update `words`) but I wanted to render the number of words in a different part of the component.

Final thoughts

Svelte is nice & fast! I plan to use it again, ideally for something more resource intensive/visually demanding to really push it to its limits (beyond where React would have issues).

I also want to see how it is to work on a larger project using Sapper, once the framework is more mature.

Go play with it here! https://bryce.io/emoji-dict

View source on Github.

Top comments (3)

Collapse
 
deciduously profile image
Ben Lovy

I think I hate this, but I also think I love this. Nice work?

Svelte looks cooler by the day.

Collapse
 
bryce profile image
Bryce Dorn Author

Haha 👻 it's getting better! Still a ways away from the clarity of React.

Collapse
 
deciduously profile image
Ben Lovy

Yeah, but "clarity" is a complicated term, right? The sales pitch of compiling away the framework entirely is too cool to dismiss outright. React often feels like overengineering for a given problem, Svelte gets you a lot of the benefit without so much overhead.

Classic DEV Post from 2020:

js visualized

🚀⚙️ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! 🥳

Happy coding!