我正在使用JSDoc。它生成带有句点的 id,如
<a id=".someMethodName"></a>
如果页面的其他部分有
<a href="#.someMethodName"></a>
这非常有效。单击第二个锚点将滚动到第一个锚点。
但是,document.querySelector
和jQuery都找不到锚点。
为什么浏览器本身接受这个锚点,而 jQuery 和 querySelector 不接受?
test("document.querySelector('#.someMethodName')", function() {
document.querySelector('#.someMethodName');
});
test("$('#.someMethodName')", function() {
$('#.someMethodName');
});
function test(msg, fn) {
try {
var result = fn();
log(msg, result);
} catch(e) {
log(msg, e);
}
}
function log() {
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(Array.prototype.join.call(arguments, " ")));
document.body.appendChild(pre);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#.someMethodName">click here to go to anchor and see errors</a>
<pre>
put
some
text
here
so
the
page
is
long
enough
that
when
we
click
the
anchor
the
browser
has
as
a
place
to
scroll
that
is
off
screen
otherwise
we'd
have
no
way
to
see
if
it
worked
or
not
</pre>
<a id=".someMethodName">we should scroll to here</a>
<p>did we make it?</p>
<hr/>
HTML5 允许在 ID 属性值中有一个句点,并且浏览器几十年来一直毫无问题地处理这个问题(这就是为什么 HTML 4 中的限制——它本身不是由 HTML 定义的,而是由它所基于的 SGML 定义的——在 HTML5 中放宽了,现在摆脱了 SGML 的遗留包袱)。所以问题不在于属性值。
RFC 3986 定义的片段标识符的语法为:
fragment = *( pchar / "/" / "?" )
其中pchar
的字符集包括句点。因此.someMethodName
是一个有效的片段标识符,这就是<a href="#.someMethodName">
工作的原因。
但是#.someMethodName
不是一个有效的选择器,原因有两个:
- ID
- 选择器由后跟一个
#
和一个 id 组成,CSS 中的 ID 不能包含句点。 - 因此,句点是为类选择器保留的(同样由句点后跟一个标识组成)。
简而言之,解析器期望在#
之后有一个 CSS 标识,但由于直接跟随它的.
而找不到一个,这使得选择器无效。这是令人惊讶的,因为ID选择器的表示法实际上是基于片段标识符的URI表示法 - 这在它们都以#
符号开头的事实中显而易见,并且它们都用于引用文档中由该标识符唯一标识的元素。期望在 URI 片段中工作的任何内容在 ID 选择器中工作并不是没有道理的——在大多数情况下,这是真的。但是因为CSS有自己的语法,不一定与URI语法相关(因为它们是两个完全不相关的标准1),所以你会得到这样的边缘情况。
由于句点是片段标识符的一部分,因此您需要使用反斜杠对其进行转义,以便在 ID 选择器中使用它:
#.someMethodName
不要忘记,你需要在 JavaScript 字符串中转义反斜杠本身(例如,用于 document.querySelector()
和 jQuery):
document.querySelector('#\.someMethodName')
$('#\.someMethodName')
1几年前,围绕一个名为"使用 CSS 选择器作为片段标识符"的提案,成立了一个 W3C 社区组(我是其中的一员),正如您可以想象的那样,该提案以一种有趣的方式结合了这两种技术。然而,这从未起飞,唯一已知的实现是一些可能甚至没有维护的浏览器扩展。
对于 HTML5 是一个有效的 id 属性:
对于身份证可以采取的形式没有其他限制;在 特别是,ID可以只由数字组成,以数字开头,以数字开头 带有下划线,仅由标点符号等组成
由于它不是一个有效的CSS标识符,为了将其与querySelector()
或$()
一起使用,您应该像这样转义它:
#\.someMethodName
Mozilla 开发者网络:
匹配不遵循 CSS 语法的 ID 或选择器(通过使用 冒号或空格不恰当地),您必须转义 带反斜杠的字符。由于反斜杠是转义字符 在 JavaScript 中,如果要输入文字字符串,则必须进行转义 它两次(一次用于 JavaScript 字符串,另一次用于 查询选择器):
请注意,它不是有效的 HTML4 id 属性
查询元素之前,您必须使用 \
转义.
。取代
document.querySelector('#.someMethodName');
自
document.querySelector('#\.someMethodName');
另请注意,从技术上讲,对于 HTML 4,所需的 ID 值格式指定如下:
ID 和 NAME 标记必须以字母 ([A-Za-z]) 开头,后跟任意数量的字母、数字 ([0-9])、连字符 ("-")、下划线 ("_")、冒号 (":") 和句点 (".")。
所以.[A-Za-z]
是无效的。
关于HTML5 ID命名约定
<小时 />在HTML5中,你可以将ID属性命名为任何你想要的,对语法几乎没有限制:
对于ID可以采用的形式没有其他限制;特别是,ID可以只由数字组成,以数字开头,下划线开头,只由标点符号组成,等等
。
选择器功能期待什么
<小时 />但是,当使用 JavsScript 选择器(如 Document.querySelector()时,请务必注意它如何计算其参数的语法。
返回文档中与指定的选择器组匹配的第一个元素(使用文档节点的深度优先预序遍历|按文档标记中的第一个元素,并按子节点的数量顺序循环访问
顺序节点)。元素 = document.querySelector(selectors);
element
是一个元素对象。selectors
是一个字符串,包含一个或多个以逗号分隔的 CSS 选择器。
当您混淆选择器函数时
<小时 />所以在这里,我们看到它正在尝试解析CSS选择器。基本上,该函数将以#
开头的任何字符串解释为类,将任何以#
开头的字符串解释为 id,因此当您尝试传递如下字符串时:
#.someMethodName
它认为您正在尝试将 id 和类全部解析为单个参数,并抛出一个错误,称其为语法错误。
结语
<小时 />因此,总而言之,虽然您的 ID 值在技术上是有效的,但使用 .
和 #
会混淆那些 JavaScript 选择器函数,如 $(selector)
和 document.querySelector(selector)
等。
若要解决此问题,需要通过转义非标识符字符,让函数知道您正在尝试将.
或#
用作字符而不是标识符:
#\.someMethodName
实际
为什么浏览器本身接受这个锚点,而 jQuery 和 querySelector 不接受?
由于哈希不是 CSS 选择器,因此它是一个后跟 ID 的#
。
浏览器愉快地滚动到该元素,因为它没有使用哈希作为 CSS 选择器,保持不变。它可能根本没有使用CSS选择器,而是通过ID 查找元素的内部方法 - 也被document.getElementById
调用,它也不关心点。证明:
document.getElementById(".someMethodName").style.color = "green";
<div id=".someMethodName">I'm green because I was found by <code>document.getElementById(".someMethodName")</code></div>
虽然CSS确实使用#
来标记ID选择器的开头,但这并不意味着每个#
都是CSS ID选择器的开头。
如果浏览器确实使用哈希作为CSS选择器,那么它可能会在使用它之前正确转义它。