Permalink
Browse files

Checkboxradio: Correctly retrieve label

This moves label-finding code into one function to help render the code
unit-testable. The new code also relies more on native means of getting the
label, which improves performance a great deal when the .labels property is
supported, and also improves performance when such a property is not supported
(such as in FF27).

http://jsperf.com/checkboxradio-label-retrieval-non-nested/5

Closes gh-7293
Fixes gh-7292
  • Loading branch information...
gabrielschulhof committed Apr 2, 2014
1 parent 00fb4e2 commit d9f5d211f7584a7f096158a3e8d0218589933c4f
@@ -39,15 +39,7 @@ $.widget( "mobile.checkboxradio", $.extend( {
return input.jqmData( dataAttr ) ||
input.closest( "form, fieldset" ).jqmData( dataAttr );
},
// NOTE: Windows Phone could not find the label through a selector
// filter works though.
parentLabel = input.closest( "label" ),
label = parentLabel.length ? parentLabel :
input
.closest( "form, fieldset, :jqmData(role='page'), :jqmData(role='dialog')" )
.find( "label" )
.filter( "[for='" + escapeId( input[0].id ) + "']" )
.first(),
label = this._findLabel(),
inputtype = input[0].type,
checkedClass = "ui-" + inputtype + "-on",
uncheckedClass = "ui-" + inputtype + "-off";
@@ -60,16 +52,17 @@ $.widget( "mobile.checkboxradio", $.extend( {
this.options.disabled = true;
}
o.iconpos = inheritAttr( input, "iconpos" ) || label.attr( "data-" + $.mobile.ns + "iconpos" ) || o.iconpos,
o.iconpos = inheritAttr( input, "iconpos" ) ||
label.element.attr( "data-" + $.mobile.ns + "iconpos" ) || o.iconpos,
// Establish options
o.mini = inheritAttr( input, "mini" ) || o.mini;
// Expose for other methods
$.extend( this, {
input: input,
label: label,
parentLabel: parentLabel,
label: label.element,
labelIsParent: label.isParent,
inputtype: inputtype,
checkedClass: checkedClass,
uncheckedClass: uncheckedClass
@@ -79,7 +72,7 @@ $.widget( "mobile.checkboxradio", $.extend( {
this._enhance();
}
this._on( label, {
this._on( label.element, {
vmouseover: "_handleLabelVMouseOver",
vclick: "_handleLabelVClick"
});
@@ -95,10 +88,36 @@ $.widget( "mobile.checkboxradio", $.extend( {
this.refresh();
},
_findLabel: function() {
var parentLabel, label, isParent,
input = this.element,
labelsList = input[ 0 ].labels;
if( labelsList && labelsList.length > 0 ) {
label = $( labelsList[ 0 ] );
isParent = $.contains( label[ 0 ], input[ 0 ] );
} else {
parentLabel = input.closest( "label" );
isParent = ( parentLabel.length > 0 );
// NOTE: Windows Phone could not find the label through a selector
// filter works though.
label = isParent ? parentLabel :
$( this.document[ 0 ].getElementsByTagName( "label" ) )
.filter( "[for='" + escapeId( input[ 0 ].id ) + "']" )
.first();
}
return {
element: label,
isParent: isParent
};
},
_enhance: function() {
this.label.addClass( "ui-btn ui-corner-all");
if ( this.parentLabel.length > 0 ) {
if ( this.labelIsParent ) {
this.input.add( this.label ).wrapAll( this._wrapper() );
} else {
//this.element.replaceWith( this.input.add( this.label ).wrapAll( this._wrapper() ) );
@@ -0,0 +1,44 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>jQuery Mobile Checkboxradio Input Set Test Suite</title>
<script src="../../../external/requirejs/require.js"></script>
<script src="../../../js/requirejs.config.js"></script>
<script src="../../../js/jquery.tag.inserter.js"></script>
<script src="../../../tests/jquery.testHelper.js"></script>
<script src="../../../external/qunit/qunit.js"></script>
<script>
$.testHelper.asyncLoad([
[
"widgets/forms/checkboxradio",
],
[
"find_label_tests_core.js"
]
]);
</script>
<link rel="stylesheet" href="../../../external/qunit/qunit.css"/>
<link rel="stylesheet" href="../../jqm-tests.css"/>
<script src="../../swarminject.js"></script>
</head>
<body>
<div id="qunit"></div>
<label id="separate-label-outside-form-label" for="separate-label-outside-form">Label</label>
<form>
<label id="separate-label-in-form-label" for="separate-label-in-form">Label</label>
<input type="checkbox" id="separate-label-in-form">
<input type="checkbox" id="separate-label-outside-form">
<label id="nested-input-inside-form-label">Label<input type="checkbox" id="nested-input-inside-form"></label>
</form>
<label id="separate-label-input-outside-form-label" for="separate-label-input-outside-form">Label</label>
<input type="checkbox" id="separate-label-input-outside-form">
<label id="nested-input-outside-form-label">Label<input type="checkbox" id="nested-input-outside-form"></label>
</body>
</html>
@@ -0,0 +1,51 @@
var pairs = [
{
label: "#separate-label-outside-form-label",
input: "#separate-label-outside-form",
isParent: false
},
{
label: "#separate-label-in-form-label",
input: "#separate-label-in-form",
isParent: false
},
{
label: "#nested-input-inside-form-label",
input: "#nested-input-inside-form",
isParent: true
},
{
label: "#separate-label-input-outside-form-label",
input: "#separate-label-input-outside-form",
isParent: false
},
{
label: "#nested-input-outside-form-label",
input: "#nested-input-outside-form",
isParent: true
}
];
test( "_findLabel() works correctly", function() {
var index, pair, result,
findLabel = $.mobile.checkboxradio.prototype._findLabel;
for ( index in pairs ) {
pair = $.extend( {}, pairs[ index ] );
pair.label = $( pair.label );
pair.input = $( pair.input );
result = findLabel.call({
element: pair.input,
document: $( document )
});
deepEqual( result.element.length > 0, true,
pair.input.attr( "id" ) + ": a label was found" );
deepEqual( result.element[ 0 ], pair.label[ 0 ],
pair.input.attr( "id" ) + ": the right label was found" );
deepEqual( result.isParent, pair.isParent,
pair.input.attr( "id" ) +
": the label was correctly identified as (not?) the parent" );
}
});

0 comments on commit d9f5d21

Please sign in to comment.