如何用JS识别用户浏览器是否支持某 Emoji?比如🧑🌾在安卓上可能展示为🧑🌾
背景
之前我在文章《为什么同一表情'🧔♂️'.length==5但'🧔♂'.length==4?本文带你深入理解 String Unicode UTF8 UTF16》中讲了非常硬核的内容,深入带大家了解了 Unicode UTF8 以及 JavaScript 中的 String 字符串。非常推荐你仔细阅读并收藏。
如果你的网页中,展示一些 Emoji,那么一定要小心!因为 Emoji 也是在不断的更新迭代的,在旧的设备或系统中,可能无法正确地展示新出的 Emoji。
比较推荐的做法:要展示某个 Emoji 前,优先判断它是否能正确展示,如果不能展示,可以展示文字描述,或者替换为旧版类似的 Emoji,或者展示兜底图案。
问题来了:如何判断用户浏览器能否正确展示某个 Emoji?
解决思路
我们在用户看不到的地方,创建一个元素,不设置该元素的宽度,并把元素的内容设置为该 Emoji。
- 如果该元素的宽度等于「正常展示 Emoji 时的宽度」,说明该 Emoji 可以正常展示。
- 如果该元素的宽度大于「正常展示 Emoji 时的宽度」,说明该 Emoji 被拆开展示了。例如安卓上🧑🌾展示为🧑🌾。
- 如果该元素的宽度小于「正常展示 Emoji 时的宽度」,说明不认识该 Emoji,可能展示为方框。
难点
- 如何获取「正常展示 Emoji 时的宽度」?
- 如何保证不影响用户体验?
- 如何确保不存在字号问题?
解决方案
获取元素宽度
首先写个函数,创建包含某个文字的元素,并计算它的宽度。计算完宽度后,把该元素删除。
const getTextWidth = (text) => {
const element = document.createElement('span');
element.setAttribute('aria-hidden', 'true');
element.setAttribute('style', 'font-size:16px!important;display:absolute;top:0;opacity:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace');
element.textContent = text;
document.body.appendChild(element);
const width = element.clientWidth;
document.body.removeChild(element);
return width;
};
为了保证不影响用户体验,我们需要设置它为绝对定位,且它是透明的。而且要设置它的 aria-hidden
,保证屏幕阅读器不会聚焦到该元素。(详细了解该属性,请阅读:《什么是无障碍适配?》和《Web如何适配无障碍?》)
好处:这样即使用户电脑很卡,也不会看到这个元素了。而且由于该元素不影响用户页面的布局,不会触发浏览器的重排。
为了确保字号一致,影响判断,我设置了内联样式,并且加了 !important
,使之具有最高优先级。
此外,我还设置了 font-family
为 monospace
这种等宽字体,主要目的是识别出方框,因为默认字体下即使字符展示为方框,它的宽度依旧跟「正常展示 Emoji 时的宽度」一致。
获取「正常展示 Emoji 时的宽度」
这里,我们使用一个兼容性最好的 Emoji,计算它的宽度。
如果该宽度大于等于字号(最多允许比16px的字号小一点点,比如允许小2px,那么就是必须大于等于14),就用这个值作为「正常展示 Emoji 时的宽度」。
const EmojiWidth = getTextWidth('😀');
检验 Emoji 能否被正常展示
const isEmojiValid = (emoji) => {
return getTextWidth(emoji) === EmojiWidth && EmojiWidth >= 14;
};
写在最后
转载自:https://juejin.cn/post/7175905261272432699