Skip to content

Commit 5e1a84c

Browse files
committed
Checkboxradio: Fix finding of form parent and simplify finding of group
1 parent 4d6c6c2 commit 5e1a84c

File tree

4 files changed

+169
-62
lines changed

4 files changed

+169
-62
lines changed

tests/unit/checkboxradio/checkboxradio.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<title>jQuery UI Checkboxradio Test Suite</title>
66

77
<script src="../../lib/css.js" data-modules="core button checkboxradio"></script>
8-
<script src="../../lib/bootstrap.js" data-modules="common core methods options"></script>
8+
<script src="../../lib/bootstrap.js" data-widget="button"></script>
99
</head>
1010
<body>
1111

@@ -17,15 +17,15 @@
1717
<input type="radio" id="radio02" name="radio"><label for="radio02">Choice 2</label>
1818
<input type="radio" id="radio03" name="radio"><label for="radio03">Choice 3</label>
1919
</div>
20-
<form>
21-
<div id="radio1" style="margin-top: 2em;">
20+
<form id="form1">
21+
<div id="radio1">
2222
<input type="radio" id="radio11" name="radio"><label for="radio11">Choice 1</label>
23-
<input type="radio" id="radio12" name="radio" checked="checked"><label for="radio12">Choice 2</label>
24-
<input type="radio" id="radio13" name="radio"><label for="radio13">Choice 3</label>
23+
<input type="radio" id="radio12" name="radio"><label for="radio12">Choice 2</label>
24+
<input type="radio" id="radio13" name="radio" checked="checked"><label for="radio13">Choice 3</label>
2525
</div>
2626
</form>
27-
<form>
28-
<div id="radio2" style="margin-top: 2em;">
27+
<form id="form2">
28+
<div id="radio2">
2929
<input type="radio" id="radio21" name="radio"><label for="radio21">Choice 1</label>
3030
<input type="radio" id="radio22" name="radio"><label for="radio22">Choice 2</label>
3131
<input type="radio" id="radio23" name="radio" checked="checked"><label for="radio23">Choice 3</label>

