
本文详解 svg 内联脚本的规范写法:必须使用 包裹 js 代码,并说明其 xml 合法性原理、常见错误原因及完整可运行示例。
SVG 是基于 XML 的矢量图形格式,因此其内联 <script> 标签必须严格遵循 XML 语法规则。直接在 <script> 中书写 JavaScript(如含 <、>、& 等字符)会导致 XML 解析失败——浏览器会将 if (x < 10) 中的 < 误判为标签起始,从而报错“XML parsing error”或“invalid token”。这是原问题中 CDATA 报错的根本原因:并非 CDATA 本身错误,而是开发者未正确闭合或遗漏了 // 注释符,导致 JS 引擎尝试解析 XML 声明。</script>
✅ 正确做法是:将全部 JavaScript 代码置于 标签内,并用 // 行注释包裹,使其对 JavaScript 解析器完全透明,同时满足 XML 的合法性要求。// 不是可选语法糖,而是必需的——它确保 不被 JS 引擎当作无效语句执行,仅作为 XML 处理指令存在。
以下是修复后的完整 SVG 时钟示例(已精简冗余属性,修正拼写错误如 tranform → transform,并优化动画初始化逻辑):
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<!-- 内阴影滤镜 -->
<filter id="innerShadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
<feOffset in="blur" dx="2.5" dy="2.5"/>
</filter>
<!-- 表盘与外圈 -->
<g>
<circle id="shadow" cx="97" cy="100" r="87" fill="rgba(0,0,0,.1)" filter="url(#innerShadow)"/>
<circle id="circle" cx="100" cy="100" r="80" stroke="#fff" stroke-width="12" fill="#20b7af"/>
</g>
<!-- 指针组(含 animateTransform) -->
<g>
<line id="hourhand" x1="100" y1="100" x2="100" y2="55"
transform="rotate(0 100 100)" stroke="#fffbf9" stroke-width="3">
<animateTransform attributeName="transform" type="rotate"
from="0" to="360" dur="43200s" repeatCount="indefinite"/>
</line>
<line id="minutehand" x1="100" y1="100" x2="100" y2="40"
transform="rotate(0 100 100)" stroke="#fdfdfd" stroke-width="4">
<animateTransform attributeName="transform" type="rotate"
from="0" to="360" dur="3600s" repeatCount="indefinite"/>
</line>
<line id="secondhand" x1="100" y1="100" x2="100" y2="30"
transform="rotate(0 100 100)" stroke="#c1efed" stroke-width="2">
<animateTransform attributeName="transform" type="rotate"
from="0" to="360" dur="60s" repeatCount="indefinite"/>
</line>
</g>
<!-- 表心与刻度 -->
<circle id="center" cx="100" cy="100" r="3" fill="#128a86" stroke="#c1efed" stroke-width="2"/>
<!-- 12个刻度线(30°间隔) -->
<g stroke="#ffffff" stroke-width="1">
<!-- 使用循环生成更简洁,此处展开便于理解 -->
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(30 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(60 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(90 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(120 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(150 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(180 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(210 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(240 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(270 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(300 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(330 100 100)"/>
<line x1="100" y1="20" x2="100" y2="40" transform="rotate(360 100 100)"/>
</g>
<!-- 关键:内联脚本必须使用 CDATA -->
<script type="text/javascript">
//<![CDATA[
// 初始化指针动画起始角度(避免初始跳变)
const now = new Date();
const secAngle = (now.getSeconds() + now.getMilliseconds() / 1000) * 6;
const minAngle = (now.getMinutes() + now.getSeconds() / 60) * 6;
const hrAngle = (now.getHours() % 12 + now.getMinutes() / 60) * 30;
// 获取对应 animateTransform 元素
const secondAnim = document.querySelector('#secondhand > animateTransform');
const minuteAnim = document.querySelector('#minutehand > animateTransform');
const hourAnim = document.querySelector('#hourhand > animateTransform');
// 设置动态起始值(格式:'角度 中心x 中心y')
const setRotate = (anim, angle) => {
anim.setAttribute('from', `${angle} 100 100`);
anim.setAttribute('to', `${angle + 360} 100 100`);
};
setRotate(secondAnim, secAngle);
setRotate(minuteAnim, minAngle);
setRotate(hourAnim, hrAngle);
//]]>
</script>
</svg>⚠️ 重要注意事项:
立即学习“Java免费学习笔记(深入)”;
-
加载方式决定脚本是否执行:
- ✅ 正确:通过
- ❌ 无效:
或 CSS background-image: url(clock.svg) —— 浏览器会禁用脚本以保障安全。
- CDATA 必须严格配对:// 结尾,中间不可出现 ]]> 字符序列(即使注释中也不行),否则提前终止 CDATA。
- 调试技巧:在 CDATA 内添加 console.log() 可输出到浏览器控制台(需通过 object/内联方式加载);若无输出,优先检查加载方式是否支持脚本执行。
- 兼容性提示:现代浏览器(Chrome/Firefox/Safari/Edge)均支持 SVG 内联脚本,但部分旧版 IE 需降级为外部 JS 引用。
总结而言,SVG 内嵌 JavaScript 的核心在于尊重其 XML 本质——CDATA 不是“黑魔法”,而是标准的 XML 内容转义机制。掌握这一原则,即可安全、可靠地为 SVG 添加交互逻辑,如实时仪表、动态图表或本文的精准时钟。


















