1
- import { isFieldElement , readValidationMessages } from "../util"
1
+ import { isAriaInvalid , isFieldElement , readValidationMessages } from "../util"
2
2
3
3
export default class {
4
- selector = "input[type=checkbox]:required "
4
+ selector = "input[type=checkbox]"
5
5
ignoringMutations = false
6
6
7
7
constructor ( constraintValidations , predicate ) {
@@ -13,15 +13,17 @@ export default class {
13
13
}
14
14
15
15
connect ( ) {
16
+ this . element . addEventListener ( "invalid" , this . handleInvalid , { capture : true , passive : true } )
16
17
this . mutationObserver . observe ( this . element , {
17
18
attributeFilter : [ "required" ] ,
18
19
childList : true ,
19
20
subtree : true
20
21
} )
21
- this . reportValidationMessages ( this . element . querySelectorAll ( this . selector ) )
22
+ this . reportValidationMessages ( this . element . querySelectorAll ( this . selector ) , isAriaInvalid )
22
23
}
23
24
24
25
disconnect ( ) {
26
+ this . element . removeEventListener ( "invalid" , this . handleInvalid , { capture : true , passive : true } )
25
27
this . mutationObserver . disconnect ( )
26
28
}
27
29
@@ -30,8 +32,8 @@ export default class {
30
32
}
31
33
32
34
validate ( target ) {
33
- const checkboxesInGroup = checkboxGroup ( target ) . filter ( isFieldElement )
34
- const allRequired = checkboxesInGroup . every ( ( checkbox ) => checkbox . getAttribute ( "aria-required" ) === "true" )
35
+ const checkboxesInGroup = checkboxGroup ( target ) . filter ( isCheckboxElement )
36
+ const allRequired = checkboxesInGroup . every ( ( checkbox ) => isRequired ( checkbox ) )
35
37
const someChecked = checkboxesInGroup . some ( ( checkbox ) => checkbox . checked )
36
38
37
39
if ( allRequired && someChecked ) {
@@ -50,6 +52,18 @@ export default class {
50
52
51
53
// Private
52
54
55
+ handleInvalid = ( { target } ) => {
56
+ const checkboxes = new Set
57
+
58
+ for ( const element of target . form . elements ) {
59
+ if ( isCheckboxElement ( element ) && this . willValidate ( element ) ) {
60
+ checkboxes . add ( element )
61
+ }
62
+ }
63
+
64
+ this . reportValidationMessages ( checkboxes )
65
+ }
66
+
53
67
handleMutation = ( mutationRecords ) => {
54
68
if ( this . ignoringMutations ) return
55
69
@@ -61,22 +75,23 @@ export default class {
61
75
target . removeAttribute ( "aria-required" )
62
76
}
63
77
} else if ( addedNodes . length ) {
64
- this . reportValidationMessages ( addedNodes )
78
+ this . reportValidationMessages ( addedNodes , isAriaInvalid )
65
79
}
66
80
}
67
81
}
68
82
69
- reportValidationMessages ( nodes ) {
83
+ reportValidationMessages ( nodes , willReport = ( ) => true ) {
70
84
const requiredCheckboxes = querySelectorAllNodes ( this . selector , nodes )
71
85
72
86
for ( const checkbox of requiredCheckboxes ) {
73
- if ( checkbox . required ) {
87
+ if ( isRequired ( checkbox ) ) {
74
88
const group = checkboxGroup ( checkbox )
75
89
76
90
if ( this . willValidateGroup ( group ) ) {
77
91
for ( const checkboxInGroup of group ) {
78
92
this . swapRequiredWithAriaRequired ( checkboxInGroup )
79
- if ( checkboxInGroup . getAttribute ( "aria-invalid" ) === "true" ) {
93
+
94
+ if ( willReport ( checkboxInGroup ) ) {
80
95
this . validate ( checkboxInGroup )
81
96
}
82
97
}
@@ -138,3 +153,11 @@ function querySelectorAllNodes(selector, nodes, elements = new Set) {
138
153
139
154
return Array . from ( elements )
140
155
}
156
+
157
+ function isCheckboxElement ( element ) {
158
+ return isFieldElement ( element ) && element . type === "checkbox"
159
+ }
160
+
161
+ function isRequired ( element ) {
162
+ return element . required || element . getAttribute ( "aria-required" ) === "true"
163
+ }
0 commit comments