From 1e2d3145fff7cdeca61e62f95eb1e30c37e664c5 Mon Sep 17 00:00:00 2001 From: David Petersen Date: Sat, 26 Mar 2011 22:14:17 -0400 Subject: [PATCH] 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 --- demos/tabs/ajax.html | 3 +- tests/unit/tabs/tabs_deprecated.js | 24 +++++++++ tests/unit/tabs/tabs_methods.js | 4 -- tests/unit/tabs/tabs_tickets.js | 37 ++++++------- ui/jquery.ui.tabs.js | 87 +++++++++++++++++------------- 5 files changed, 92 insertions(+), 63 deletions(-) diff --git a/demos/tabs/ajax.html b/demos/tabs/ajax.html index c62625791..284de57f9 100644 --- a/demos/tabs/ajax.html +++ b/demos/tabs/ajax.html @@ -14,7 +14,8 @@ $( "#tabs" ).tabs({ ajaxOptions: { error: function( xhr, status, index, anchor ) { - $( anchor.hash ).html( + var selector = $( anchor ).attr( "aria-controls" ); + $( selector ).html( "Couldn't load this tab. We'll try to fix this as soon as possible. " + "If this wouldn't be a demo." ); } diff --git a/tests/unit/tabs/tabs_deprecated.js b/tests/unit/tabs/tabs_deprecated.js index 880ea075e..ef03d84cb 100644 --- a/tests/unit/tabs/tabs_deprecated.js +++ b/tests/unit/tabs/tabs_deprecated.js @@ -1,5 +1,19 @@ (function( $ ) { +module("tabs (deprecated): core"); + +test( "#4581 - title attribute for remote tabs does not support foreign languages", function() { + expect( 1 ); + + $( "#tabs2" ).tabs({ + selected: 3, + beforeload: function( event, ui ) { + event.preventDefault(); + equal( ui.panel.id, "∫ßáö_Սե", "proper title" ); + } + }); +}); + module("tabs (deprecated): options"); test('ajaxOptions', function() { @@ -148,4 +162,14 @@ test('length', function() { equals(el.tabs('length'), $('ul a', el).length, ' should return length'); }); +test('url', function() { + el = $('#tabs2').tabs(); + var tab = el.find('a:eq(3)'), + url = tab.attr('href'); + + el.tabs('url', 3, "data/test2.html"); + equals(tab.attr('href'), 'data/test2.html', 'Url was updated'); + tab.attr('href', url ); +}); + }( jQuery ) ); diff --git a/tests/unit/tabs/tabs_methods.js b/tests/unit/tabs/tabs_methods.js index 50b8abd27..e00a95a3e 100644 --- a/tests/unit/tabs/tabs_methods.js +++ b/tests/unit/tabs/tabs_methods.js @@ -191,8 +191,4 @@ test('load', function() { ok(false, "missing test - untested code is broken code."); }); -test('url', function() { - ok(false, "missing test - untested code is broken code."); -}); - })(jQuery); diff --git a/tests/unit/tabs/tabs_tickets.js b/tests/unit/tabs/tabs_tickets.js index c301e9eb0..9df72a6f7 100644 --- a/tests/unit/tabs/tabs_tickets.js +++ b/tests/unit/tabs/tabs_tickets.js @@ -42,21 +42,29 @@ test('#3627 - Ajax tab with url containing a fragment identifier fails to load', // http://dev.jqueryui.com/ticket/3627 expect(1); - el = $('#tabs2').tabs(); - - ok(/test.html$/.test( $('a:eq(2)', el).data('load.tabs') ), 'should ignore fragment identifier'); - + el = $('#tabs2').tabs({ + selected: 2, + beforeload: function( event, ui ) { + event.preventDefault(); + ok(/test.html$/.test( ui.settings.url ), 'should ignore fragment identifier'); + } + }); }); test('#4033 - IE expands hash to full url and misinterprets tab as ajax', function() { // http://dev.jqueryui.com/ticket/4033 expect(1); - el = $('
') - .appendTo('#main').tabs(); + el = $('
'); + el.appendTo('#main'); + el.tabs({ + beforeload: function( event, ui ) { + event.preventDefault(); + ok( false, 'should not be an ajax tab'); + } + }); - equals($('a', el).data('load.tabs'), undefined, 'should not create ajax tab'); - + equals($('a', el).attr('aria-controls'), '#tab', 'aria-contorls attribute is correct'); }); 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() { }); -asyncTest( "#4581 - title attribute for remote tabs does not support foreign languages", function() { - expect( 1 ); - - $( "#tabs2" ).tabs({ - selected: 3, - load: function( event, ui ) { - equal( ui.panel.id, "∫ßáö_Սե", "proper title" ); - start(); - } - }); -}); - - test('#6710 - selectors are global', function() { // http://bugs.jqueryui.com/ticket/6710 expect(1); diff --git a/ui/jquery.ui.tabs.js b/ui/jquery.ui.tabs.js index 0157a3329..f3741f0a6 100755 --- a/ui/jquery.ui.tabs.js +++ b/ui/jquery.ui.tabs.js @@ -99,15 +99,16 @@ $.widget( "ui.tabs", { this.lis.removeClass( "ui-tabs-selected ui-state-active" ); // check for length avoids error when initializing empty list if ( o.selected >= 0 && this.anchors.length ) { - var temp = self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ) - .removeClass( "ui-tabs-hide" ); + var tab = self.anchors[ o.selected ], + panel = self.element.find( self._sanitizeSelector( $( tab ).attr( "aria-controls" ) ) ); + + panel.removeClass( "ui-tabs-hide" ); this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" ); // seems to be expected behavior that the show callback is fired self.element.queue( "tabs", function() { - self._trigger( "show", null, - self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) ); + self._trigger( "show", null, self._ui( tab, panel[ 0 ] ) ); }); this.load( o.selected ); @@ -133,7 +134,7 @@ $.widget( "ui.tabs", { }, _tabId: function( a ) { - return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) || + return ( $( a ).attr( "aria-controls" ) || "" ).replace( /^#/ , "" ) || this.options.idPrefix + getNextTabId(); }, @@ -165,7 +166,7 @@ $.widget( "ui.tabs", { // Remove panels that we created that are missing their tab this.element.find(".ui-tabs-panel:data(destroy.tabs)").each( function( index, panel ) { - var anchor = self.anchors.filter( "[href$='#" + panel.id + "']"); + var anchor = self.anchors.filter( "[aria-controls='#" + panel.id + "']"); if ( !anchor.length ) { $( panel ).remove(); } @@ -224,14 +225,17 @@ $.widget( "ui.tabs", { this.panels = $( [] ); this.anchors.each(function( i, a ) { - var href = $( a ).attr( "href" ); + var href = $( a ).attr( "href" ), + hrefBase = href.split( "#" )[ 0 ], + selector, + panel, + baseEl; + // For dynamically created HTML that contains a hash as href IE < 8 expands // such href to the full page url with hash and then misinterprets tab as ajax. // Same consideration applies for an added tab with a fragment identifier // since a[href=#fragment-identifier] does unexpectedly not match. // Thus normalize href attribute... - var hrefBase = href.split( "#" )[ 0 ], - baseEl; if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] || ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) { href = a.hash; @@ -240,32 +244,30 @@ $.widget( "ui.tabs", { // inline tab if ( fragmentId.test( href ) ) { - self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) ); + selector = href; + panel = self.element.find( self._sanitizeSelector( selector ) ); // remote tab // prevent loading the page itself if href is just "#" } else if ( href && href !== "#" ) { - // required for restore on destroy - $.data( a, "href.tabs", href ); - - // TODO until #3808 is fixed strip fragment identifier from url - // (IE fails to load from such url) - $.data( a, "load.tabs", href.replace( /#.*$/, "" ) ); - var id = self._tabId( a ); - a.href = "#" + id; - var $panel = self.element.find( "#" + id ); - if ( !$panel.length ) { - $panel = $( self.options.panelTemplate ) + selector = "#" + id; + panel = self.element.find( selector ); + if ( !panel.length ) { + panel = $( self.options.panelTemplate ) .attr( "id", id ) .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) + .data( "destroy.tabs", true ) .insertAfter( self.panels[ i - 1 ] || self.list ); - $panel.data( "destroy.tabs", true ); } - self.panels = self.panels.add( $panel ); // invalid tab href } else { self.options.disabled.push( i ); } + + if ( panel.length) { + self.panels = self.panels.add( panel ); + } + $( a ).attr( "aria-controls", selector ); }); }, @@ -348,9 +350,9 @@ $.widget( "ui.tabs", { var self = this, o = this.options, el = event.currentTarget, - $li = $(el).closest( "li" ), + $li = $( el ).closest( "li" ), $hide = self.panels.filter( ":not(.ui-tabs-hide)" ), - $show = self.element.find( self._sanitizeSelector( el.hash ) ); + $show = self.element.find( self._sanitizeSelector( $( el ).attr( "aria-controls" ) ) ); // tab is already selected, but not collapsible if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible ) || @@ -455,10 +457,6 @@ $.widget( "ui.tabs", { this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); this.anchors.each(function() { - var href = $.data( this, "href.tabs" ); - if ( href ) { - this.href = href; - } var $this = $( this ).unbind( ".tabs" ); $.each( [ "href", "load" ], function( i, prefix ) { $this.removeData( prefix + ".tabs" ); @@ -558,8 +556,11 @@ $.widget( "ui.tabs", { var self = this, o = this.options, a = this.anchors.eq( index )[ 0 ], - url = $.data( a, "load.tabs" ), - eventData = self._ui( self.anchors[ index ], self.panels[ index ] ); + panel = self.element.find( self._sanitizeSelector( $( a ).attr( "aria-controls" ) ) ), + // TODO until #3808 is fixed strip fragment identifier from url + // (IE fails to load from such url) + url = $( a ).attr( "href" ).replace( /#.*$/, "" ), + eventData = self._ui( a, panel[ 0 ] ); if ( this.xhr ) { this.xhr.abort(); @@ -585,7 +586,7 @@ $.widget( "ui.tabs", { this.xhr .success( function( response ) { - self.element.find( self._sanitizeSelector( a.hash ) ).html( response ); + panel.html( response ); }) .complete( function( jqXHR, status ) { if ( status === "abort" ) { @@ -609,13 +610,9 @@ $.widget( "ui.tabs", { // last, so that load event is fired before show... self.element.dequeue( "tabs" ); - return this; - }, - - url: function( index, url ) { - this.anchors.eq( index ).data( "load.tabs", url ); return this; } + }); $.extend( $.ui.tabs, { @@ -861,6 +858,22 @@ if ( $.uiBackCompat !== false ) { return this.anchors.length; }; }( jQuery, jQuery.ui.tabs.prototype ) ); + + // url method + (function( $, prototype ) { + prototype.url = function( index, url ) { + this.anchors.eq( index ).attr( "href", url ); + }; + }( jQuery, jQuery.ui.tabs.prototype ) ); + + // _tabId method + (function( $, prototype ) { + var _tabId = prototype._tabId; + prototype._tabId = function( a ) { + return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) || + _tabId.apply( this, arguments ); + }; + }( jQuery, jQuery.ui.tabs.prototype ) ); } })( jQuery ); -- 2.39.5