13
13
* jquery.ui.position.js
14
14
*/
15
15
( function ( $ ) {
16
-
16
+
17
17
var idIncrement = 0 ;
18
18
19
19
$ . widget ( "ui.popup" , {
@@ -27,34 +27,34 @@ $.widget( "ui.popup", {
27
27
if ( ! this . options . trigger ) {
28
28
this . options . trigger = this . element . prev ( ) ;
29
29
}
30
-
30
+
31
31
if ( ! this . element . attr ( "id" ) ) {
32
32
this . element . attr ( "id" , "ui-popup-" + idIncrement ++ ) ;
33
33
this . generatedId = true ;
34
34
}
35
-
35
+
36
36
if ( ! this . element . attr ( "role" ) ) {
37
37
// TODO alternatives to tooltip are dialog and menu, all three aren't generic popups
38
38
this . element . attr ( "role" , "dialog" ) ;
39
39
this . generatedRole = true ;
40
40
}
41
-
41
+
42
42
this . options . trigger
43
43
. attr ( "aria-haspopup" , true )
44
44
. attr ( "aria-owns" , this . element . attr ( "id" ) ) ;
45
-
45
+
46
46
this . element
47
- . addClass ( "ui-popup" )
47
+ . addClass ( "ui-popup" )
48
48
this . close ( ) ;
49
49
50
50
this . _bind ( this . options . trigger , {
51
51
keydown : function ( event ) {
52
- // prevent space-to-open to scroll the page, only hapens for anchor ui.button
53
- if ( this . options . trigger . is ( "a:ui-button" ) && event . keyCode == $ . ui . keyCode . SPACE ) {
54
- event . preventDefault ( )
52
+ // prevent space-to-open to scroll the page, only happens for anchor ui.button
53
+ if ( this . options . trigger . is ( "a:ui-button" ) && event . keyCode == $ . ui . keyCode . SPACE ) {
54
+ event . preventDefault ( ) ;
55
55
}
56
56
// TODO handle SPACE to open popup? only when not handled by ui.button
57
- if ( event . keyCode == $ . ui . keyCode . SPACE && this . options . trigger . is ( "a:not(:ui-button)" ) ) {
57
+ if ( event . keyCode == $ . ui . keyCode . SPACE && this . options . trigger . is ( "a:not(:ui-button)" ) ) {
58
58
this . options . trigger . trigger ( "click" , event ) ;
59
59
}
60
60
// translate keydown to click
@@ -78,8 +78,29 @@ $.widget( "ui.popup", {
78
78
} , 1 ) ;
79
79
}
80
80
} ) ;
81
-
82
- this . _bind ( this . element , {
81
+
82
+ if ( ! this . element . is ( ":ui-menu" ) ) {
83
+ //default use case, wrap tab order in popup
84
+ this . element . bind ( "keydown.ui-popup" , function ( event ) {
85
+ if ( event . keyCode !== $ . ui . keyCode . TAB ) {
86
+ return ;
87
+ }
88
+
89
+ var tabbables = $ ( ":tabbable" , this ) ,
90
+ first = tabbables . first ( ) ,
91
+ last = tabbables . last ( ) ;
92
+
93
+ if ( event . target === last [ 0 ] && ! event . shiftKey ) {
94
+ first . focus ( 1 ) ;
95
+ return false ;
96
+ } else if ( event . target === first [ 0 ] && event . shiftKey ) {
97
+ last . focus ( 1 ) ;
98
+ return false ;
99
+ }
100
+ } ) ;
101
+ }
102
+
103
+ this . _bind ( {
83
104
focusout : function ( event ) {
84
105
var that = this ;
85
106
// use a timer to allow click to clear it and letting that
@@ -89,52 +110,52 @@ $.widget( "ui.popup", {
89
110
} , 100 ) ;
90
111
} ,
91
112
focusin : function ( event ) {
92
- var that = this ;
93
- clearTimeout ( that . closeTimer ) ;
94
- }
113
+ clearTimeout ( this . closeTimer ) ;
114
+ }
95
115
} ) ;
96
116
97
117
this . _bind ( {
98
118
// TODO only triggered on element if it can receive focus
99
119
// bind to document instead?
100
120
// either element itself or a child should be focusable
101
121
keyup : function ( event ) {
102
- if ( event . keyCode == $ . ui . keyCode . ESCAPE && this . element . is ( ":visible" ) ) {
122
+ if ( event . keyCode == $ . ui . keyCode . ESCAPE && this . element . is ( ":visible" ) ) {
103
123
this . close ( event ) ;
104
124
// TODO move this to close()? would allow menu.select to call popup.close, and get focus back to trigger
105
125
this . options . trigger . focus ( ) ;
106
126
}
107
127
}
108
128
} ) ;
109
-
129
+
110
130
this . _bind ( document , {
111
131
click : function ( event ) {
112
- if ( this . isOpen && ! $ ( event . target ) . closest ( ".ui-popup" ) . length ) {
132
+ if ( this . isOpen && ! $ ( event . target ) . closest ( ".ui-popup" ) . length ) {
113
133
this . close ( event ) ;
114
134
}
115
135
}
116
136
} )
117
137
} ,
118
-
138
+
119
139
_destroy : function ( ) {
120
140
this . element
121
141
. show ( )
122
142
. removeClass ( "ui-popup" )
123
143
. removeAttr ( "aria-hidden" )
124
- . removeAttr ( "aria-expanded" ) ;
144
+ . removeAttr ( "aria-expanded" )
145
+ . unbind ( "keypress.ui-popup" ) ;
125
146
126
147
this . options . trigger
127
148
. removeAttr ( "aria-haspopup" )
128
149
. removeAttr ( "aria-owns" ) ;
129
-
150
+
130
151
if ( this . generatedId ) {
131
152
this . element . removeAttr ( "id" ) ;
132
153
}
133
154
if ( this . generatedRole ) {
134
155
this . element . removeAttr ( "role" ) ;
135
156
}
136
157
} ,
137
-
158
+
138
159
open : function ( event ) {
139
160
var position = $ . extend ( { } , {
140
161
of : this . options . trigger
@@ -144,46 +165,26 @@ $.widget( "ui.popup", {
144
165
. show ( )
145
166
. attr ( "aria-hidden" , false )
146
167
. attr ( "aria-expanded" , true )
147
- . position ( position ) ;
148
-
149
- if ( this . element . is ( ":ui-menu" ) ) { //popup is a menu
150
- this . element . focus ( ) ;
151
- this . element . menu ( "focus" , event , this . element . children ( "li" ) . first ( ) ) ;
168
+ . position ( position ) ;
169
+
170
+ if ( this . element . is ( ":ui-menu" ) ) { //popup is a menu
171
+ this . element . menu ( "focus" , event , this . element . children ( "li" ) . first ( ) ) ;
152
172
this . element . focus ( ) ;
153
- }
154
- // TODO add other special use cases that differ from the default dialog style keyboard mechanism
155
- else {
156
- //default use case, popup could be anything (e.g. a form)
157
- this . element
158
- . bind ( "keydown.ui-popup" , function ( event ) {
159
- if ( event . keyCode !== $ . ui . keyCode . TAB ) {
160
- return ;
161
- }
162
- var tabbables = $ ( ":tabbable" , this ) ,
163
- first = tabbables . filter ( ":first" ) ,
164
- last = tabbables . filter ( ":last" ) ;
165
- if ( event . target === last [ 0 ] && ! event . shiftKey ) {
166
- first . focus ( 1 ) ;
167
- return false ;
168
- } else if ( event . target === first [ 0 ] && event . shiftKey ) {
169
- last . focus ( 1 ) ;
170
- return false ;
171
- }
172
- } ) ;
173
-
173
+ } else {
174
174
// set focus to the first tabbable element in the popup container
175
- // if there are no tabbable elements, set focus on the popup itself
176
- var tabbables = this . element . find ( ":tabbable" ) ;
177
- if ( ! tabbables . length ) {
178
- this . element . attr ( "tabindex" , "0" ) ;
179
- tabbables . add ( this . element ) ;
180
- }
181
- tabbables . eq ( 0 ) . focus ( 1 ) ;
175
+ // if there are no tabbable elements, set focus on the popup itself
176
+ var tabbables = this . element . find ( ":tabbable" ) ;
177
+ if ( ! tabbables . length ) {
178
+ if ( ! this . element . is ( ":tabbable" ) ) {
179
+ this . element . attr ( "tabindex" , "0" ) ;
180
+ }
181
+ tabbables . add ( this . element ) ;
182
+ }
183
+ tabbables . first ( ) . focus ( 1 ) ;
182
184
}
183
-
184
- // take trigger out of tab order to allow shift-tab to skip trigger
185
- this . options . trigger . attr ( "tabindex" , - 1 ) ;
186
185
186
+ // take trigger out of tab order to allow shift-tab to skip trigger
187
+ this . options . trigger . attr ( "tabindex" , - 1 ) ;
187
188
this . isOpen = true ;
188
189
this . _trigger ( "open" , event ) ;
189
190
} ,
@@ -192,16 +193,13 @@ $.widget( "ui.popup", {
192
193
this . element
193
194
. hide ( )
194
195
. attr ( "aria-hidden" , true )
195
- . attr ( "aria-expanded" , false )
196
- . unbind ( "keypress.ui-popup" ) ;
196
+ . attr ( "aria-expanded" , false ) ;
197
197
198
198
this . options . trigger . attr ( "tabindex" , 0 ) ;
199
199
200
200
this . isOpen = false ;
201
201
this . _trigger ( "close" , event ) ;
202
202
}
203
-
204
-
205
203
} ) ;
206
204
207
205
} ( jQuery ) ) ;
0 commit comments