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

(cherry picked from commit d9f5d21)

Closes gh-7293
Fixes gh-7292
1 parent 73fd265 commit dca0976c0c7336b9567afb51ef00ffe995235edd @gabrielschulhof gabrielschulhof committed Apr 2, 2014
@@ -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 dca0976

Please sign in to comment.