注:本文翻译自developer.mozilla.org/en/docs/Web/SVG/Namespaces_Crash_Course 。是在自己的理解的基础上翻译的,如果有疑义可以自己看英文版。
作为XML 的一个方言,SVG 需要在一个命名空间内(is namespaced)。理解命名空间的概念,以及在你打算编辑SVG内容时,命名空间如何使用,是很重要的。SVG查看器的版本早于Firefox 1.5的发布的,都几乎没有注意命名空间的问题,但它们对于支持多XML方言的用户代理(如基于Gecko的浏览器)必不可少,这些浏览器对这些也有非常严格的要求。花一些时间来了解命名空间,它会为您节省各种各样的头痛。
1、背景
W3C的长期目标是使不同类型的基于XML的内容能够在同一个XML文件中混合使用。例如,SVG和MathML可以直接并入基于XHTML的科学文档中。能够混合这些不同的内容类型具有许多优点,但是它还需要解决一个实际的问题。
自然地,每种XML方言定义其规范中描述的标记标签名称的含义。在单个XML文档中混合来自不同XML方言的内容的问题是由一种方言定义的标记标签可能会有与由另一种方言定义的标记标签相同的名称。例如,XHTML和SVG都有一个<title>标签。用户代理应该如何区分两者?
事实上,用户代理什么时候能够识别XML内容,而不只是一个包含许多无法识别的标签的XML文件?与大众意见相反,这个问题的答案不是“它可以从DOCTYPE声明”。DTD从未设计过混合内容,过去尝试创建混合内容DTD现在被认为已失败。XML和一些XML方言(包括SVG),不需要DOCTYPE声明,SVG 1.2甚至不会有一个。 DOCTYPE声明(通常)匹配单个内容类型文件中的内容的事实仅仅是巧合的。DTD仅用于验证,而不是内容的识别。用户代理使用其DOCTYPE声明伪装和识别XML内容是没有作用的。
这个问题的真正答案是,XML内容通过给标签显式的“命名空间声明”告诉用户代理标记名称属于哪个方言。
2、声明命名空间
那么这些命名空间声明是什么样子,声明在哪里?这里是一个简短的例子。
<svg xmlns="http://www.w3.org/2000/svg">
<!-- more tags here -->
</svg>
命名空间声明由xmlns属性提供。此属性表示<svg>标记及其子标记属于名称空间为“http://www.w3.org/2000/svg”的XML方言,当然,它是SVG。注意,命名空间声明只需要在根标记上提供一次。声明定义了默认命名空间,因此用户代理知道所有<svg>标签的后代标签也属于同一命名空间。用户代理检查他们是否属于同一个命名空间,如果是的话才去如何处理命名空间下的标签标记。
注意,命名空间名称只是字符串,尽管SVG命名空间名称看起来像URI,但这并不重要。URI通常被使用,因为它们是唯一的,目的不是“链接”某处。(事实上,URI经常使用,通常使用术语“命名空间URI”而不是“命名空间名称”。)
3、重新声明默认命名空间
因此,如果根标记的所有后代也被定义在默认命名空间中,那么如何混合来自另一个命名空间的内容?简单。您只需重新定义默认命名空间。这里有一个简短的例子。
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<!-- some XHTML tags here -->
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="200px">
<!-- some SVG tags here -->
</svg>
<!-- some XHTML tags here -->
</body>
</html>
在此示例中,根<html>标签上的xmlns属性将默认名称空间声明为XHTML。因此,除了<svg>标记,用户代理将其及其所有子标记解释为属于XHTML。 <svg>标记具有自己的xmlns属性,通过重新声明默认命名空间,这告诉用户代理,<svg>标记及其后代(除非他们也重新编写默认命名空间)属于SVG。看,命名空间真的不是那么难。
4、声明命名空间前缀
XML方言不仅定义自己的标签,而且定义自己的属性。默认情况下,属性根本没有命名空间,并且只知道是唯一的,因为它们出现在本身具有唯一名称的元素上。然而,有时需要定义属性,使得它们可以在许多不同的元素上重用,并且仍然被认为是相同的属性,与它们所使用的元素无关。一个很好的例子是由XLink规范定义的href属性。此属性通常被其他XML方言用作链接到外部资源的手段。但是如何告诉用户代理该属性属于哪个方言,在这种情况下用“XLink”?请考虑以下示例。
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<script xlink:href="cool-script.js" type="text/ecmascript"/>
</svg>
这个例子有相当不寻常的看起来的属性xmlns:xlink。正如你可能从第一个“xmlns”部分猜到的,这是另一个命名空间声明。然而,这个命名空间声明不是设置默认命名空间,而是设置称为“命名空间前缀”的命名空间。在这种情况下,我们选择使用前缀xlink(第二部分),因为前缀将用于告诉用户代理属于XLink的属性。
顾名思义,命名空间前缀用于前缀属性名称和标签名称。这是通过在属性名称之前放置命名空间前缀和冒号来实现的,如上例中的<script>标记所示。这告诉用户代理该特定属性属于分配给命名空间前缀(XLink)的命名空间,并且它是可以用于具有相同含义的其他标记上。
请注意,使用未绑定到命名空间名称前缀是XML错误。尽管xlink:href属性不会导致错误,但上面示例中由xmlns:xlink属性创建的绑定是绝对必要的。这个XLink属性在SVG中经常在<a>,<use>和<image>标签等中使用,所以最好在文档中始终包含XLink声明。
另外,命名空间前缀也可以用于标记名称。这告诉用户代理,这个特定的标签(但不是它的孩子这一次!)属于分配给前缀的命名空间。知道这些将减少你一些迷惑,如果你遇到了像下面的例子中的标记:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg">
<body>
<h1>SVG embedded inline in XHTML</h1>
<svg:svg width="300px" height="200px">
<svg:circle cx="150" cy="100" r="50" fill="#ff0000"/>
</svg:svg>
</body>
</html>
请注意,因为名称空间前缀用于<svg:svg>标记及其子<svg:circle>,因此无需重新声明默认名称空间。一般来说,最好重新声明默认名称空间,而不是以这种很多前缀标签的方式。
5、声明命名空间前缀使用命名空间XML编写脚本
命名空间不仅影响标记,还影响脚本。如果您编写用于命名空间的XML(如SVG)的脚本,请继续阅读。
DOM1级别推荐在XML的原始命名空间发布之前创建; 因此,DOM1不是命名空间所识别的。这会导致名称空间的XML(如SVG)出现问题。为了解决这些问题,DOM2级别添加了命名空间的所有适用的DOM1方法。当编写SVG脚本时,使用命名空间能识别的方法很重要。下表列出了不应在SVG中使用的DOM1方法,以及应该使用的等效DOM2对等方法。如下图:
所有DOM2命名空间方法的第一个参数必须是有问题的元素或属性的命名空间名称(也称为命名空间URI)。对于SVG元素,这是“http://www.w3.org/2000/svg”。 但是,请注意:XML 1.1中的命名空间建议指出,没有前缀的属性的命名空间名称没有值。换句话说,虽然属性属于标签的命名空间,但不使用标签的命名空间名称。相反,您必须使用null作为未限定(无前缀)属性的命名空间名称。 因此,要使用document.createElementNS()创建一个SVG rect元素,您必须写:
document.createElementNS('http://www.w3.org/2000/svg', 'rect');
但是要检索SVG rect元素上的x属性的值,必须写:
rect.getAttributeNS(null, 'x');
注意,不是针对具有命名空间前缀的属性(属于与标记不属于同一个XML方言的属性)的情况。诸如xlink:href属性的属性需要分配给该前缀的命名空间名称(对于XLink,请使用http://www.w3.org/1999/xlink)。 因此,要获取SVG中<a>元素的xlink:href属性的值,您应该写:
elt.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
对于设置具有命名空间的属性,建议(但不是必需),您还在第二个参数中包括其前缀,以便以后可以更容易地将DOM转换回XML(如果您想要将其发送回 服务器)。 例如:
elt.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'otherdoc.svg');
作为最后一个例子,这里是一个演示如何使用脚本动态创建一个<image>元素:
var SVG_NS = 'http://www.w3.org/2000/svg';
var XLink_NS = 'http://www.w3.org/1999/xlink';
var image = document.createElementNS(SVG_NS, 'image');
image.setAttributeNS(null, 'width', '100');
image.setAttributeNS(null, 'height', '100');
image.setAttributeNS(XLink_NS, 'xlink:href', 'flower.png');
6、总结:
确保您始终声明您在XML文件中使用的命名空间。 如果不这样做,Firefox等用户代理将无法识别您的内容,只会显示XML标记或通知用户XML中有错误。 在创建新的SVG文件时,最好使用包含所有常用命名空间声明的模板。 如果你还没有一个,请从下面的代码开始:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
</svg>
即使您不在特定文档中使用所有这些命名空间,包含命名空间声明也没有任何危害。 如果你在以后添加来自一个未使用的命名空间的内容,它可以避免一些恼人的错误。