最近在一个老项目中,需要在 IE8 环境下生成二维码。原本以为用个 QR 库搞定就行,结果遇到各种“Canvas 不支持”、“对象不支持此属性或方法” 的报错。
本文记录了整个问题的排查与最终封装方案,希望能帮到还在与 IE8 搏斗的朋友。
问题背景
我们项目要在旧版浏览器(特别是 IE8)中动态生成二维码,用的是开源的 jsqr(官方地址:https://www.jsqr.de/encode.html)。
在现代浏览器上一切正常,但到了 IE8 —— 页面直接报错:
对象不支持此属性或方法: getContext
也就是说:
IE8 压根不支持 Canvas!
而 jsqr 默认是基于 Canvas 绘图的。
调试准备
IE8调试方法有很多种,可以开一个虚拟机,我这里直接使用Edge的Internet Explorer兼容模式,不需要安装任何额外的软件。
可能是运气不好,原来一直存在的快捷选项在我想要调试的时候突然不见了,

于是又去找,最终找到了开启方法:https://learn.microsoft.com/zh-cn/answers/questions/5606783/142-0-3595-53-(-)-(64-)edge
在这里加入要调试的页面地址就行了

调试开发者工具开启方法:
Win + R 运行:
%systemroot%\system32\f12\IEChooser.exe
第一次尝试:直接用 jsqr
最初的代码结构大致如下,基本是从官网粘贴下来的:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>QR Test IE8+ Compatible</title>
<script src="./jsqr-1.0.3-min.js"></script>
</head>
<body>
<div id="container">container</div>
<script>
var qr = new JSQR();
var code = new qr.Code();
code.encodeMode = code.ENCODE_MODE.BYTE;
code.version = code.DEFAULT;
code.errorCorrection = code.ERROR_CORRECTION.H;
var input = new qr.Input();
input.dataType = input.DATA_TYPE.URL;
input.data = {
"url": "jsqr.de"
};
var matrix = new qr.Matrix(input, code);
var canvas = document.createElement('canvas');
canvas.setAttribute('width', matrix.pixelWidth);
canvas.setAttribute('height', matrix.pixelWidth);
canvas.getContext('2d').fillStyle = 'rgb(0,0,0)';
matrix.draw(canvas, 0, 0);
document.getElementById('container').appendChild(canvas);
</script>
</body>
</html>
在 Chrome / Edge 下一切正常,但在 IE8:
报错:
语法错误

查了一下之后发现我的 jsqr 版本1.0.3 ,源代码中包含ES6+语法,于是我就使用Babel将它编译成ES5版本。
第二次尝试:使用ES5版本jsqr
代码基本没变,把引入的原始js库改成了我编译之后的ES5版本
<!-- ES5 版 JSQR 库 -->
<script src="./jsqr-1.0.3-es5.js"></script>
结果仍然报错:
canvas.setAttribute('width', matrix.pixelWidth);
canvas.setAttribute('height', matrix.pixelWidth);
canvas.getContext('2d').fillStyle = 'rgb(0,0,0)'; // 这里在 IE8 报错:对象不支持此属性或方法 getContext
matrix.draw(canvas, 0, 0);
document.getElementById('container').appendChild(canvas);

第三次尝试:引入 excanvas.js
查资料后发现:
IE8 可以通过一个微软早期的兼容层 —— excanvas.js 来模拟 Canvas 功能。
于是我加上:
<!--[if lt IE 9]>
<script src="./excanvas.js"></script>
<![endif]-->
并在创建 canvas 后初始化:
var canvas = document.createElement('canvas');
if (typeof G_vmlCanvasManager !== 'undefined') {
G_vmlCanvasManager.initElement(canvas);
}
canvas.setAttribute('width', matrix.pixelWidth);
canvas.setAttribute('height', matrix.pixelWidth);
canvas.getContext('2d').fillStyle = 'rgb(0,0,0)';
matrix.draw(canvas, 0, 0);
document.getElementById('container').appendChild(canvas);
这一步之后,IE8 确实没有报错了,但不知为何仍然加载不出来二维码,不过元素的宽高确实出来了。

第四次尝试:调整canvas生成时机(成功)
再次查阅资料,找到一篇文章https://blog.csdn.net/weixin_42629522/article/details/153745582,文章写的非常详细,我也了解到exCanvas的初始化逻辑和时机非常重要,参考此文章我把代码顺序调整了一下。
var canvas = document.createElement('canvas');
document.getElementById('container').appendChild(canvas); // 创建元素操作移至初始化之前
if (typeof G_vmlCanvasManager !== 'undefined') {
G_vmlCanvasManager.initElement(canvas);
}
canvas.setAttribute('width', matrix.pixelWidth);
canvas.setAttribute('height', matrix.pixelWidth);
canvas.getContext('2d').fillStyle = 'rgb(0,0,0)';
matrix.draw(canvas, 0, 0);
确实也可以正常显示了

封装
在成功调试过后我也把代码封装了一下,包括原jsqr的各个参数都重新封装了,包括其他的依赖一并All in One,接着暴露出函数QRGenerator,直接生成,方便后续使用,参数名除了前两个,其他全部沿用jsqr官网的参数名。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>QR Test IE8+ Compatible</title>
<script src="jsqr-ie.mix.js"></script>
</head>
<body>
<div id="container">container</div>
<script>
QRGenerator(
containerId = 'container',
text = 'qr for IE8',
{
version: 1,
errorCorrection: 'M',
scale: 4,
margin: 4,
fgColor: '#000000',
bgColor: 'transparent'
}
);
</script>
</body>
</html>
参考资料
在摸索过程中,我参考了两篇很有帮助的文章:
封装混合文件
已上传至github,项目地址:https://github.com/Alore111/jsqr-for-IE8
导入方法:
下载混合源文件或使用CDN导入,注意放在文件最开始的地方:
<!--方法1:下载引用源混合文件-->
<script src="jsqr-ie.mix.js"></script>
<!--方法2:使用在线CDN-->
<script src="https://cdn.jsdelivr.net/gh/Alore111/jsqr-for-IE8@0.0.1/jsqr-ie.mix.js"></script>


