1
+ ( function ( $ ) {
2
+
3
+ // Make jQuery's :contains case insensitive (like HTML5 datalist)
4
+ // Changed the name to prevent overriding original functionality
5
+ $ . expr [ ':' ] . RD_contains = function ( a , i , m ) {
6
+ return $ ( a ) . text ( ) . toUpperCase ( ) . indexOf ( m [ 3 ] . toUpperCase ( ) ) >= 0 ;
7
+ } ;
8
+
9
+ $ . fn . relevantDropdown = function ( options ) {
10
+
11
+ options = $ . extend ( {
12
+ fadeOutSpeed : 'normal' , // speed to fade out the dataList Popup
13
+ change : null
14
+ } , options ) ;
15
+
16
+ return this . each ( function ( ) {
17
+
18
+ var $input = $ ( this ) ,
19
+ list_id = $input . attr ( 'list' ) ,
20
+ $datalist = $ ( "#" + list_id ) ,
21
+ datalistItems = $datalist . find ( "option" ) ,
22
+
23
+ searchPosition ,
24
+ scrollValue = 0 ;
25
+
26
+ // Makes sure the browser doesn't already support the list attribute
27
+ // todo: I couldn't figure out how to make an opposite `if statement` without Safari acting up
28
+ if ( ! Modernizr . input . list || ( parseInt ( $ . browser . version ) > 400 ) ) {
29
+
30
+ // Insert home for new fake datalist
31
+ $ ( "<ul />" , {
32
+ "class" : "datalist" ,
33
+ "id" : list_id
34
+ } ) . appendTo ( "body" ) ;
35
+
36
+ // Remove old datalist
37
+ $datalist . remove ( ) ;
38
+
39
+ // Update pointer
40
+ var $datalist = $ ( "#" + list_id ) ;
41
+
42
+ // Fill new fake datalist
43
+ datalistItems . each ( function ( ) {
44
+ $ ( "<li />" , {
45
+ "text" : $ ( this ) . val ( )
46
+ } ) . appendTo ( $datalist ) ;
47
+ } ) ;
48
+
49
+ // Update pointer
50
+ datalistItems = $datalist . find ( "li" ) ;
51
+
52
+ // Typey type type
53
+ $input
54
+ . on ( "focus" , function ( ) {
55
+ // Reset scroll
56
+ $datalist . scrollTop ( 0 ) ;
57
+ scrollValue = 0 ;
58
+ } )
59
+ . on ( "blur" , function ( ) {
60
+
61
+ // If this fires immediately, it prevents click-to-select from working
62
+ setTimeout ( function ( ) {
63
+ $datalist . fadeOut ( options . fadeOutSpeed ) ;
64
+ datalistItems . removeClass ( "active" ) ;
65
+ } , 500 ) ;
66
+ } )
67
+ . on ( "keyup focus" , function ( e ) {
68
+ searchPosition = $input . position ( ) ;
69
+
70
+ // Build datalist
71
+ $datalist
72
+ . show ( )
73
+ . css ( {
74
+ top : searchPosition . top + $ ( this ) . outerHeight ( ) ,
75
+ left : searchPosition . left ,
76
+ width : $input . outerWidth ( )
77
+ } ) ;
78
+
79
+ datalistItems . hide ( ) ;
80
+ $datalist . find ( "li:RD_contains('" + $input . val ( ) + "')" ) . show ( ) ;
81
+ } ) ;
82
+
83
+ // Don't want to use :hover in CSS so doing this instead
84
+ // really helps with arrow key navigation
85
+ datalistItems
86
+ . on ( "mouseenter" , function ( ) {
87
+ $ ( this ) . addClass ( "active" ) . siblings ( ) . removeClass ( "active" ) ;
88
+ } )
89
+ . on ( "mouseleave" , function ( ) {
90
+ $ ( this ) . removeClass ( "active" ) ;
91
+ } ) ;
92
+
93
+ // Window resize
94
+ $ ( window ) . resize ( function ( ) {
95
+ searchPosition = $input . position ( ) ;
96
+ $datalist
97
+ . css ( {
98
+ top : searchPosition . top + $ ( this ) . outerHeight ( ) ,
99
+ left : searchPosition . left ,
100
+ width : $input . outerWidth ( )
101
+ } ) ;
102
+ } ) ;
103
+
104
+ // Watch arrow keys for up and down
105
+ $input . on ( "keydown" , function ( e ) {
106
+
107
+ var active = $ ( "li.active" ) ,
108
+ datalistHeight = $datalist . outerHeight ( ) ,
109
+ datalistItemsHeight = datalistItems . outerHeight ( ) ;
110
+
111
+ // up arrow
112
+ if ( e . keyCode == 38 ) {
113
+ if ( active . length ) {
114
+ prevAll = active . prevAll ( "li:visible" ) ;
115
+ if ( prevAll . length > 0 ) {
116
+ active . removeClass ( "active" ) ;
117
+ prevAll . eq ( 0 ) . addClass ( "active" ) ;
118
+ }
119
+
120
+ if ( prevAll . length && prevAll . position ( ) . top < 0 && scrollValue > 0 ) {
121
+ $datalist . scrollTop ( scrollValue -= datalistItemsHeight ) ;
122
+ }
123
+ }
124
+ }
125
+
126
+ // down arrow
127
+ if ( e . keyCode == 40 ) {
128
+ if ( active . length ) {
129
+ var nextAll = active . nextAll ( "li:visible" ) ;
130
+ if ( nextAll . length > 0 ) {
131
+ active . removeClass ( "active" ) ;
132
+ nextAll . eq ( 0 ) . addClass ( "active" ) ;
133
+ }
134
+
135
+ if ( nextAll . length && ( nextAll . position ( ) . top + datalistItemsHeight ) >= datalistHeight ) {
136
+ $datalist . stop ( ) . animate ( {
137
+ scrollTop : scrollValue += datalistItemsHeight
138
+ } , 200 ) ;
139
+ }
140
+ } else {
141
+ datalistItems . removeClass ( "active" ) ;
142
+ $datalist . find ( "li:visible:first" ) . addClass ( "active" ) ;
143
+ }
144
+ }
145
+
146
+ // return or tab key
147
+ if ( e . keyCode == 13 || e . keyCode == 9 ) {
148
+ if ( active . length ) {
149
+ $input . val ( active . text ( ) ) ;
150
+ item_selected ( active . text ( ) ) ;
151
+ }
152
+ $datalist . fadeOut ( options . fadeOutSpeed ) ;
153
+ datalistItems . removeClass ( "active" ) ;
154
+ }
155
+
156
+ // keys
157
+ if ( e . keyCode != 13 && e . keyCode != 38 && e . keyCode != 40 ) {
158
+ // Reset active class
159
+ datalistItems . removeClass ( "active" ) ;
160
+ $datalist . find ( "li:visible:first" ) . addClass ( "active" ) ;
161
+
162
+ // Reset scroll
163
+ $datalist . scrollTop ( 0 ) ;
164
+ scrollValue = 0 ;
165
+ }
166
+
167
+ } ) ;
168
+
169
+ // When choosing from dropdown
170
+ datalistItems . on ( "click" , function ( ) {
171
+ var active = $ ( "li.active" ) ;
172
+ if ( active . length ) {
173
+ $input . val ( $ ( this ) . text ( ) ) ;
174
+ }
175
+ $datalist . fadeOut ( options . fadeOutSpeed ) ;
176
+ datalistItems . removeClass ( "active" ) ;
177
+ item_selected ( $ ( this ) . text ( ) ) ;
178
+ } ) ;
179
+
180
+ function item_selected ( new_text ) {
181
+ if ( typeof options . change === 'function' )
182
+ options . change . call ( this , new_text ) ;
183
+ }
184
+
185
+ } // end if
186
+ } ) ;
187
+ } ;
188
+ } ) ( jQuery ) ;
0 commit comments