
本文详解如何在 Chart.js 4.x 中安全、规范地为散点图(scatter)添加自定义标签,重点解决 Jinja2 模板中因 HTML 实体转义导致的 Unexpected Token ‘&’ 报错问题,并推荐使用 |tojson 过滤器实现 JavaScript 数据的无损传递。
本文详解如何在 chart.js 4.x 中安全、规范地为散点图(scatter)添加自定义标签,重点解决 jinja2 模板中因 html 实体转义导致的 `unexpected token ‘&’` 报错问题,并推荐使用 `|tojson` 过滤器实现 javascript 数据的无损传递。
在 Chart.js 中,labels 并非 dataset 的属性,而是属于顶层 data 对象的字段——这是引发本次错误的根本原因。你当前将 labels: {{ labels }} 错误地写在了某个 scatter 数据集内部(如第四个 dataset),而 Chart.js 会忽略该配置,更关键的是:Jinja2 默认会对变量输出进行 HTML 转义,将单引号 ‘ 替换为 ‘、双引号 ” 变为 “,而像 [‘test1’, ‘test2’] 这样的字符串在渲染时会变成 [‘test1’, ‘test2’],最终被浏览器解析为非法 JS 语法,抛出 Uncaught SyntaxError: Unexpected token ‘&’。
✅ 正确做法是:将 labels 提升至图表 data 对象顶层,并与 datasets 同级;同时,必须使用 Jinja2 的 |tojson 过滤器,它能将 Python 对象(如 [‘test1’, ‘test2’, ‘test3’])序列化为标准、安全、未转义的 JSON 字符串(如 [“test1″,”test2″,”test3”]),并自动处理引号、斜杠等特殊字符。
以下是修正后的完整代码示例(适配 Chart.js v4.2.1 + flask + Jinja2):
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.2.1"></script> <canvas id="myChart" width="400" height="400"></canvas> <script> // ✅ 正确:labels 在 data 顶层,且使用 |tojson 过滤器 const chartData = { labels: {{ labels | tojson }}, // ← 关键:安全注入字符串数组 datasets: [ { type: 'line', label: 'Baseline', data: [], backgroundColor: 'rgb(168, 38, 8)', borderColor: 'rgb(168, 38, 8)', pointRadius: 0, borderWidth: 3, order: 2 }, { type: 'scatter', label: 'Group A', data: [], backgroundColor: 'rgb(168, 38, 8)', borderColor: 'rgb(168, 38, 8)', pointRadius: 3, order: 3 }, { type: 'scatter', label: 'Group B', data: [], backgroundColor: 'rgb(46, 179, 84)', borderColor: 'rgb(46, 179, 84)', pointRadius: 3, order: 1 }, { type: 'scatter', label: 'Annotated Points', // ✅ 推荐为每个 dataset 显式设置 label data: {{ data | tojson }}, // ← 同样需 |tojson backgroundColor: 'rgb(52, 110, 235)', borderColor: 'rgb(52, 110, 235)', pointRadius: 1, order: 4 } ] }; const config = { type: 'scatter', data: chartData, // ← 引用顶层 data 对象 options: { maintainAspectRatio: true, aspectRatio: 1, plugins: { legend: { display: false } }, scales: { x: { display: false }, y: { display: false } } } }; const myChart = new Chart( document.getElementById('myChart'), config ); </script>
⚠️ 重要注意事项:
- labels 数组长度应与所有 scatter 数据集中 data 的总点数匹配(若需为每个点独立标注),但 Chart.js 散点图默认不显示轴标签;如需在悬停提示(tooltip)中显示自定义文本,请配合 options.plugins.tooltip.callbacks.label 自定义;
- |tojson 是 Flask/Jinja2 官方推荐方案,不可替换为 |safe ——后者仅禁用转义,不保证 JSON 格式合法,存在 xss 风险;
- 若后端传入的 labels 为 None 或空列表,|tojson 会正确输出 NULL 或 [],无需额外判空;
- Chart.js v4 要求 data 中的 labels 与 datasets[i].data 的索引一一对应(尤其当混合 line/scatter 时),确保数据结构对齐。
总结:添加标签的核心在于「位置正确 + 序列化安全」。牢记 labels 属于 data,而非 dataset;坚持使用 {{ var | tojson }},即可彻底规避模板注入错误,让动态图表数据稳定可靠地呈现。