I want to add emoji to my site, but hide them if they're not supported on the platform, rather than showing little squares.
I have a feeling that this isn't possible, but does anybody disagree? How can this be done?
I want to add emoji to my site, but hide them if they're not supported on the platform, rather than showing little squares.
I have a feeling that this isn't possible, but does anybody disagree? How can this be done?
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
false
for me on both Chrome and Firefox (on Linux), even though I can see the 😗 in both the text and the code of the answer. –
Quarter true
on Windows 10) What do you get for console.log(ctx.getImageData(0, 0, 1, 1))
? –
Prescribe { "data": { "0": 0, "1": 0, "2": 0, "3": 0 }, "width": 1, "height": 1 }
. –
Quarter data
{"0": 255,"1": 200,"2": 61,"3": 255}
–
Prescribe ctx.fillText("😗", -2, 4);
on Linux?.. –
Prescribe -2, 4
might be erracit on Lunix –
Prescribe 🦣
–
Pursuer -2, 4
are not the best ones? The code works for me, and I cannot currently test on other OS. I'm kindly open for any sugg. & improvements –
Prescribe window.navigator
–
Donothing ctx.fillText("🦣", -2, 4);
gives true on both OSX where I see a mammoth AND on PC where I see a square with a question mark. The Emoji itself is a \u1F9A3 5 char emoji. –
Pursuer -4, 4
seems like better centering - but than again as pointed in Scott's answer might be faulty as well. –
Prescribe ☹ supported:false
on Win 10 Chrome latest. The rest work - any thoughts? –
Pursuer function supportsEmoji(emoji) ... ctx.fillText(emoji, -4, 4);
–
Pursuer 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;
}
console.log(supportsFlagEmoji())
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)
}
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.
Here's a method that relies on the fact that emojis are almost always square aspect ratio (or close to it). Browsers that don't support emojis will usually render them as a thin rectangle, "", or for grapheme cluster emojis, a sequence of characters, "🤦🏻♂".
/** Check if browser probably supports native emoji rendering
* @param {string} emoji - which emoji to use to test; a multicharacter emoji (grapheme
* cluster) will have lower chance of false positives; common emojis (smiley face),
* sometimes have unicode alternatives the browser will render
* @returns {boolean}
*/
function supports_emoji(emoji="🤦🏼♂️"){
const el = document.createElement("div");
el.style = "position:absolute;visibility:hidden;";
el.textContent = emoji;
document.body.appendChild(el);
const size = el.getBoundingClientRect();
el.remove();
// expecting a roughly square emoji; incorrectly rendered grapheme clusters will be
// rendered as multiple characters (width > height), while single character emojis are
// usually rendered as a thinner rectangle (height > width)
const square = Math.abs(1-size.width/size.height);
return square < .23;
}
As a side note, you may consider using the Noto Emoji font and not worry about checking for emoji support.
["\uFFFF", "\uFFFF\uFFFF", "🖤", "☺", "☹", "💀", "☠", "🩴"]
from Scott's answer. But for some reasons I could not make it work. Would you be kind to make a snippet from your code? And perhaps explain the strange rendering of "🤦🏼♂️"
in the snippet editor –
Pursuer © 2022 - 2024 — McMap. All rights reserved.