PageSourceSearch

https://severen.ru/assets/js/selectFx.js

js severen.ru collected 2026-10-05 04:47:47 UTC 9,076 bytes, 331 lines download raw bytes

1/**
2 * selectFx.js v1.0.0
3 * http://www.codrops.com
4 *
5 * Licensed under the MIT license.
6 * http://www.opensource.org/licenses/mit-license.php
7 * 
8 * Copyright 2014, Codrops
9 * http://www.codrops.com
10 */
11;( function( window ) {
12	
13	'use strict';
14
15	/**
16	 * based on from https://github.com/inuyaksa/jquery.nicescroll/blob/master/jquery.nicescroll.js
17	 */
18	function hasParent( e, p ) {
19		if (!e) return false;
20		var el = e.target||e.srcElement||e||false;
21		while (el && el != p) {
22			el = el.parentNode||false;
23		}
24		return (el!==false);
25	};
26	
27	/**
28	 * extend obj function
29	 */
30	function extend( a, b ) {
31		for( var key in b ) { 
32			if( b.hasOwnProperty( key ) ) {
33				a[key] = b[key];
34			}
35		}
36		return a;
37	}
38
39	/**
40	 * SelectFx function
41	 */
42	function SelectFx( el, options ) {	
43		this.el = el;
44		this.options = extend( {}, this.options );
45		extend( this.options, options );
46		this._init();
47	}
48
49	/**
50	 * SelectFx options
51	 */
52	SelectFx.prototype.options = {
53		// if true all the links will open in a new tab.
54		// if we want to be redirected when we click an option, we need to define a data-link attr on the option of the native select element
55		newTab : true,
56		// when opening the select element, the default placeholder (if any) is shown
57		stickyPlaceholder : true,
58		// callback when changing the value
59		onChange : function( val ) { return false; }
60	}
61
62	/**
63	 * init function
64	 * initialize and cache some vars
65	 */
66	SelectFx.prototype._init = function() {
67		// check if we are using a placeholder for the native select box
68		// we assume the placeholder is disabled and selected by default
69		var selectedOpt = this.el.querySelector( 'option[selected]' );
70		this.hasDefaultPlaceholder = selectedOpt && selectedOpt.disabled;
71
72		// get selected option (either the first option with attr selected or just the first option)
73		this.selectedOpt = selectedOpt || this.el.querySelector( 'option' );
74
75		// create structure
76		this._createSelectEl();
77
78		// all options
79		this.selOpts = [].slice.call( this.selEl.querySelectorAll( 'li[data-option]' ) );
80		
81		// total options
82		this.selOptsCount = this.selOpts.length;
83		
84		// current index
85		this.current = this.selOpts.indexOf( this.selEl.querySelector( 'li.cs-selected' ) ) || -1;
86		
87		// placeholder elem
88		this.selPlaceholder = this.selEl.querySelector( 'span.cs-placeholder' );
89
90		// init events
91		this._initEvents();
92	}
93
94	/**
95	 * creates the structure for the select element
96	 */
97	SelectFx.prototype._createSelectEl = function() {
98		var self = this, options = '', createOptionHTML = function(el) {
99			var optclass = '', classes = '', link = '';
100
101			if( el.selectedOpt && !this.foundSelected && !this.hasDefaultPlaceholder ) {
102				classes += 'cs-selected ';
103				this.foundSelected = true;
104			}
105			// extra classes
106			if( el.getAttribute( 'data-class' ) ) {
107				classes += el.getAttribute( 'data-class' );
108			}
109			// link options
110			if( el.getAttribute( 'data-link' ) ) {
111				link = 'data-link=' + el.getAttribute( 'data-link' );
112			}
113
114			if( classes !== '' ) {
115				optclass = 'class="' + classes + '" ';
116			}
117
118			return '<li ' + optclass + link + ' data-option data-value="' + el.value + '"><span>' + el.textContent + '</span></li>';
119		};
120
121		[].slice.call( this.el.children ).forEach( function(el) {
122			if( el.disabled ) { return; }
123
124			var tag = el.tagName.toLowerCase();
125
126			if( tag === 'option' ) {
127				options += createOptionHTML(el);
128			}
129			else if( tag === 'optgroup' ) {
130				options += '<li class="cs-optgroup"><span>' + el.label + '</span><ul>';
131				[].slice.call( el.children ).forEach( function(opt) {
132					options += createOptionHTML(opt);
133				} );
134				options += '</ul></li>';
135			}
136		} );
137
138		var opts_el = '<div class="cs-options"><ul>' + options + '</ul></div>';
139		this.selEl = document.createElement( 'div' );
140		this.selEl.className = this.el.className;
141		this.selEl.tabIndex = this.el.tabIndex;
142		this.selEl.innerHTML = '<span class="cs-placeholder">' + this.selectedOpt.textContent + '</span>' + opts_el;
143		this.el.parentNode.appendChild( this.selEl );
144		this.selEl.appendChild( this.el );
145	}
146
147	/**
148	 * initialize the events
149	 */
150	SelectFx.prototype._initEvents = function() {
151		var self = this;
152
153		// open/close select
154		this.selPlaceholder.addEventListener( 'click', function() {
155			self._toggleSelect();
156		} );
157
158		// clicking the options
159		this.selOpts.forEach( function(opt, idx) {
160			opt.addEventListener( 'click', function() {
161				self.current = idx;
162				self._changeOption();
163				// close select elem
164				self._toggleSelect();
165			} );
166		} );
167
168		// close the select element if the target it´s not the select element or one of its descendants..
169		document.addEventListener( 'click', function(ev) {
170			var target = ev.target;
171			if( self._isOpen() && target !== self.selEl && !hasParent( target, self.selEl ) ) {
172				self._toggleSelect();
173			}
174		} );
175
176		// keyboard navigation events
177		this.selEl.addEventListener( 'keydown', function( ev ) {
178			var keyCode = ev.keyCode || ev.which;
179
180			switch (keyCode) {
181				// up key
182				case 38:
183					ev.preventDefault();
184					self._navigateOpts('prev');
185					break;
186				// down key
187				case 40:
188					ev.preventDefault();
189					self._navigateOpts('next');
190					break;
191				// space key
192				case 32:
193					ev.preventDefault();
194					if( self._isOpen() && typeof self.preSelCurrent != 'undefined' && self.preSelCurrent !== -1 ) {
195						self._changeOption();
196					}
197					self._toggleSelect();
198					break;
199				// enter key
200				case 13:
201					ev.preventDefault();
202					if( self._isOpen() && typeof self.preSelCurrent != 'undefined' && self.preSelCurrent !== -1 ) {
203						self._changeOption();
204						self._toggleSelect();
205					}
206					break;
207				// esc key
208				case 27:
209					ev.preventDefault();
210					if( self._isOpen() ) {
211						self._toggleSelect();
212					}
213					break;
214			}
215		} );
216	}
217
218	/**
219	 * navigate with up/dpwn keys
220	 */
221	SelectFx.prototype._navigateOpts = function(dir) {
222		if( !this._isOpen() ) {
223			this._toggleSelect();
224		}
225
226		var tmpcurrent = typeof this.preSelCurrent != 'undefined' && this.preSelCurrent !== -1 ? this.preSelCurrent : this.current;
227		
228		if( dir === 'prev' && tmpcurrent > 0 || dir === 'next' && tmpcurrent < this.selOptsCount - 1 ) {
229			// save pre selected current - if we click on option, or press enter, or press space this is going to be the index of the current option
230			this.preSelCurrent = dir === 'next' ? tmpcurrent + 1 : tmpcurrent - 1;
231			// remove focus class if any..
232			this._removeFocus();
233			// add class focus - track which option we are navigating
234			classie.add( this.selOpts[this.preSelCurrent], 'cs-focus' );
235		}
236	}
237
238	/**
239	 * open/close select
240	 * when opened show the default placeholder if any
241	 */
242	SelectFx.prototype._toggleSelect = function() {
243		// remove focus class if any..
244		this._removeFocus();
245		
246		if( this._isOpen() ) {
247			if( this.current !== -1 ) {
248				// update placeholder text
249				this.selPlaceholder.textContent = this.selOpts[ this.current ].textContent;
250			}
251			classie.remove( this.selEl, 'cs-active' );
252		}
253		else {
254			if( this.hasDefaultPlaceholder && this.options.stickyPlaceholder ) {
255				// everytime we open we wanna see the default placeholder text
256				this.selPlaceholder.textContent = this.selectedOpt.textContent;
257			}
258			classie.add( this.selEl, 'cs-active' );
259		}
260	}
261
262	/**
263	 * change option - the new value is set
264	 */
265	SelectFx.prototype._changeOption = function() {
266		// if pre selected current (if we navigate with the keyboard)...
267		if( typeof this.preSelCurrent != 'undefined' && this.preSelCurrent !== -1 ) {
268			this.current = this.preSelCurrent;
269			this.preSelCurrent = -1;
270		}
271
272		// current option
273		var opt = this.selOpts[ this.current ];
274
275
276		// update current selected value
277		this.selPlaceholder.textContent = opt.textContent;
278		
279		// change native select element´s value
280		this.el.value = opt.getAttribute( 'data-value' );
281
282		// remove class cs-selected from old selected option and add it to current selected option
283		var oldOpt = this.selEl.querySelector( 'li.cs-selected' );
284		if( oldOpt ) {
285			classie.remove( oldOpt, 'cs-selected' );
286		}
287		classie.add( opt, 'cs-selected' );
288
289		// if there´s a link defined
290		if( opt.getAttribute( 'data-link' ) ) {
291			// open in new tab?
292			if( this.options.newTab ) {
293				window.open( opt.getAttribute( 'data-link' ), '_blank' );
294			}
295			else {
296				window.location = opt.getAttribute( 'data-link' );
297			}
298		}
299
300		this.selectedOpt.removeAttribute('selected');
301		// var test = this.el.querySelector( 'option[value="'+this.el.value+'"]' );
302		// test.setAttribute('aria-invalid', true);
303		// // console.log(test);
304
305		// callback
306		this.options.onChange( this.el.value );
307	}
308
309	/**
310	 * returns true if select element is opened
311	 */
312	SelectFx.prototype._isOpen = function(opt) {
313		return classie.has( this.selEl, 'cs-active' );
314	}
315
316	/**
317	 * removes the focus class from the option
318	 */
319	SelectFx.prototype._removeFocus = function(opt) {
320		var focusEl = this.selEl.querySelector( 'li.cs-focus' )
321		if( focusEl ) {
322			classie.remove( focusEl, 'cs-focus' );
323		}
324	}
325
326	/**
327	 * add to global namespace
328	 */
329	window.SelectFx = SelectFx;
330
331} )( window );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.