为啥“#.id”在 CSS/jQuery 中是一个糟糕的选择器,但它在 HTML 锚中工作?
Posted
技术标签:
【中文标题】为啥“#.id”在 CSS/jQuery 中是一个糟糕的选择器,但它在 HTML 锚中工作?【英文标题】:Why is "#.id" a bad selector in CSS/jQuery yet it works in an HTML anchor?为什么“#.id”在 CSS/jQuery 中是一个糟糕的选择器,但它在 HTML 锚中工作? 【发布时间】:2016-06-29 02:13:36 【问题描述】:我正在使用 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/>
【问题讨论】:
一个点在选择器中具有特定的含义:它表示后面的单词是一个类名。'#\\.someMethodName'
我敢说这是 JSDoc 的创建者做出的一个糟糕的设计决定。
@cst1992:请不要用“有趣”的编辑建议浪费审稿人的时间。 SO is the place where we hate fun.
【参考方案1】:
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 组成。
因此,该句点是为类选择器保留的(类似地由句点后跟一个标识符组成)。
简而言之,解析器期望在#
之后有一个CSS 标识,但由于紧随其后的.
而没有找到一个,这使得选择器无效。这是令人惊讶的,因为 ID 选择器的表示法实际上是基于片段标识符的 URI 表示法——这从它们都以 #
符号开头的事实很明显,而且它们都被使用通过该标识符在文档中引用唯一标识的元素。期望在 URI 片段中工作的任何东西也能在 ID 选择器中工作并不是没有道理的——而且在大多数情况下它是正确的。但是因为 CSS 有自己的语法,它不一定与 URI 语法相关(因为它们是两个完全不相关的标准1),你会遇到像这样的边缘情况。
由于句点是片段标识符的一部分,您需要使用反斜杠对其进行转义,以便在 ID 选择器中使用它:
#\.someMethodName
不要忘记您需要在 javascript 字符串中转义反斜杠本身(例如,用于 document.querySelector()
和 jQuery):
document.querySelector('#\\.someMethodName')
$('#\\.someMethodName')
1几年前,W3C Community Group(我是其中的一员)围绕着一个名为使用 CSS 选择器作为片段标识符的提议成立,该提议,您可以想象,以一种有趣的方式将这两种技术结合在一起。然而,这从未成功,唯一已知的实现是一些可能甚至没有得到维护的浏览器扩展。
【讨论】:
【参考方案2】:为什么浏览器本身接受这个锚点,而 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 选择器,它可能会在使用它之前正确转义它。
【讨论】:
... 在这种情况下,它不会真的将其用作 CSS 选择器:P +1【参考方案3】:对于HTML5 是一个有效的id 属性:
对于 ID 可以采用的形式没有其他限制;在 特别是,ID 可以只包含数字,以数字开头,开始 带下划线,仅包含标点符号等
由于它不是有效的CSS identifier,为了将它与querySelector()
或$()
一起使用,您应该像这样转义它:
#\\.someMethodName
Mozilla Developer Network:
匹配不遵循 CSS 语法的 ID 或选择器(通过使用 例如冒号或空格),您必须转义 带有反斜杠的字符。因为反斜杠是转义字符 在 JavaScript 中,如果您输入的是文字字符串,您必须转义 它两次(一次用于 JavaScript 字符串,另一次用于 查询选择器):
请注意它不是有效的HTML4 id 属性
【讨论】:
@tac 应该向他们报告什么?他们的代码符合 html 5 规范,适用于所有浏览器,但不是有效的 html 4? @CodesInChaos “请注意它不是有效的 HTML4 id 属性”。 OP 的问题清楚地表明这是不直观的、破坏性的行为,使应用 JQ 或 CSS 变得痛苦。 @tac:这到底是 JSDoc 的作者还是 HTML 工作组的错? HTML 4 id 属性的设计甚至没有考虑到 CSS 或 jQuery - 限制来自 SGML 中的 NAME 标记,它是在这两个东西中的任何一个存在之前 年 设计的。很明显,这些 ID 旨在命名为锚点,仅此而已 - 正如我的回答中已经提到的(唯一这样做的),.someMethodName
是一个完全合法的 URI 片段标识符。请求更改 ID 只是为了让 you 更容易编写选择器是愚蠢的。【参考方案4】:
关于 HTML5 ID 命名约定
在 HTML5 中,您可以name your ID attributes anything you want 几乎没有语法限制:
对于 ID 可以采用的形式没有其他限制;特别是,ID 可以仅包含数字、以数字开头、以下划线开头、仅包含标点符号等
需要什么选择器函数
但是,当使用诸如 Document.querySelector() 之类的 JavsScript 选择器时,请务必注意如何评估其参数的语法。
返回文档中与指定选择器组匹配的第一个元素(使用文档节点的深度优先预序遍历|按文档标记中的第一个元素,并按子节点数量的顺序遍历顺序节点) .
element = document.querySelector(selectors);
element
是一个元素对象。selectors
是一个字符串,包含一个或多个用逗号分隔的 CSS 选择器。
当你混淆选择器函数时
所以在这里,我们看到它正在尝试解析CSS selectors。基本上,该函数将任何以#
开头的字符串解释为class,并将任何以#
开头的字符串解释为id,所以当你尝试传递它时像这样的字符串:
#.someMethodName
它认为您正在尝试将一个 id 和一个类全部解析为单个参数,并抛出一个错误,称其为语法错误。
结论
总之,虽然您的 ID 值在技术上是有效的,但使用 .
和 #
会混淆那些 JavaScript 选择器函数,例如 $(selector)
和 document.querySelector(selector)
等。
要解决此问题,您需要通过转义非标识符字符来让函数知道您正在尝试使用 .
或 #
作为字符而不是标识符:
#\\.someMethodName
Working demo of this in action
【讨论】:
【参考方案5】:在查询元素之前,您必须使用 \\
转义 .
。
替换
document.querySelector('#.someMethodName');
到
document.querySelector('#\\.someMethodName');
还请注意,从技术上讲,对于HTML 4 所需的 ID 值格式指定如下:
ID 和 NAME 标记必须以字母 ([A-Za-z]) 开头,后跟任意数量的字母、数字 ([0-9])、连字符 ("-")、下划线 (" _")、冒号 (":") 和句点 (".")。
所以.[A-Za-z]
是无效的..
【讨论】:
这在 许多 年前被 HTML5 规范取代,该规范取消了这些限制(考虑到浏览器从未强加这些限制,人们很乐意忽略它们)。在 HTML 中,id
属性的唯一要求是它不能有任何空格,并且必须是唯一的。以上是关于为啥“#.id”在 CSS/jQuery 中是一个糟糕的选择器,但它在 HTML 锚中工作?的主要内容,如果未能解决你的问题,请参考以下文章