How can I detect rendering support for emoji in JavaScript?
The accepted answer isn't working on my system (Windows 10/chrome). It returns true for the unsupported glyphs, and it does not appear to be accurate for emojis that don't cover the center pixel but are in fact rendered (☹).
The unsupported glyphs that my system paints, seemingly interchangeably, are : and . Certainly the center pixel of the first is opaque, perhaps that's why.
An alternate method would be to paint the unsupported glyph into a canvas (you can use \uFFFF which is a guaranteed non-character, see https://en.wikipedia.org/wiki/Specials_(Unicode_block)), then "checksum" or really just sum the results of getImageData()
, and check your emojis against those numbers. This way you aren't dependent on the implementation of the glyph, as long as the system is displays a 'not found' symbol. Firefox appears to show a unique hex code of an unsupported emoji, so this method wouldn't work for that browser or similar configurations.
A second way, more accurate but far slower in my usage, would be to compare using toDataURL()
:
I've included the "double unsupported" emoji as well in the snippet tests that run all three methods. For the record, I'm using the "rgb sum" method right now in a demo on CodePen to make sure blank emojis don't appear in the output, and it doesn't filter out anything erroneously.
var c = document.createElement("canvas")
var ctx = c.getContext("2d");
const em = 16;
c.width = em;
c.height = em;
["\uFFFF", "\uFFFF\uFFFF", "ð¤", "☺", "☹", "ð", "☠", "ð©´"].forEach(e => console.log(e + " isSupported (center pixel method):" + supports(e) + ", (checksum method):" + supports2(e) + ", (toDataURL method):" + supports3(e)));
//using center pixel detection
function supports(e) {
let ctx = document.createElement("canvas").getContext("2d");
ctx.fillText(e, -2, 4);
return ctx.getImageData(0, 0, 1, 1).data[3] > 0; // Not a transparent pixel
}
//using checksum method
function supports2(e) {
//https://en.wikipedia.org/wiki/Specials_(Unicode_block) (NON-Character)
var unsupported = ["\uFFFF", "\uFFFF\uFFFF"].map(b => {
ctx.clearRect(0, 0, em, em);
ctx.fillText(b, 0, em);
let d = ctx.getImageData(0, 0, em, em).data
let sum = d.reduce((acc, cur) => {
return acc + cur
})
return sum
});
ctx.clearRect(0, 0, em, em);
ctx.fillText(e, 0, em);
let d = ctx.getImageData(0, 0, em, em).data
let sum = d.reduce((acc, cur) => {
return acc + cur
})
return !unsupported.some(b => b == sum)
}
//using toDataURL() method
function supports3(e) {
ctx.clearRect(0, 0, em, em);
ctx.fillText(e, 0, em);
let emo = c.toDataURL()
ctx.clearRect(0, 0, em, em);
ctx.fillText('\uFFFF', 0, em);
let bad1 = c.toDataURL()
ctx.clearRect(0, 0, em, em);
ctx.fillText('\uFFFF\uFFFF', 0, em);
let bad2 = c.toDataURL()
return (emo != bad1) && (emo != bad2)
}
Paint a glyph (in the Emoji range that is the one most popular by vendors, in the range of Emojis by the Unicode Consortium like Happy face, Kiss, Sad face etc) to canvas
and read a pixel using getImageData
. If the pixel's Alpha channel data[3]
you're interested-in is not transparent (like for example in the center of ) , else, it might be an Emoji ð
function supportsEmoji () {
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = ctx.canvas.height = 1;
ctx.fillText("ð", -4, 4);
return ctx.getImageData(0, 0, 1, 1).data[3] > 0; // Not a transparent pixel
}
console.log( supportsEmoji() );
or something like that...
Tested the above in Chrome, Firefox, IE11, Edge, Safari
Safari returns false
and IE11 although has Emoji but without colors returned true
.
Edit:
(As pointed in comments) Modernizr has a similar detection for Emoji - so you might give it also a go
In case if someone is looking for a library, then here is the one if-emoji
. It uses the same approach Roko has used in his answer.
The flag emoji are a bit of an edge case to the answers posted here. Since if the flag emoji is not supported on a device, a fallback of the country code is rendered instead. E.g. the union jack flag (ð¬ð§) will become -> GB.
Windows 10 doesn't support the Emoji Flags.
This script uses a similar method to the existing answer, but instead checks to see if the canvas is greyscale. If there are colours in the canvas, we know an emoji was rendered.
function supportsFlagEmoji () {
var canvas = document.createElement("canvas");
canvas.height = 10;
canvas.width = canvas.height*2;
var ctx = canvas.getContext("2d");
ctx.font = canvas.height+"px Arial";
ctx.fillText("ð¬ð§", 0, canvas.height);
var data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
var i = 0;
while(i<data.length) {
if (data[i] !== data[i+1] || data[i] !== data[i+2]) return true;
i+=4;
}
return false;
}