为啥“#.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 是一个有效的片段标识符,这就是&lt;a href="#.someMethodName"&gt; 起作用的原因。

但是#.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";
&lt;div id=".someMethodName"&gt;I'm green because I was found by &lt;code&gt;document.getElementById(".someMethodName")&lt;/code&gt;&lt;/div&gt;

虽然 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 锚中工作?的主要内容,如果未能解决你的问题,请参考以下文章

为啥php每次在测试环境(WAMP)中生成相同的会话ID?

为啥 -1**2 在 JavaScript 中是语法错误?

为啥main方法在java中是静态的[重复]

为啥局部变量在 Java 中是线程安全的

函数在 Swift 中是值类型还是引用类型?为啥?

为啥 no_data_found ORA-01403 在 Oracle 中是一个异常?