Rending Emoji Glyphs Using Hexadecimal CodePoints In JavaScript
A year ago, I updated my comments to use the utf8mb4
character set so that the comments could contain emoji glyphs. But, using emoji still isn't that easy. On MacOS, to add an emoji, I have to use Ctrl+Cmd+Space
(on MacOS) to trigger the emoji keyboard; then, I have to make sure the cursor is in the right place; then, I have to find and select the target emoji. It's kind of a hassle. As such, I wanted to start exploring the idea of adding emoji short-cuts right into my blogging interface. As a first step, I wanted to look at how to render emoji glyphs given a set of hexadecimal unicode codepoints in JavaScript.
Run this demo in my JavaScript Demos project on GitHub.
View this code in my JavaScript Demos project on GitHub.
Now, you might be wondering, why not just store the emoji glyphs directly in the code? Why bother jumping through hoops to convert between different codepoints and glyphs. To which I would say, because I'm old. And my old brain still can't wrap itself around the idea of having non-ASCII characters in a code file. So, I want my code to be ASCII, but be able to generate non-ASCII output.
I'm old. That's a thing.
That said, to experiment with emoji glphy rendering in JavaScript, I grabbed a few of the more common emoji out of this emoji-test.txt
file over on unicode.org. Each of the emoji in that file is represented by a series of one-or-more hexadecimal unicode codepoints. For example:
- "grinning face" →
1F600
- "smiling face" →
263A
FE0F
- "face in clouds" →
1F636
200D
1F32B
To render these series of codepoints as a String, we can use the String.fromCodePoint()
function. This function is a variadic function, which means that it accepts a dynamic number of arguments wherein each argument is a single codepoint. It then constructs a String using the specified sequence of codepoints.
CAUTION: The
String.fromCodePoint()
prototype method is not available in IE11; but, it can be polyfilled if you need to support that browser.
To see this method in action, I've created a simple demo that lists-out a few of the common emoji as text-labels. Then, if you click on any of the text-labels, we convert the hexadecimal values to glphys and append the glyph to a textarea
control:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>
Rending Emoji Glyphs Using Hexadecimal CodePoints In JavaScript
</title>
<link rel="stylesheet" type="text/css" href="./demo.css" />
</head>
<body>
<h1>
Rending Emoji Glyphs Using Hexadecimal CodePoints In JavaScript
</h1>
<textarea id="text"></textarea>
<ul id="emoji">
<li><a data-hex="1F600">grinning face</a></li>
<li><a data-hex="1F603">grinning face with big eyes</a></li>
<li><a data-hex="1F604">grinning face with smiling eyes</a></li>
<li><a data-hex="1F601">beaming face with smiling eyes</a></li>
<li><a data-hex="1F606">grinning squinting face</a></li>
<li><a data-hex="1F605">grinning face with sweat</a></li>
<li><a data-hex="1F923">rolling on the floor laughing</a></li>
<li><a data-hex="1F602">face with tears of joy</a></li>
<li><a data-hex="1F642">slightly smiling face</a></li>
<li><a data-hex="1F643">upside-down face</a></li>
<li><a data-hex="1F609">winking face</a></li>
<li><a data-hex="1F60A">smiling face with smiling eyes</a></li>
<li><a data-hex="1F607">smiling face with halo</a></li>
<li><a data-hex="263A FE0F">smiling face</a></li>
<li><a data-hex="1F62E 200D 1F4A8">face exhaling</a></li>
<li><a data-hex="1F635 200D 1F4AB">face with spiral eyes</a></li>
<li><a data-hex="1F615">confused face</a></li>
<li><a data-hex="1F61F">worried face</a></li>
<li><a data-hex="1F641">slightly frowning face</a></li>
<li><a data-hex="2639 FE0F">frowning face</a></li>
<li><a data-hex="1F62E">face with open mouth</a></li>
<li><a data-hex="1F62F">hushed face</a></li>
<li><a data-hex="1F632">astonished face</a></li>
<li><a data-hex="1F633">flushed face</a></li>
<li><a data-hex="1F97A">pleading face</a></li>
<li><a data-hex="1F626">frowning face with open mouth</a></li>
<li><a data-hex="1F627">anguished face</a></li>
<li><a data-hex="1F628">fearful face</a></li>
<li><a data-hex="1F630">anxious face with sweat</a></li>
<li><a data-hex="1F625">sad but relieved face</a></li>
<li><a data-hex="1F622">crying face</a></li>
<li><a data-hex="1F62D">loudly crying face</a></li>
<li><a data-hex="1F631">face screaming in fear</a></li>
<li><a data-hex="1F616">confounded face</a></li>
<li><a data-hex="1F623">persevering face</a></li>
<li><a data-hex="1F61E">disappointed face</a></li>
<li><a data-hex="1F613">downcast face with sweat</a></li>
<li><a data-hex="1F629">weary face</a></li>
<li><a data-hex="1F62B">tired face</a></li>
<li><a data-hex="1F971">yawning face</a></li>
</ul>
<script type="text/javascript" src="../../vendor/jquery/3.6.0/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
var textarea = $( "#text" );
// Delegate all click events on our special anchor tags.
var emoji = $( "#emoji" )
.on( "click", "a[ data-hex ]", handleClick )
;
// I handle the click on the emoji labels. Each click will place the
// corresponding emoji character at the end of the textarea input.
function handleClick( event ) {
// Each emoji link contains a data attribute with hexadecimal code-points. In
// order to generate our emoji strings, we need to map those hexadecimal
// values onto decimal values.
var hexPoints = $( event.target )
.data( "hex" )
.split( " " )
;
var codePoints = hexPoints.map(
function operator( hex ) {
return( parseInt( hex, 16 ) );
}
);
// Now that we have our code-points, we can generate the emoji glyph using
// the .fromCodePoint() function.
// --
// CAUTION: IE11 does not have .fromCodePoint() method.
var emojiGlyph = String.fromCodePoint.apply( null, codePoints );
textarea.val( textarea.val() + emojiGlyph + " " );
console.group( "Emoji Operation" );
console.log( "Hex Points:", hexPoints )
console.log( "Code Points:", codePoints );
console.log( "Emoji Glyph:", emojiGlyph );
console.groupEnd();
}
</script>
</body>
</html>
Here, we're using jQuery to simplify the code with its dead-simple .on()
based event-delegation. This allows us to capture click
events on the ul
element and then only respond to those events if they originate from any one of our anchor tags.
When we do capture the click
event, we access the anchor's dataset
, pull out the hexadecimal unicode codepoints, .map()
them to decimal codepoints, and then render the emoji glphy using String.fromCodePoint()
. And, if we run this in the browser and click on some of the links, we get the following output:
And lo, there were emoji, and it was good!
In this demo, I'm just arbitrarily adding the emoji to the end of the texarea
value; but, in the long-run, I'd like to remember where the last Selection
is and then insert the emoji at that caret position and update the Selection
accordingly (to be after the inserted emoji).
An Aside on jQuery and Dogmatic Programming in 2021
When I first started authoring this demo, I had intended to use vanilla JavaScript to wire-up all the event-handlers and the event-delegation. But, after a minute of doing this, I remembered that jQuery gives me all that stuff for free, right out of the box, with a single line of code. And, since this is a stand-alone JavaScript demo, there's no point in even being dogmatic about page-weight or latency.
So, I pulled-in the latest version of jQuery - and kablamo! - my code became significantly simpler and easier to read.
Want to use code from this post? Check out the license.
Reader Comments
@All,
As a quick follow-up to this post, I wanted to quickly look at a way to render emoji characters in HTML using HTML Entities:
www.bennadel.com/blog/4085-printing-emoji-characters-from-unicode-codepoints-using-html-entities-in-lucee-cfml-5-3-7-47.htm
Since we already have the Unicode codepoints, all we have to do is output them using either decimal or hexadecimal values. Very similar to what we were doing in the JavaScript.
@All,
As I've been continuing to learn about emoji and using them in my comments, I've had a breakthrough in my understanding of how input elements work. Specifically, I just learned that an input's selection properties are retained even after the input loses focus:
www.bennadel.com/blog/4086-inserting-text-at-the-last-known-selection-caret-location-in-javascript.htm
What this means is that I can always reference the last known caret location of a textarea - no need to track that information independently. 😎