Skip to content

Commit 1e2d314

Browse files
committed
Tabs: Deprecate url method; use aria-controls instead of title to specify panels. Fixes #7132 Tabs: Deprecate url method; use aria-controls instead of title to specify panels
1 parent f6e7b6c commit 1e2d314

File tree

5 files changed

+92
-63
lines changed

5 files changed

+92
-63
lines changed

demos/tabs/ajax.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
$( "#tabs" ).tabs({
1515
ajaxOptions: {
1616
error: function( xhr, status, index, anchor ) {
17-
$( anchor.hash ).html(
17+
var selector = $( anchor ).attr( "aria-controls" );
18+
$( selector ).html(
1819
"Couldn't load this tab. We'll try to fix this as soon as possible. " +
1920
"If this wouldn't be a demo." );
2021
}

tests/unit/tabs/tabs_deprecated.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
(function( $ ) {
22

3+
module("tabs (deprecated): core");
4+
5+
test( "#4581 - title attribute for remote tabs does not support foreign languages", function() {
6+
expect( 1 );
7+
8+
$( "#tabs2" ).tabs({
9+
selected: 3,
10+
beforeload: function( event, ui ) {
11+
event.preventDefault();
12+
equal( ui.panel.id, "∫ßáö_Սե", "proper title" );
13+
}
14+
});
15+
});
16+
317
module("tabs (deprecated): options");
418

519
test('ajaxOptions', function() {
@@ -148,4 +162,14 @@ test('length', function() {
148162
equals(el.tabs('length'), $('ul a', el).length, ' should return length');
149163
});
150164

165+
test('url', function() {
166+
el = $('#tabs2').tabs();
167+
var tab = el.find('a:eq(3)'),
168+
url = tab.attr('href');
169+
170+
el.tabs('url', 3, "data/test2.html");
171+
equals(tab.attr('href'), 'data/test2.html', 'Url was updated');
172+
tab.attr('href', url );
173+
});
174+
151175
}( jQuery ) );

tests/unit/tabs/tabs_methods.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,4 @@ test('load', function() {
191191
ok(false, "missing test - untested code is broken code.");
192192
});
193193

194-
test('url', function() {
195-
ok(false, "missing test - untested code is broken code.");
196-
});
197-
198194
})(jQuery);

tests/unit/tabs/tabs_tickets.js

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,29 @@ test('#3627 - Ajax tab with url containing a fragment identifier fails to load',
4242
// http://dev.jqueryui.com/ticket/3627
4343
expect(1);
4444

45-
el = $('#tabs2').tabs();
46-
47-
ok(/test.html$/.test( $('a:eq(2)', el).data('load.tabs') ), 'should ignore fragment identifier');
48-
45+
el = $('#tabs2').tabs({
46+
selected: 2,
47+
beforeload: function( event, ui ) {
48+
event.preventDefault();
49+
ok(/test.html$/.test( ui.settings.url ), 'should ignore fragment identifier');
50+
}
51+
});
4952
});
5053

5154
test('#4033 - IE expands hash to full url and misinterprets tab as ajax', function() {
5255
// http://dev.jqueryui.com/ticket/4033
5356
expect(1);
5457

55-
el = $('<div><ul><li><a href="#tab">Tab</a></li></ul><div id="tab"></div></div>')
56-
.appendTo('#main').tabs();
58+
el = $('<div><ul><li><a href="#tab">Tab</a></li></ul><div id="tab"></div></div>');
59+
el.appendTo('#main');
60+
el.tabs({
61+
beforeload: function( event, ui ) {
62+
event.preventDefault();
63+
ok( false, 'should not be an ajax tab');
64+
}
65+
});
5766

58-
equals($('a', el).data('load.tabs'), undefined, 'should not create ajax tab');
59-
67+
equals($('a', el).attr('aria-controls'), '#tab', 'aria-contorls attribute is correct');
6068
});
6169

6270
test('#5893 - Sublist in the tab list are considered as tab', function() {
@@ -68,19 +76,6 @@ test('#5893 - Sublist in the tab list are considered as tab', function() {
6876

6977
});
7078

71-
asyncTest( "#4581 - title attribute for remote tabs does not support foreign languages", function() {
72-
expect( 1 );
73-
74-
$( "#tabs2" ).tabs({
75-
selected: 3,
76-
load: function( event, ui ) {
77-
equal( ui.panel.id, "∫ßáö_Սե", "proper title" );
78-
start();
79-
}
80-
});
81-
});
82-
83-
8479
test('#6710 - selectors are global', function() {
8580
// http://bugs.jqueryui.com/ticket/6710
8681
expect(1);

ui/jquery.ui.tabs.js

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ $.widget( "ui.tabs", {
9999
this.lis.removeClass( "ui-tabs-selected ui-state-active" );
100100
// check for length avoids error when initializing empty list
101101
if ( o.selected >= 0 && this.anchors.length ) {
102-
var temp = self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )
103-
.removeClass( "ui-tabs-hide" );
102+
var tab = self.anchors[ o.selected ],
103+
panel = self.element.find( self._sanitizeSelector( $( tab ).attr( "aria-controls" ) ) );
104+
105+
panel.removeClass( "ui-tabs-hide" );
104106

105107
this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
106108

107109
// seems to be expected behavior that the show callback is fired
108110
self.element.queue( "tabs", function() {
109-
self._trigger( "show", null,
110-
self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
111+
self._trigger( "show", null, self._ui( tab, panel[ 0 ] ) );
111112
});
112113

113114
this.load( o.selected );
@@ -133,7 +134,7 @@ $.widget( "ui.tabs", {
133134
},
134135

135136
_tabId: function( a ) {
136-
return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
137+
return ( $( a ).attr( "aria-controls" ) || "" ).replace( /^#/ , "" ) ||
137138
this.options.idPrefix + getNextTabId();
138139
},
139140

@@ -165,7 +166,7 @@ $.widget( "ui.tabs", {
165166

166167
// Remove panels that we created that are missing their tab
167168
this.element.find(".ui-tabs-panel:data(destroy.tabs)").each( function( index, panel ) {
168-
var anchor = self.anchors.filter( "[href$='#" + panel.id + "']");
169+
var anchor = self.anchors.filter( "[aria-controls='#" + panel.id + "']");
169170
if ( !anchor.length ) {
170171
$( panel ).remove();
171172
}
@@ -224,14 +225,17 @@ $.widget( "ui.tabs", {
224225
this.panels = $( [] );
225226

226227
this.anchors.each(function( i, a ) {
227-
var href = $( a ).attr( "href" );
228+
var href = $( a ).attr( "href" ),
229+
hrefBase = href.split( "#" )[ 0 ],
230+
selector,
231+
panel,
232+
baseEl;
233+
228234
// For dynamically created HTML that contains a hash as href IE < 8 expands
229235
// such href to the full page url with hash and then misinterprets tab as ajax.
230236
// Same consideration applies for an added tab with a fragment identifier
231237
// since a[href=#fragment-identifier] does unexpectedly not match.
232238
// Thus normalize href attribute...
233-
var hrefBase = href.split( "#" )[ 0 ],
234-
baseEl;
235239
if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
236240
( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
237241
href = a.hash;
@@ -240,32 +244,30 @@ $.widget( "ui.tabs", {
240244

241245
// inline tab
242246
if ( fragmentId.test( href ) ) {
243-
self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
247+
selector = href;
248+
panel = self.element.find( self._sanitizeSelector( selector ) );
244249
// remote tab
245250
// prevent loading the page itself if href is just "#"
246251
} else if ( href && href !== "#" ) {
247-
// required for restore on destroy
248-
$.data( a, "href.tabs", href );
249-
250-
// TODO until #3808 is fixed strip fragment identifier from url
251-
// (IE fails to load from such url)
252-
$.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
253-
254252
var id = self._tabId( a );
255-
a.href = "#" + id;
256-
var $panel = self.element.find( "#" + id );
257-
if ( !$panel.length ) {
258-
$panel = $( self.options.panelTemplate )
253+
selector = "#" + id;
254+
panel = self.element.find( selector );
255+
if ( !panel.length ) {
256+
panel = $( self.options.panelTemplate )
259257
.attr( "id", id )
260258
.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
259+
.data( "destroy.tabs", true )
261260
.insertAfter( self.panels[ i - 1 ] || self.list );
262-
$panel.data( "destroy.tabs", true );
263261
}
264-
self.panels = self.panels.add( $panel );
265262
// invalid tab href
266263
} else {
267264
self.options.disabled.push( i );
268265
}
266+
267+
if ( panel.length) {
268+
self.panels = self.panels.add( panel );
269+
}
270+
$( a ).attr( "aria-controls", selector );
269271
});
270272
},
271273

@@ -348,9 +350,9 @@ $.widget( "ui.tabs", {
348350
var self = this,
349351
o = this.options,
350352
el = event.currentTarget,
351-
$li = $(el).closest( "li" ),
353+
$li = $( el ).closest( "li" ),
352354
$hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
353-
$show = self.element.find( self._sanitizeSelector( el.hash ) );
355+
$show = self.element.find( self._sanitizeSelector( $( el ).attr( "aria-controls" ) ) );
354356

355357
// tab is already selected, but not collapsible
356358
if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible ) ||
@@ -455,10 +457,6 @@ $.widget( "ui.tabs", {
455457
this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
456458

457459
this.anchors.each(function() {
458-
var href = $.data( this, "href.tabs" );
459-
if ( href ) {
460-
this.href = href;
461-
}
462460
var $this = $( this ).unbind( ".tabs" );
463461
$.each( [ "href", "load" ], function( i, prefix ) {
464462
$this.removeData( prefix + ".tabs" );
@@ -558,8 +556,11 @@ $.widget( "ui.tabs", {
558556
var self = this,
559557
o = this.options,
560558
a = this.anchors.eq( index )[ 0 ],
561-
url = $.data( a, "load.tabs" ),
562-
eventData = self._ui( self.anchors[ index ], self.panels[ index ] );
559+
panel = self.element.find( self._sanitizeSelector( $( a ).attr( "aria-controls" ) ) ),
560+
// TODO until #3808 is fixed strip fragment identifier from url
561+
// (IE fails to load from such url)
562+
url = $( a ).attr( "href" ).replace( /#.*$/, "" ),
563+
eventData = self._ui( a, panel[ 0 ] );
563564

564565
if ( this.xhr ) {
565566
this.xhr.abort();
@@ -585,7 +586,7 @@ $.widget( "ui.tabs", {
585586

586587
this.xhr
587588
.success( function( response ) {
588-
self.element.find( self._sanitizeSelector( a.hash ) ).html( response );
589+
panel.html( response );
589590
})
590591
.complete( function( jqXHR, status ) {
591592
if ( status === "abort" ) {
@@ -609,13 +610,9 @@ $.widget( "ui.tabs", {
609610
// last, so that load event is fired before show...
610611
self.element.dequeue( "tabs" );
611612

612-
return this;
613-
},
614-
615-
url: function( index, url ) {
616-
this.anchors.eq( index ).data( "load.tabs", url );
617613
return this;
618614
}
615+
619616
});
620617

621618
$.extend( $.ui.tabs, {
@@ -861,6 +858,22 @@ if ( $.uiBackCompat !== false ) {
861858
return this.anchors.length;
862859
};
863860
}( jQuery, jQuery.ui.tabs.prototype ) );
861+
862+
// url method
863+
(function( $, prototype ) {
864+
prototype.url = function( index, url ) {
865+
this.anchors.eq( index ).attr( "href", url );
866+
};
867+
}( jQuery, jQuery.ui.tabs.prototype ) );
868+
869+
// _tabId method
870+
(function( $, prototype ) {
871+
var _tabId = prototype._tabId;
872+
prototype._tabId = function( a ) {
873+
return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
874+
_tabId.apply( this, arguments );
875+
};
876+
}( jQuery, jQuery.ui.tabs.prototype ) );
864877
}
865878

866879
})( jQuery );

0 commit comments

Comments
 (0)