为什么使用未转义破折号“-”字符的正则表达式似乎无法在 HTML 中的“pattern”属性中工作,但在 regex101 上测试时却可以工作?

Mar*_*ark 10 html regex

<div class="formRow">
    <label for="phone">Phone number</label>
    <input name="custPhone" id="phone" type="tel" placeholder="(nnn) nnn-nnnn" 
     pattern="^\d{10}$|^(\(\d{3}\)\s*)?\d{3}[\s-]?\d{4}$" > 
</div>
Run Code Online (Sandbox Code Playgroud)

这来自一本教科书(尽管已经有几年了),我不知道为什么浏览器只接受任何内容。

任何人都可以看到是什么可能阻止正则表达式完成其工作以验证 7 或 10 位电话号码并拒绝其他模式?

当我只使用表达式并在 regex101.com 上进行测试时,该表达式似乎在那里完成了它的工作。

^\d{10}$|^(\(\d{3}\)\s*)?\d{3}[\s-]?\d{4}$

在 regex101.com 上,1234567 将匹配,1234567890 将匹配,其他少于 7、多于 10 的模式以及包含 8 和 9 位数字的模式将被拒绝。

教科书的其他正则表达式验证很好。它似乎与浏览器无关,因为它在两种不同的浏览器上失败。

VLA*_*LAZ 16

为什么无效

提供给属性的值pattern将转换为带有unicode 集v标志的正则表达式。

HTML 标准定义:

输入元素的已编译模式正则表达式(如果存在)是 JavaScript RegExp 对象。其确定如下:

  1. 如果该元素没有pattern指定属性,则不返回任何内容。该元素没有已编译的模式正则表达式。

  2. patternpattern为元素属性的值。

  3. regexpCompletion为 RegExpCreate( pattern , "v")。

  4. 如果regexpCompletion是突然完成,则不返回任何内容。该元素没有已编译的模式正则表达式。

  5. anchoredPattern为字符串“^(?:”,后跟pattern,后跟“)$”。

  6. 返回 !RegExpCreate(锚定模式,“v”)。

v(unicode 集) 模式下,必须转义字符类内的单个破折号(以及在外部有效的其他值v),即使它位于括号旁边。演示:

const nonV = new RegExp(/[a-]/);

console.log('nonV match "a"', nonV.test("a"));
console.log('nonV match "-"', nonV.test("-"));
console.log('nonV match "b"', nonV.test("b"));

try {
  const v = new RegExp(/[a-]/, "v"); // throws an error

  console.log('v match "a"', v.test("a"));
  console.log('v match "-"', v.test("-"));
  console.log('v match "-"', v.test("b"));
  
} catch (error) {
  console.error("cannot use unescaped dash:", error.message)
}

const vEscaped = new RegExp(/[a\-]/, "v");

console.log('vEscaped match "a"', vEscaped.test("a"));
console.log('vEscaped match "-"', vEscaped.test("-"));
console.log('vEscaped match "b"', vEscaped.test("b"));
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important }
Run Code Online (Sandbox Code Playgroud)

什么时候失效的?

u这于 2023 年 4 月从之前的(unicode)标志更改为v(unicode set),从而导致使用pattern具有某些未转义值的属性的 HTML 发生重大更改。在针对 HTML 标准这一更改的 GutHub Pull 请求中,Mathias Bynens 表示:

[重大更改]一些以前有效的模式现在是错误的,特别是那些字符类包含未转义特殊字符 ( ) [ ] { } / - \ |双标点符号的模式:

pattern="[(]"
pattern="[)]"
pattern="[[]"
pattern="[{]"
pattern="[}]"
pattern="[/]"
pattern="[-]"
pattern="[|]"
pattern="[&&]"
pattern="[!!]"
pattern="[##]"
pattern="[$$]"
pattern="[%%]"
pattern="[**]"
pattern="[++]"
pattern="[,,]"
pattern="[..]"
pattern="[::]"
pattern="[;;]"
pattern="[<<]"
pattern="[==]"
pattern="[>>]"
pattern="[??]"
pattern="[@@]"
pattern="[``]"
pattern="[~~]"
pattern="[_^^]"
Run Code Online (Sandbox Code Playgroud)

抛出模式会导致inputElement.validity.valid === true任何输入值,因此唯一的兼容性风险是以前会导致的某些值/模式组合inputElement.validity.valid === false现在会导致inputElement.validity.valid === true.

如果使先前有效的模式无效,其影响被认为很低,并且权衡允许使用更强大的模式。PR 中的示例:

pattern="[\p{ASCII_Hex_Digit}--[Ff]]"
pattern="\p{RGI_Emoji}"
pattern="[_\q{a|bc|def}]"
Run Code Online (Sandbox Code Playgroud)

使固定

因此, HTML中的-in[\s-]需要进行转义:

:invalid {
  background-color: red;
  color: white;
}
Run Code Online (Sandbox Code Playgroud)
<div class="formRow">
    <label for="phone">Phone number</label>
    <input name="custPhone" id="phone" type="tel" placeholder="(nnn) nnn-nnnn" 
     pattern="^\d{10}$|^(\(\d{3}\)\s*)?\d{3}[\s\-]?\d{4}$" > 
</div>
Run Code Online (Sandbox Code Playgroud)

相同的修复需要应用于现在在v(unicode 集)模式下无效的任何其他模式。

有关详细信息,请参阅使用 RegExp u 标志有效,但使用 v 标志无效