Skip to content

Commit bd7a6f1

Browse files
committed
Calendar: Use classes option for non performance critical rendering
1 parent e02ef66 commit bd7a6f1

File tree

2 files changed

+133
-107
lines changed

2 files changed

+133
-107
lines changed

tests/unit/calendar/common.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ define( [
77
common.testWidget( "calendar", {
88
defaults: {
99
buttons: [],
10-
classes: {},
10+
classes: {
11+
"ui-calendar": "ui-corner-all",
12+
"ui-calendar-header-first": "ui-corner-left",
13+
"ui-calendar-header-last": "ui-corner-right",
14+
"ui-calendar-prev": "ui-corner-all",
15+
"ui-calendar-next": "ui-corner-all"
16+
},
1117
disabled: false,
1218
dateFormat: { date: "short" },
1319
eachDay: $.noop,

ui/widgets/calendar.js

Lines changed: 126 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ return $.widget( "ui.calendar", {
4545
version: "@VERSION",
4646
options: {
4747
buttons: [],
48+
classes: {
49+
"ui-calendar": "ui-corner-all",
50+
"ui-calendar-header-first": "ui-corner-left",
51+
"ui-calendar-header-last": "ui-corner-right",
52+
"ui-calendar-prev": "ui-corner-all",
53+
"ui-calendar-next": "ui-corner-all"
54+
},
4855
dateFormat: { date: "short" },
4956
eachDay: $.noop,
5057
labels: {
@@ -119,7 +126,7 @@ return $.widget( "ui.calendar", {
119126
},
120127

121128
_hover: function( event ) {
122-
$( event.currentTarget ).toggleClass( "ui-state-hover" );
129+
this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
123130
},
124131

125132
_handleKeydown: function( event ) {
@@ -193,14 +200,15 @@ return $.widget( "ui.calendar", {
193200
},
194201

195202
_updateDayElement: function( state ) {
196-
var id = this._getDayId( this.date );
203+
var id = this._getDayId( this.date ),
204+
button = this._getDateElement( id ).children( "button" );
205+
206+
this.grid.attr( "aria-activedescendant", id );
197207

198-
this.grid
199-
.attr( "aria-activedescendant", id )
200-
.find( "button." + state )
201-
.removeClass( state );
208+
this._removeClass( this.grid.find( "button." + state ), null, state );
209+
this._addClass( button, null, state );
202210

203-
return this._getDateElement( id ).children( "button" ).addClass( state );
211+
return button;
204212
},
205213

206214
_getDateElement: function( id ) {
@@ -232,146 +240,161 @@ return $.widget( "ui.calendar", {
232240
},
233241

234242
_createCalendar: function() {
235-
var classes = "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all",
236-
pickerHtml = this._buildHeaderButtons();
243+
this.element
244+
.attr( "role", "region" )
245+
.append( this._buildHeaderButtons() );
237246

238247
if ( this.options.numberOfMonths === 1 ) {
239-
pickerHtml += this._buildHeader() + this._buildGrid();
240-
this.element.attr( "aria-labelledby", this.gridId + "-title" );
248+
this._buildSinglePicker();
241249
} else {
242-
pickerHtml += this._buildMultiplePicker();
243-
classes += " ui-calendar-multi";
250+
this._buildMultiplePicker();
244251
}
245252

246-
this.element
247-
.addClass( classes )
248-
.attr( "role", "region" )
249-
.html( pickerHtml );
253+
this._addClass( this.element, "ui-calendar", "ui-widget ui-widget-content ui-helper-clearfix" );
250254

251-
this.prevButton = this.element.find( ".ui-calendar-prev" );
252-
this.nextButton = this.element.find( ".ui-calendar-next" );
253255
this._refreshHeaderButtons();
254-
255256
this._createButtonPane();
256257

257258
this.grid = this.element.find( ".ui-calendar-calendar" );
258259
},
259260

261+
_buildSinglePicker: function() {
262+
var header = this._buildHeader();
263+
264+
this._addClass( header, "ui-calendar-header-first ui-calendar-header-last" );
265+
this.element
266+
.attr( "aria-labelledby", this.gridId + "-title" )
267+
.append( header )
268+
.append( this._buildGrid() );
269+
},
270+
260271
_buildMultiplePicker: function() {
261-
var headerClass,
262-
html = "",
272+
var element, header,
273+
rowBreak = $( "<div>" ),
263274
currentDate = this.viewDate,
264275
months = this.viewDate.months( this.options.numberOfMonths - 1 ),
265-
labelledby = [],
276+
labelledBy = [],
266277
i = 0;
267278

268279
for ( ; i < months.length; i++ ) {
269280

270281
// TODO: Shouldn't we pass date as a parameter to build* fns instead of setting this.date?
271282
this.viewDate = months[ i ];
272283
this.gridId = this.id + "-" + i;
284+
labelledBy.push( this.gridId + "-title" );
273285

274-
labelledby.push( this.gridId + "-title" );
275-
headerClass = "ui-calendar-header ui-widget-header ui-helper-clearfix";
276-
if ( months[ i ].first ) {
277-
headerClass += " ui-corner-left";
278-
} else if ( months[ i ].last ) {
279-
headerClass += " ui-corner-right";
280-
}
286+
element = $( "<div>" );
287+
this._addClass( element, "ui-calendar-group" );
281288

282-
html += "<div class='ui-calendar-group'>";
283-
html += "<div class='" + headerClass + "'>" +
284-
this._buildTitlebar() + "</div>";
285-
html += this._buildGrid() + "</div>";
289+
header = this._buildHeader();
290+
this._addClass( header, "ui-calendar-header-" +
291+
( ( months[ i ].first ) ? "first" : ( months[ i ].last ) ? "last" : "middle" )
292+
);
293+
294+
element.appendTo( this.element )
295+
.append( header )
296+
.append( this._buildGrid() );
286297
}
287298

288-
html += "<div class='ui-calendar-row-break'></div>";
299+
this._addClass( this.element, "ui-calendar-multi" )
300+
._addClass( rowBreak, "ui-calendar-row-break" );
289301

290-
this.element.attr( "aria-labelledby", labelledby.join( " " ) );
302+
this.element
303+
.attr( "aria-labelledby", labelledBy.join( " " ) )
304+
.append( rowBreak );
291305

292306
this.viewDate = currentDate;
293-
294-
return html;
295-
},
296-
297-
_buildHeader: function() {
298-
return "<div class='ui-calendar-header ui-widget-header ui-helper-clearfix ui-corner-all'>" +
299-
this._buildTitlebar() +
300-
"</div>";
301307
},
302308

303309
_buildHeaderButtons: function() {
304-
return "<div class='ui-calendar-header-buttons'>" +
305-
this._buildPreviousLink() +
306-
this._buildNextLink() +
307-
"</div>";
308-
},
310+
var buttons = $( "<div>" );
309311

310-
_buildPreviousLink: function() {
311-
return "<button class='ui-calendar-prev ui-corner-all'>" +
312-
"<span class='ui-icon ui-icon-circle-triangle-w'></span>" +
313-
"</button>";
314-
},
312+
this.prevButton = $( "<button>", {
313+
html: "<span class='ui-icon ui-icon-circle-triangle-w'></span>"
314+
} );
315+
this.nextButton = $( "<button>", {
316+
html: "<span class='ui-icon ui-icon-circle-triangle-e'></span>"
317+
} );
318+
319+
this._addClass( buttons, "ui-calendar-header-buttons" )
320+
._addClass( this.prevButton, "ui-calendar-prev" )
321+
._addClass( this.nextButton, "ui-calendar-next" );
315322

316-
_buildNextLink: function() {
317-
return "<button class='ui-calendar-next ui-corner-all'>" +
318-
"<span class='ui-icon ui-icon-circle-triangle-e'></span>" +
319-
"</button>";
323+
return buttons
324+
.append( this.prevButton )
325+
.append( this.nextButton );
320326
},
321327

322-
_buildTitlebar: function() {
323-
return "<div role='header' id='" + this.gridId + "-title'>" +
324-
"<div id='" + this.gridId + "-month-label' role='alert' class='ui-calendar-title'>" +
325-
this._buildTitle() +
326-
"</div>" +
327-
"<span class='ui-helper-hidden-accessible'>, " +
328-
this._getTranslation( "datePickerRole" ) +
329-
"</span>" +
330-
"</div>";
328+
_buildHeader: function() {
329+
var header = $( "<div>" ),
330+
title = $( "<div>", { role: "header", id: this.gridId + "-title" } ),
331+
notice = $( "<span>" ).text( ", " + this._getTranslation( "datePickerRole" ) );
332+
333+
this._addClass( header, "ui-calendar-header", "ui-widget-header ui-helper-clearfix" )
334+
._addClass( notice, null, "ui-helper-hidden-accessible" );
335+
336+
return header.append(
337+
title
338+
.append( this._buildTitle() )
339+
.append( notice )
340+
);
331341
},
332342

333343
_buildTitle: function() {
334-
return "<span class='ui-calendar-month'>" +
335-
this.viewDate.monthName() +
336-
"</span> " +
337-
"<span class='ui-calendar-year'>" +
338-
this.viewDate.year() +
339-
"</span>";
344+
var title = $( "<div>", { role: "alert", id: this.gridId + "-month-label" } ),
345+
month = $( "<span>" ).text( this.viewDate.monthName() ),
346+
year = $( "<span>" ).text( this.viewDate.year() );
347+
348+
this._addClass( title, "ui-calendar-title" )
349+
._addClass( month, "ui-calendar-month" )
350+
._addClass( year, "ui-calendar-year" );
351+
352+
return title
353+
.append( month )
354+
.append( " " )
355+
.append( year );
340356
},
341357

342358
_buildGrid: function() {
343-
return "<table class='ui-calendar-calendar' role='grid' aria-readonly='true' " +
344-
"aria-labelledby='" + this.gridId + "-month-label' tabindex='0' " +
345-
"aria-activedescendant='" + this._getDayId( this.date ) + "'>" +
346-
this._buildGridHeading() +
347-
this._buildGridBody() +
348-
"</table>";
359+
var table = $( "<table>", {
360+
role: "grid",
361+
tabindex: 0,
362+
"aria-readonly": true,
363+
"aria-labelledby": this.gridId + "-month-label",
364+
"aria-activedescendant": this._getDayId( this.date )
365+
} );
366+
367+
this._addClass( table, "ui-calendar-calendar" );
368+
369+
return table
370+
.append( this._buildGridHeading() )
371+
.append( this._buildGridBody() );
349372
},
350373

351374
_buildGridHeading: function() {
352-
var cells = "",
375+
var head = $( "<thead role='presentation'>" ),
376+
week = $( "<th>" ),
377+
row = $( "<tr role='row'>" ),
353378
i = 0,
354379
weekDayLength = this.viewDate.weekdays().length,
355380
weekdays = this.viewDate.weekdays();
356381

357382
if ( this.options.showWeek ) {
358-
cells += "<th class='ui-calendar-week-col'>" + this._getTranslation( "weekHeader" ) + "</th>";
383+
this._addClass( week, "ui-calendar-week-col" );
384+
row.append( week.text( this._getTranslation( "weekHeader" ) ) );
359385
}
386+
360387
for ( ; i < weekDayLength; i++ ) {
361-
cells += this._buildGridHeaderCell( weekdays[ i ] );
388+
row.append( this._buildGridHeaderCell( weekdays[ i ] ) );
362389
}
363390

364-
return "<thead role='presentation'>" +
365-
"<tr role='row'>" + cells + "</tr>" +
366-
"</thead>";
391+
return head.append( row );
367392
},
368393

369394
_buildGridHeaderCell: function( day ) {
370-
return "<th role='columnheader' abbr='" + day.fullname + "' aria-label='" + day.fullname + "'>" +
371-
"<span title='" + day.fullname + "'>" +
372-
day.shortname +
373-
"</span>" +
374-
"</th>";
395+
return $( "<th role='columnheader' abbr='" + day.fullname + "' aria-label='" + day.fullname + "'>" +
396+
"<span title='" + day.fullname + "'>" + day.shortname + "</span>" +
397+
"</th>" );
375398
},
376399

377400
_buildGridBody: function() {
@@ -468,12 +491,11 @@ return $.widget( "ui.calendar", {
468491
},
469492

470493
_createButtonPane: function() {
471-
this.buttonPane = $( "<div>" )
472-
.addClass( "ui-calendar-buttonpane ui-widget-content ui-helper-clearfix" );
494+
this.buttonPane = $( "<div>" );
495+
this.buttonSet = $( "<div>" ).appendTo( this.buttonPane );
473496

474-
this.buttonSet = $( "<div>" )
475-
.addClass( "ui-calendar-buttonset" )
476-
.appendTo( this.buttonPane );
497+
this._addClass( this.buttonPane, "ui-calendar-buttonpane", "ui-widget-content ui-helper-clearfix" )
498+
._addClass( this.buttonSet, "ui-calendar-buttonset" );
477499

478500
this._createButtons();
479501
},
@@ -486,7 +508,7 @@ return $.widget( "ui.calendar", {
486508
this.buttonSet.empty();
487509

488510
if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) {
489-
this.element.removeClass( "ui-calendar-buttons" );
511+
this._removeClass( this.element, "ui-calendar-buttons" );
490512
return;
491513
}
492514

@@ -519,7 +541,8 @@ return $.widget( "ui.calendar", {
519541
click.apply( that.buttonClickContext, arguments );
520542
} );
521543
} );
522-
this.element.addClass( "ui-calendar-buttons" );
544+
545+
this._addClass( this.element, "ui-calendar-buttons" );
523546
this.buttonPane.appendTo( this.element );
524547
},
525548

@@ -532,15 +555,15 @@ return $.widget( "ui.calendar", {
532555
// Determine which day grid cell to focus after refresh
533556
// TODO: Prevent disabled cells from being focused
534557
if ( this.options.numberOfMonths === 1 ) {
535-
this.element.find( ".ui-calendar-title" ).html( this._buildTitle() );
558+
this.element.find( ".ui-calendar-title" ).replaceWith( this._buildTitle() );
536559
this.element.find( ".ui-calendar-calendar" ).replaceWith( this._buildGrid() );
537560
} else {
538561
this._refreshMultiplePicker();
539562
}
540563

541564
this.grid = this.element.find( ".ui-calendar-calendar" );
542-
this._setActiveDescendant();
543565

566+
this._setActiveDescendant();
544567
this._refreshHeaderButtons();
545568
this._createButtons();
546569
},
@@ -577,17 +600,16 @@ return $.widget( "ui.calendar", {
577600
},
578601

579602
_disableElement: function( element, state ) {
580-
element
581-
.attr( "aria-disabled", state )
582-
.toggleClass( "ui-state-disabled", state );
603+
element.attr( "aria-disabled", state );
604+
this._toggleClass( element, null, "ui-state-disabled", state );
583605
},
584606

585607
_refreshMultiplePicker: function() {
586608
var i = 0;
587609

588610
for ( ; i < this.options.numberOfMonths; i++ ) {
589-
this.element.find( ".ui-calendar-title" ).eq( i ).html( this._buildTitle() );
590-
this.element.find( ".ui-calendar-calendar" ).eq( i ).html( this._buildGrid() );
611+
this.element.find( ".ui-calendar-title" ).eq( i ).replaceWith( this._buildTitle() );
612+
this.element.find( ".ui-calendar-calendar" ).eq( i ).replaceWith( this._buildGrid() );
591613
this.viewDate.adjust( "M", 1 );
592614
}
593615
this.viewDate.adjust( "M", -this.options.numberOfMonths );
@@ -642,8 +664,6 @@ return $.widget( "ui.calendar", {
642664

643665
_destroy: function() {
644666
this.element
645-
.off( ".calendar" )
646-
.removeClass( "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-calendar-multi" )
647667
.removeAttr( "role aria-labelledby" )
648668
.removeUniqueId()
649669
.empty();

0 commit comments

Comments
 (0)