tests/unit/checkboxradio/core.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ function radioFormAssert( noForm, form1, form2, assert ) {
5454
test( "radio groups", function( assert ) {
5555
expect( 36 );
5656
$( "input[type=radio]").checkboxradio();
57-
radioFormAssert( ":eq(0)", ":eq(1)", ":eq(2)", assert );
57+
radioFormAssert( ":eq(0)", ":eq(2)", ":eq(2)", assert );
5858

5959
// click outside of forms
6060
$( "#radio0 .ui-button:eq(1)" ).simulate( "click" );
61-
radioFormAssert(":eq(1)", ":eq(1)", ":eq(2)", assert );
61+
radioFormAssert(":eq(1)", ":eq(2)", ":eq(2)", assert );
6262

6363
// click in first form
6464
$( "#radio1 .ui-button:eq(0)").simulate( "click" );

tests/unit/checkboxradio/events.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
define( [
2+
"jquery",
3+
"ui/checkboxradio"
4+
], function( $ ) {
5+
6+
module( "Checkboxradio: events" );
7+
8+
asyncTest( "form reset / click", function( assert ) {
9+
expect( 35 );
10+
11+
var radios = [
12+
$( "#radio11" ).checkboxradio(),
13+
$( "#radio12" ).checkboxradio(),
14+
$( "#radio13" ).checkboxradio()
15+
],
16+
widgets = [
17+
radios[ 0 ].checkboxradio( "widget" ),
18+
radios[ 1 ].checkboxradio( "widget" ),
19+
radios[ 2 ].checkboxradio( "widget" )
20+
],
21+
form1 = $( "#form1" ),
22+
form2 = $( "#form2" );
23+
24+
function assertChecked( checked ) {
25+
$.each( widgets, function( index ) {
26+
var method = index === checked ? "hasClasses" : "lacksClasses";
27+
28+
assert[ method ]( widgets[ index ], "ui-checkboxradio-checked" );
29+
} );
30+
}
31+
32+
function assertFormCount( count ) {
33+
equal( form1.data( "uiCheckboxradioCount" ), count, "Form1 has a count of " + count );
34+
equal( form2.data( "uiCheckboxradioCount" ), 3, "Form2 has a count of 3" );
35+
}
36+
37+
function testForms( original, current, start ) {
38+
var count = 3 - current;
39+
assertChecked( original );
40+
41+
if ( !start && current !== 0 ) {
42+
radios[ current - 1 ].checkboxradio( "destroy" );
43+
}
44+
45+
assertFormCount( count );
46+
47+
radios[ current ].prop( "checked", true );
48+
radios[ current ].trigger( "change" );
49+
assertChecked( current );
50+
51+
form1.trigger( "reset" );
52+
}
53+
54+
$( "#form2 input" ).checkboxradio();
55+
56+
testForms( 2, 0, true );
57+
58+
setTimeout( function() {
59+
testForms( 2, 0 );
60+
61+
setTimeout( function() {
62+
testForms( 2, 1 );
63+
64+
setTimeout( function() {
65+
testForms( 2, 2 );
66+
67+
setTimeout( function() {
68+
radios[ 2 ].checkboxradio( "destroy" );
69+
assertChecked( false );
70+
start();
71+
} );
72+
});
73+
});
74+
});
75+
76+
} );
77+
78+
} );

ui/checkboxradio.js

Lines changed: 82 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ var formResetHandler = function() {
3434
form.find( ".ui-checkboxradio" ).checkboxradio( "refresh" );
3535
} );
3636
},
37-
escapeId = new RegExp( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g ),
38-
forms = {};
37+
escapeId = new RegExp( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g );
3938

4039
$.widget( "ui.checkboxradio", {
4140
version: "@VERSION",
@@ -84,17 +83,19 @@ $.widget( "ui.checkboxradio", {
8483
},
8584

8685
_create: function() {
87-
this.formElement = $( this.element[ 0 ].form ).uniqueId();
88-
this.formId = this.formElement.attr( "id" );
86+
this.formParent = this._getFormParent();
8987

90-
forms[ this.formId ] = forms[ this.formId ] || 0;
88+
// We don't use _on and _off here because we want all the checkboxes in the same form to use
89+
// single handler which handles all the checkboxradio widgets in the form
90+
var formCount = this.formParent.data( "uiCheckboxradioCount" ) || 0;
9191

9292
// We don't use _on and _off here because we want all the checkboxes in the same form to use
9393
// single handler which handles all the checkboxradio widgets in the form
94-
if ( forms[ this.formId ] === 0 ) {
95-
this.formElement.on( "reset." + this.widgetFullName, formResetHandler );
94+
if ( formCount === 0 ) {
95+
this.formParent.on( "reset." + this.widgetFullName, formResetHandler );
9696
}
97-
forms[ this.formId ]++;
97+
98+
this.formParent.data( "uiCheckboxradioCount", formCount + 1 );
9899

99100
if ( this.options.disabled == null ) {
100101
this.options.disabled = this.element[ 0 ].disabled || false;
@@ -115,49 +116,50 @@ $.widget( "ui.checkboxradio", {
115116

116117
_findLabel: function() {
117118
var ancestor, labelSelector, id,
118-
parent = this.element.closest( "label" );
119+
parent;
119120

120121
// Check control.labels first
121122
if ( this.element[ 0 ].labels !== undefined && this.element[ 0 ].labels.length > 0 ) {
122123
this.label = $( this.element[ 0 ].labels[ 0 ] );
123-
} else {
124-
parent = this.element.closest( "label" );
124+
return;
125+
}
125126

126-
if ( parent.length > 0 ) {
127-
this.label = parent;
128-
this.parentLabel = true;
129-
return;
130-
}
127+
parent = this.element.closest( "label" );
128+
129+
if ( parent.length > 0 ) {
130+
this.label = parent;
131+
this.parentLabel = true;
132+
return;
133+
}
131134

132-
// We don't search against the document in case the element
133-
// is disconnected from the DOM
134-
ancestor = this.element.parents().last();
135+
// We don't search against the document in case the element
136+
// is disconnected from the DOM
137+
ancestor = this.element.parents().last();
135138

136-
// Look for the label based on the id
137-
id = this.element.attr( "id" );
138-
if ( id ) {
139-
labelSelector = "label[for='" +
140-
this.element.attr( "id" ).replace( escapeId, "\\$1" ) + "']";
141-
this.label = ancestor.find( labelSelector );
139+
// Look for the label based on the id
140+
id = this.element.attr( "id" );
141+
if ( id ) {
142+
labelSelector = "label[for='" +
143+
this.element.attr( "id" ).replace( escapeId, "\\$1" ) + "']";
144+
this.label = ancestor.find( labelSelector );
142145

143-
if ( !this.label.length ) {
146+
if ( !this.label.length ) {
144147

145-
// The label was not found, make sure ancestors exist. If they do check their
146-
// siblings, if they dont check the elements siblings
147-
ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
148+
// The label was not found, make sure ancestors exist. If they do check their
149+
// siblings, if they dont check the elements siblings
150+
ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
148151

149-
// Check if any of the new set of ancestors is the label
150-
this.label = ancestor.filter( labelSelector );
151-
if ( !this.label.length ) {
152+
// Check if any of the new set of ancestors is the label
153+
this.label = ancestor.filter( labelSelector );
154+
if ( !this.label.length ) {
152155

153-
// Still not found look inside the ancestors for the label
154-
this.label = ancestor.find( labelSelector );
155-
}
156+
// Still not found look inside the ancestors for the label
157+
this.label = ancestor.find( labelSelector );
156158
}
157159
}
158-
if ( !this.label || !this.label.length ) {
159-
$.error( "No label found for checkboxradio widget" );
160-
}
160+
}
161+
if ( !this.label || !this.label.length ) {
162+
$.error( "No label found for checkboxradio widget" );
161163
}
162164
},
163165

@@ -196,20 +198,44 @@ $.widget( "ui.checkboxradio", {
196198
return this.label;
197199
},
198200

201+
_getFormParent: function( element ) {
202+
var parent;
203+
204+
element = element || this.element[ 0 ];
205+
206+
if ( element.form && typeof element.form !== "string" ) {
207+
return $( element.form );
208+
} else if ( !element.form ) {
209+
return $( "body" );
210+
}
211+
212+
// Support: IE8 only ( the rest of the method )
213+
// IE8 supports the form property but not the form attribute worse yet it if you supply the
214+
// form attribute it overwrites the form property with the string. Other supported browsers
215+
// like all other IE's and android 2.3 support the form attribute not at all or partially
216+
// we dont care in those cases because they still always return an element. We are not
217+
// trying to fix the form attribute here only deal with the prop supplying a string.
218+
parent = this.document.getElementByID( element.form );
219+
if ( parent.length ) {
220+
return $( parent );
221+
}
222+
parent = $( element ).closest( "form" );
223+
if ( parent.length ) {
224+
return parent;
225+
}
226+
return $( "body" );
227+
},
228+
199229
_getRadioGroup: function() {
200230
var name = this.element[ 0 ].name,
201-
form = this.element[ 0 ].form,
231+
that = this,
202232
radios = $( [] );
233+
203234
if ( name ) {
204235
name = name.replace( escapeId, "\\$1" );
205-
if ( form ) {
206-
radios = $( form ).find( "[name='" + name + "']" );
207-
} else {
208-
radios = this.document.find( "[name='" + name + "']" )
209-
.filter( function() {
210-
return !this.form;
211-
} );
212-
}
236+
radios = this.formParent.find( "[name='" + name.replace( escapeId, "\\$1" ) + "']" ).filter( function() {
237+
return that._getFormParent( this )[ 0 ] === that.formParent[ 0 ];
238+
} );
213239
}
214240
return radios.not( this.element );
215241
},
@@ -238,14 +264,17 @@ $.widget( "ui.checkboxradio", {
238264
},
239265

240266
_destroy: function() {
241-
if ( this.icon ) {
242-
this.icon.remove();
243-
this.iconSpace.remove();
267+
var formCount = this.formParent.data( "uiCheckboxradioCount" ) - 1;
268+
269+
this.formParent.data( "uiCheckboxradioCount", formCount );
270+
271+
if ( formCount === 0 ) {
272+
this.formParent.off( "reset." + this.widgetFullName, formResetHandler );
244273
}
245274

246-
forms[ this.formId ]--;
247-
if ( forms[ this.formId ] === 0 ) {
248-
this.formElement.off( "reset." + this.widgetFullName, formResetHandler );
275+
if ( formCount === 0 ) {
276+
this.icon.remove();
277+
this.iconSpace.remove();
249278
}
250279
},
251280

0 commit comments

Comments
 (0)