Issue
<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>
This comes from a textbook (albeit a few years old) and I don't know why the browser just accepts anything.
Can anyone see what might be preventing the regex from doing its job to get 7 or 10 digit phone numbers to be validated and other patterns to be rejected?
When I take just the expression and test at regex101.com, the expression seems to do its job there.
^\d{10}$|^(\(\d{3}\)\s*)?\d{3}[\s-]?\d{4}$
On regex101.com 1234567 will match and 1234567890 will match and other patterns with fewer than 7, more than 10 and patterns with 8, and 9 digits will be rejected.
The textbook's other regex validation is fine. It doesn't seem to be related to the browser as it fails with two different browsers.
Solution
Why is it invalid
The value provided to a pattern
attribute is converted into a regular expression with the v
flag for unicode sets.
The HTML standard defines that:
The compiled pattern regular expression of an input element, if it exists, is a JavaScript RegExp object. It is determined as follows:
If the element does not have a
pattern
attribute specified, then return nothing. The element has no compiled pattern regular expression.Let pattern be the value of the
pattern
attribute of the element.Let regexpCompletion be RegExpCreate(pattern, "v").
If regexpCompletion is an abrupt completion, then return nothing. The element has no compiled pattern regular expression.
Let anchoredPattern be the string "^(?:", followed by pattern, followed by ")$".
Return ! RegExpCreate(anchoredPattern, "v").
In v
(unicode sets) mode, it is mandatory to escape a single dash inside a character class (among other values that are valid outside v
) even if it is next to a bracket. Demo:
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"));
.as-console-wrapper { max-height: 100% !important }
When did it become invalid?
This was changed in April 2023 from the previous u
(unicode) flag to v
(unicode sets), thus resulting in a breaking change for HTML that uses the pattern
attribute with certain unescaped values. From the GutHub Pull Request for this change to the HTML standard, Mathias Bynens said:
[BREAKING CHANGE] Some previously valid patterns are now errors, specifically those with a character class including either an unescaped special character
(
)
[
]
{
}
/
-
\
|
or a double punctuator: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="[_^^]"
Throwing patterns result in
inputElement.validity.valid === true
for any input value, so the only compatibility risk is that some value/pattern combinations that would previously result ininputElement.validity.valid === false
now result ininputElement.validity.valid === true
.
The impact of if invalidating previously valid patterns was considered to be low and the trade off allows using more powerful patterns to be used. Examples from the PR:
pattern="[\p{ASCII_Hex_Digit}--[Ff]]" pattern="\p{RGI_Emoji}" pattern="[_\q{a|bc|def}]"
Fix
Therefore, the -
in [\s-]
needs to be escaped in HTML:
:invalid {
background-color: red;
color: white;
}
<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>
The same fix would need to be applied to any other pattern that is now invalid with v
(unicode sets) mode.
For more information see Valid with the RegExp u flag, but not with the v flag
Answered By - VLAZ
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.