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 : false, 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 ) { 60 61 $(this).prop("selectedIndex", $(this).data("index")); 62 63 } 64 } 65 66 /** 67 * init function 68 * initialize and cache some vars 69 */ 70 SelectFx.prototype._init = function() { 71 // check if we are using a placeholder for the native select box 72 // we assume the placeholder is disabled and selected by default 73 var selectedOpt = this.el.querySelector( 'option[selected]' ); 74 this.hasDefaultPlaceholder = selectedOpt && selectedOpt.disabled; 75 76 // get selected option (either the first option with attr selected or just the first option) 77 this.selectedOpt = selectedOpt || this.el.querySelector( 'option' ); 78 79 // create structure 80 this._createSelectEl(); 81 82 // all options 83 this.selOpts = [].slice.call( this.selEl.querySelectorAll( 'li[data-option]' ) ); 84 85 // total options 86 this.selOptsCount = this.selOpts.length; 87 88 // current index 89 this.current = this.selOpts.indexOf( this.selEl.querySelector( 'li.cs-selected' ) ) || -1; 90 91 // placeholder elem 92 this.selPlaceholder = this.selEl.querySelector( 'span.cs-placeholder' ); 93 94 // init events 95 this._initEvents(); 96 } 97 98 /** 99 * creates the structure for the select element 100 */ 101 SelectFx.prototype._createSelectEl = function() { 102 var self = this, options = '', createOptionHTML = function(el) { 103 var optclass = '', classes = '', link = ''; 104 105 if( el.selectedOpt && !this.foundSelected && !this.hasDefaultPlaceholder ) { 106 classes += 'cs-selected '; 107 this.foundSelected = true; 108 } 109 // extra classes 110 if( el.getAttribute( 'data-class' ) ) { 111 classes += el.getAttribute( 'data-class' ); 112 } 113 // link options 114 if( el.getAttribute( 'data-link' ) ) { 115 link = 'data-link=' + el.getAttribute( 'data-link' ); 116 } 117 118 if( classes !== '' ) { 119 optclass = 'class="' + classes + '" '; 120 } 121 122 return '<li ' + optclass + link + ' data-option data-value="' + el.value + '"><span>' + el.textContent + '</span></li>'; 123 }; 124 125 [].slice.call( this.el.children ).forEach( function(el) { 126 if( el.disabled ) { return; } 127 128 var tag = el.tagName.toLowerCase(); 129 130 if( tag === 'option' ) { 131 options += createOptionHTML(el); 132 } 133 else if( tag === 'optgroup' ) { 134 options += '<li class="cs-optgroup"><span>' + el.label + '</span><ul>'; 135 [].slice.call( el.children ).forEach( function(opt) { 136 options += createOptionHTML(opt); 137 } ); 138 options += '</ul></li>'; 139 } 140 } ); 141 142 var opts_el = '<div class="cs-options"><ul>' + options + '</ul></div>'; 143 this.selEl = document.createElement( 'div' ); 144 this.selEl.className = this.el.className; 145 this.selEl.tabIndex = this.el.tabIndex; 146 this.selEl.innerHTML = '<span class="cs-placeholder">' + this.selectedOpt.textContent + '</span>' + opts_el; 147 this.el.parentNode.appendChild( this.selEl ); 148 this.selEl.appendChild( this.el ); 149 } 150 151 /** 152 * initialize the events 153 */ 154 SelectFx.prototype._initEvents = function() { 155 var self = this; 156 157 // open/close select 158 this.selPlaceholder.addEventListener( 'click', function() { 159 self._toggleSelect(); 160 } ); 161 162 // clicking the options
163 this.selOpts.forEach( function(opt, idx) { 164 opt.addEventListener( 'click', function() { 165 self.current = idx; 166 self._changeOption(); 167 // close select elem 168 self._toggleSelect(); 169 } ); 170 } ); 171 172 // close the select element if the target it´s not the select element or one of its descendants.. 173 document.addEventListener( 'click', function(ev) { 174 var target = ev.target; 175 if( self._isOpen() && target !== self.selEl && !hasParent( target, self.selEl ) ) { 176 self._toggleSelect(); 177 } 178 } ); 179 180 // keyboard navigation events 181 this.selEl.addEventListener( 'keydown', function( ev ) { 182 var keyCode = ev.keyCode || ev.which; 183 184 switch (keyCode) { 185 // up key 186 case 38: 187 ev.preventDefault(); 188 self._navigateOpts('prev'); 189 break; 190 // down key 191 case 40: 192 ev.preventDefault(); 193 self._navigateOpts('next'); 194 break; 195 // space key 196 case 32: 197 ev.preventDefault(); 198 if( self._isOpen() && typeof self.preSelCurrent != 'undefined' && self.preSelCurrent !== -1 ) { 199 self._changeOption(); 200 } 201 self._toggleSelect(); 202 break; 203 // enter key 204 case 13: 205 ev.preventDefault(); 206 if( self._isOpen() && typeof self.preSelCurrent != 'undefined' && self.preSelCurrent !== -1 ) { 207 self._changeOption(); 208 self._toggleSelect(); 209 } 210 break; 211 // esc key 212 case 27: 213 ev.preventDefault(); 214 if( self._isOpen() ) { 215 self._toggleSelect(); 216 } 217 break; 218 } 219 } ); 220 } 221 222 /** 223 * navigate with up/dpwn keys 224 */ 225 SelectFx.prototype._navigateOpts = function(dir) { 226 if( !this._isOpen() ) { 227 this._toggleSelect(); 228 } 229 230 var tmpcurrent = typeof this.preSelCurrent != 'undefined' && this.preSelCurrent !== -1 ? this.preSelCurrent : this.current; 231 232 if( dir === 'prev' && tmpcurrent > 0 || dir === 'next' && tmpcurrent < this.selOptsCount - 1 ) { 233 // 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 234 this.preSelCurrent = dir === 'next' ? tmpcurrent + 1 : tmpcurrent - 1; 235 // remove focus class if any.. 236 this._removeFocus(); 237 // add class focus - track which option we are navigating 238 classie.add( this.selOpts[this.preSelCurrent], 'cs-focus' ); 239 } 240 } 241 242 /** 243 * open/close select 244 * when opened show the default placeholder if any 245 */ 246 SelectFx.prototype._toggleSelect = function() { 247 // remove focus class if any.. 248 this._removeFocus(); 249 250 if( this._isOpen() ) { 251 if( this.current !== -1 ) { 252 // update placeholder text 253 this.selPlaceholder.textContent = this.selOpts[ this.current ].textContent; 254 } 255 classie.remove(this.selEl, 'cs-active'); 256 document.body.classList.remove("-selectFx"); 257 258 } 259 else { 260 if( this.hasDefaultPlaceholder && this.options.stickyPlaceholder ) { 261 // everytime we open we wanna see the default placeholder text 262 this.selPlaceholder.textContent = this.selectedOpt.textContent; 263 } 264 classie.add(this.selEl, 'cs-active'); 265 document.body.classList.add("-selectFx"); 266 } 267 } 268 269 /** 270 * change option - the new value is set 271 */ 272 SelectFx.prototype._changeOption = function() { 273 // if pre selected current (if we navigate with the keyboard)... 274 if( typeof this.preSelCurrent != 'undefined' && this.preSelCurrent !== -1 ) { 275 this.current = this.preSelCurrent; 276 this.preSelCurrent = -1; 277 } 278 279 // current option 280 var opt = this.selOpts[ this.current ]; 281 282 // update current selected value 283 this.selPlaceholder.textContent = opt.textContent; 284 285 // change native select element´s value 286 this.el.value = opt.getAttribute( 'data-value' ); 287 288 // remove class cs-selected from old selected option and add it to current selected option 289 var oldOpt = this.selEl.querySelector( 'li.cs-selected' ); 290 if( oldOpt ) { 291 classie.remove( oldOpt, 'cs-selected' ); 292 } 293 classie.add( opt, 'cs-selected' ); 294 295 // if there´s a link defined 296 if( opt.getAttribute( 'data-link' ) ) { 297 298 if( opt.getAttribute( 'data-link' ) == '#' ){ 299 $('.modal.construccion').toggleClass('active'); 300 return; 301 } 302 303 // open in new tab? 304 if( this.options.newTab ) { 305 window.open( opt.getAttribute( 'data-link' ), '_blank' ); 306 } 307 else { 308 window.location = opt.getAttribute( 'data-link' ); 309 } 310 } 311 312 // callback 313 this.options.onChange( this.el.value ); 314 } 315 316 /** 317 * returns true if select element is opened 318 */ 319 SelectFx.prototype._isOpen = function(opt) { 320 return classie.has( this.selEl, 'cs-active' ); 321 } 322 323 /** 324 * removes the focus class from the option 325 */ 326 SelectFx.prototype._removeFocus = function(opt) { 327 var focusEl = this.selEl.querySelector( 'li.cs-focus' ) 328 if( focusEl ) { 329 classie.remove( focusEl, 'cs-focus' ); 330 } 331 } 332
333 /** 334 * add to global namespace 335 */ 336 window.SelectFx = SelectFx; 337 338} )( 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.