为什么“#.id”在 CSS/jQuery 中是一个错误的选择器,但它在 HTML 锚中有效?
Why is "#.id" a bad selector in CSS/jQuery yet it works in an HTML anchor?
我正在使用 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/>
在查询元素之前,您必须使用 \
转义 .
。
替换
document.querySelector('#.someMethodName');
到
document.querySelector('#\.someMethodName');
另请注意,从技术上讲,HTML 4 所需的 ID 值格式指定如下:
ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").
所以.[A-Za-z]
是无效的..
因为 HTML5 是一个有效的 id 属性:
There are no other restrictions on what form an ID can take; in
particular, IDs can consist of just digits, start with a digit, start
with an underscore, consist of just punctuation, etc
因为它不是有效的 CSS identifier,为了将它与 querySelector()
或 $()
一起使用,您应该像这样转义它:
#\.someMethodName
To match ID or selectors that do not follow the CSS syntax (by using a
colon or space inappropriately for example), you must escape the
character with a back slash. As the backslash is an escape character
in JavaScript, if you are entering a literal string, you must escape
it twice (once for the JavaScript string, and another time for
querySelector):
注意它不是有效的 HTML4 id 属性
关于 HTML5 ID 命名约定
在 HTML5 中,您可以 name your ID attributes anything you want 几乎没有语法限制:
There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc
需要什么选择器函数
但是,当使用 Document.querySelector() 等 JavsScript 选择器时,请务必注意 的语法,它如何评估其参数。
Returns the first element within the document (using depth-first pre-order traversal of the document's nodes|by first element in document markup and iterating through sequential nodes by order of amount of child nodes) that matches the specified group of selectors.
element = document.querySelector(selectors);
element
is an element object.
selectors
is a string containing one or more CSS selectors separated by commas.
当你混淆选择器函数时
所以在这里,我们看到它正在尝试解析 CSS selectors。基本上,该函数将任何以 #
开头的字符串解释为 class 并将任何以 #
开头的字符串解释为 id,所以当你尝试向它传递一个像这样的字符串时:
#.someMethodName
它认为您正在尝试将 id 和 和 class 全部解析为单个参数,并抛出一个错误,称其为语法错误。
总结
总之,虽然您的 ID 值在技术上是有效的,但使用 .
和 #
会混淆那些 JavaScript 选择器函数,例如 $(selector)
和 document.querySelector(selector)
等
要解决此问题,您需要通过转义非标识符字符(小号):
#\.someMethodName
HTML5 允许在 ID 属性值中使用句点,浏览器几十年来一直在毫无问题地处理这个问题(这就是为什么 HTML 4 中的限制本身不是由 HTML 但由它所基于的 SGML — 在 HTML5 中放宽了,现在摆脱了 SGML 的遗留包袱)。所以问题不在属性值中。
RFC 3986定义的片段标识符的语法是:
fragment = *( pchar / "/" / "?" )
其中pchar
的字符集包含句点。所以 .someMethodName
是一个有效的片段标识符,这就是 <a href="#.someMethodName">
起作用的原因。
但是#.someMethodName
不是有效的选择器,原因有两个:
- ID 选择器由
#
后跟一个 ident 和 an ident in CSS cannot contain a period. 组成
- 句点因此保留给 class 选择器(它同样由一个句点后跟一个标识符组成)。
简而言之,解析器期望在 #
之后有一个 CSS 标识,但由于直接跟在它后面的 .
而找不到,这使得选择器无效。这是令人惊讶的,因为 ID 选择器的表示法实际上是基于片段标识符的 URI 表示法——事实证明它们都以 #
符号开头,而且它们是两者都用于引用由该标识符在文档中唯一标识的元素。期望在 URI 片段中起作用的任何东西在 ID 选择器中也起作用并不是没有道理的——在大多数情况下,它 是 是正确的。但是因为 CSS 有自己的语法,不一定与 URI 语法相关(因为它们是两个完全不相关的标准 1),你会遇到这样的边缘情况一.
由于句点是片段标识符的一部分,您需要用反斜杠将其转义才能在 ID 选择器中使用它:
#\.someMethodName
不要忘记您需要在 JavaScript 字符串中对反斜杠本身进行转义(例如与 document.querySelector()
和 jQuery 一起使用):
document.querySelector('#\.someMethodName')
$('#\.someMethodName')
1 几年前,W3C Community Group 围绕一项名为 的提案成立(我是其中的成员)使用 CSS 选择器作为片段标识符 ,如您所想,这两种技术以一种有趣的方式结合在一起。然而,这从未成功,唯一已知的实现是一些可能甚至没有被维护的浏览器扩展。
Why does the browser itself accept this anchor but jQuery and querySelector do not?
因为散列不是 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 选择器的开头,但这并不意味着每个 #
都是 [=33 的开头=] ID选择器。
如果浏览器确实使用散列作为CSS选择器,它可能会在使用它之前正确转义它。
我正在使用 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/>
在查询元素之前,您必须使用 \
转义 .
。
替换
document.querySelector('#.someMethodName');
到
document.querySelector('#\.someMethodName');
另请注意,从技术上讲,HTML 4 所需的 ID 值格式指定如下:
ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").
所以.[A-Za-z]
是无效的..
因为 HTML5 是一个有效的 id 属性:
There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc
因为它不是有效的 CSS identifier,为了将它与 querySelector()
或 $()
一起使用,您应该像这样转义它:
#\.someMethodName
To match ID or selectors that do not follow the CSS syntax (by using a colon or space inappropriately for example), you must escape the character with a back slash. As the backslash is an escape character in JavaScript, if you are entering a literal string, you must escape it twice (once for the JavaScript string, and another time for querySelector):
注意它不是有效的 HTML4 id 属性
关于 HTML5 ID 命名约定
在 HTML5 中,您可以 name your ID attributes anything you want 几乎没有语法限制:
There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc
需要什么选择器函数
但是,当使用 Document.querySelector() 等 JavsScript 选择器时,请务必注意 的语法,它如何评估其参数。
Returns the first element within the document (using depth-first pre-order traversal of the document's nodes|by first element in document markup and iterating through sequential nodes by order of amount of child nodes) that matches the specified group of selectors.
element = document.querySelector(selectors);
element
is an element object.selectors
is a string containing one or more CSS selectors separated by commas.
当你混淆选择器函数时
所以在这里,我们看到它正在尝试解析 CSS selectors。基本上,该函数将任何以 #
开头的字符串解释为 class 并将任何以 #
开头的字符串解释为 id,所以当你尝试向它传递一个像这样的字符串时:
#.someMethodName
它认为您正在尝试将 id 和 和 class 全部解析为单个参数,并抛出一个错误,称其为语法错误。
总结
总之,虽然您的 ID 值在技术上是有效的,但使用 .
和 #
会混淆那些 JavaScript 选择器函数,例如 $(selector)
和 document.querySelector(selector)
等
要解决此问题,您需要通过转义非标识符字符(小号):
#\.someMethodName
HTML5 允许在 ID 属性值中使用句点,浏览器几十年来一直在毫无问题地处理这个问题(这就是为什么 HTML 4 中的限制本身不是由 HTML 但由它所基于的 SGML — 在 HTML5 中放宽了,现在摆脱了 SGML 的遗留包袱)。所以问题不在属性值中。
RFC 3986定义的片段标识符的语法是:
fragment = *( pchar / "/" / "?" )
其中pchar
的字符集包含句点。所以 .someMethodName
是一个有效的片段标识符,这就是 <a href="#.someMethodName">
起作用的原因。
但是#.someMethodName
不是有效的选择器,原因有两个:
- ID 选择器由
#
后跟一个 ident 和 an ident in CSS cannot contain a period. 组成
- 句点因此保留给 class 选择器(它同样由一个句点后跟一个标识符组成)。
简而言之,解析器期望在 #
之后有一个 CSS 标识,但由于直接跟在它后面的 .
而找不到,这使得选择器无效。这是令人惊讶的,因为 ID 选择器的表示法实际上是基于片段标识符的 URI 表示法——事实证明它们都以 #
符号开头,而且它们是两者都用于引用由该标识符在文档中唯一标识的元素。期望在 URI 片段中起作用的任何东西在 ID 选择器中也起作用并不是没有道理的——在大多数情况下,它 是 是正确的。但是因为 CSS 有自己的语法,不一定与 URI 语法相关(因为它们是两个完全不相关的标准 1),你会遇到这样的边缘情况一.
由于句点是片段标识符的一部分,您需要用反斜杠将其转义才能在 ID 选择器中使用它:
#\.someMethodName
不要忘记您需要在 JavaScript 字符串中对反斜杠本身进行转义(例如与 document.querySelector()
和 jQuery 一起使用):
document.querySelector('#\.someMethodName')
$('#\.someMethodName')
1 几年前,W3C Community Group 围绕一项名为 的提案成立(我是其中的成员)使用 CSS 选择器作为片段标识符 ,如您所想,这两种技术以一种有趣的方式结合在一起。然而,这从未成功,唯一已知的实现是一些可能甚至没有被维护的浏览器扩展。
Why does the browser itself accept this anchor but jQuery and querySelector do not?
因为散列不是 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 选择器的开头,但这并不意味着每个 #
都是 [=33 的开头=] ID选择器。
如果浏览器确实使用散列作为CSS选择器,它可能会在使用它之前正确转义它。