├── .nvmrc ├── doc_assets └── example.png ├── .travis.yml ├── demo ├── css │ ├── style.css │ └── normalize.css ├── index.html └── js │ └── jquery.email-autocomplete.js ├── .github └── stale.yml ├── .jshintrc ├── .editorconfig ├── bower.json ├── LICENSE ├── package.json ├── .gitignore ├── Gruntfile.js ├── README.md ├── dist ├── jquery.email-autocomplete.min.js └── jquery.email-autocomplete.js └── src └── jquery.email-autocomplete.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.16.3 2 | -------------------------------------------------------------------------------- /doc_assets/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongzhenlow/email-autocomplete/HEAD/doc_assets/example.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10.16" 4 | 5 | branches: 6 | only: 7 | - master 8 | - gh-pages 9 | -------------------------------------------------------------------------------- /demo/css/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | padding-top:1%; 3 | font-family: 'Source Sans Pro', Arial, san-serif; 4 | font-size: large; 5 | color:#333; 6 | max-width: 600px; 7 | margin: 0 auto; 8 | } 9 | 10 | h1{ 11 | font-weight:700; 12 | } 13 | 14 | input{ 15 | color:#333; 16 | } 17 | 18 | .eac-sugg{ 19 | color:#ccc; 20 | } -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | daysUntilStale: 30 3 | daysUntilClose: 7 4 | staleLabel: stale 5 | exemptLabels: 6 | - "feature-request" 7 | - "WIP" 8 | - "pending" 9 | - "discussion" 10 | exemptAssignees: true 11 | only: issues 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "es5": true, 5 | "esnext": true, 6 | "bitwise": false, 7 | "camelcase": false, 8 | "curly": true, 9 | "eqeqeq": true, 10 | "immed": true, 11 | "indent": 2, 12 | "latedef": true, 13 | "newcap": true, 14 | "noarg": true, 15 | "quotmark": "false", 16 | "regexp": true, 17 | "undef": true, 18 | "unused": true, 19 | "strict": true, 20 | "trailing": false, 21 | "smarttabs": true, 22 | "globals" : { 23 | "jQuery": true, 24 | "Modernizr": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 26 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "email-autocomplete", 3 | "description": "jQuery plugin that displays in-place autocomplete suggestions for email input fields.", 4 | "main": "dist/jquery.email-autocomplete.js", 5 | "authors": [ 6 | "Low Yong Zhen " 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "jquery-plugin", 11 | "autocomplete", 12 | "suggestion", 13 | "typeahead", 14 | "domain", 15 | "email", 16 | "input" 17 | ], 18 | "homepage": "https://github.com/10w042/email-autocomplete", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests" 25 | ], 26 | "dependencies": { 27 | "jquery": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 by Low Yong Zhen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "email-autocomplete", 3 | "version": "0.1.3", 4 | "description": "jQuery plugin that displays in-place autocomplete suggestions for email input fields.", 5 | "keywords": [ 6 | "jquery-plugin", 7 | "ecosystem:jquery", 8 | "autocomplete", 9 | "suggestion", 10 | "typeahead", 11 | "domain", 12 | "email", 13 | "form" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/yongzhenlow/email-autocomplete.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/yongzhenlow/email-autocomplete/issues" 21 | }, 22 | "author": "Low Yong Zhen", 23 | "license": "MIT", 24 | "devDependencies": { 25 | "grunt": "^1.0.1", 26 | "grunt-browser-sync": "^2.2.0", 27 | "grunt-cli": "^1.2.0", 28 | "grunt-contrib-concat": "^1.0.1", 29 | "grunt-contrib-copy": "^1.0.0", 30 | "grunt-contrib-jshint": "^1.0.0", 31 | "grunt-contrib-uglify": "^2.0.0", 32 | "grunt-contrib-watch": "^1.0.0", 33 | "load-grunt-tasks": "^3.5.2" 34 | }, 35 | "dependencies": { 36 | "jquery": "*" 37 | }, 38 | "scripts": { 39 | "test": "grunt travis --verbose" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Autosuggest/autocompletes email domains 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Smarter email fields for a better UX

15 | 16 |

17 | Entering the first few letters of your domain will display a suggestion. (e.g "@g" for "@gmail.com")

18 | Click, or press tab-key to autocomplete your email domains.
(or tap on the suggestion for mobile users.) 19 |

20 | 21 |
22 |
23 | 24 | 25 | Pressing the tab-key autocompletes and jumps to the next field. 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 44 | 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/windows,osx,linux,node,phpstorm 2 | 3 | bower_components 4 | 5 | ### Windows ### 6 | # Windows image file caches 7 | Thumbs.db 8 | ehthumbs.db 9 | 10 | # Folder config file 11 | Desktop.ini 12 | 13 | # Recycle Bin used on file shares 14 | $RECYCLE.BIN/ 15 | 16 | # Windows Installer files 17 | *.cab 18 | *.msi 19 | *.msm 20 | *.msp 21 | 22 | # Windows shortcuts 23 | *.lnk 24 | 25 | 26 | ### OSX ### 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Icon must end with two \r 32 | Icon 33 | 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | 53 | 54 | ### Linux ### 55 | *~ 56 | 57 | # KDE directory preferences 58 | .directory 59 | 60 | # Linux trash folder which might appear on any partition or disk 61 | .Trash-* 62 | 63 | 64 | ### Node ### 65 | # Logs 66 | logs 67 | *.log 68 | npm-debug.log* 69 | 70 | # Runtime data 71 | pids 72 | *.pid 73 | *.seed 74 | 75 | # Directory for instrumented libs generated by jscoverage/JSCover 76 | lib-cov 77 | 78 | # Coverage directory used by tools like istanbul 79 | coverage 80 | 81 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 82 | .grunt 83 | 84 | # node-waf configuration 85 | .lock-wscript 86 | 87 | # Compiled binary addons (http://nodejs.org/api/addons.html) 88 | build/Release 89 | 90 | # Dependency directory 91 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 92 | node_modules 93 | 94 | 95 | ### PhpStorm ### 96 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 97 | 98 | ## Directory-based project format: 99 | .idea/ 100 | 101 | ## File-based project format: 102 | *.ipr 103 | *.iws 104 | 105 | ## Plugin-specific files: 106 | 107 | # IntelliJ 108 | /out/ 109 | 110 | # mpeltonen/sbt-idea plugin 111 | .idea_modules/ 112 | 113 | # JIRA plugin 114 | atlassian-ide-plugin.xml 115 | 116 | # Crashlytics plugin (for Android Studio and IntelliJ) 117 | com_crashlytics_export_strings.xml 118 | crashlytics.properties 119 | crashlytics-build.properties 120 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Gruntfile 3 | * @author Yong Zhen 4 | */ 5 | "use strict"; 6 | 7 | module.exports = function (grunt) { 8 | 9 | // Load all grunt tasks matching the ['grunt-*', '@*/grunt-*'] patterns 10 | require('load-grunt-tasks')(grunt); 11 | 12 | grunt.initConfig({ 13 | 14 | // Import package manifest 15 | pkg: grunt.file.readJSON("package.json"), 16 | 17 | project: { 18 | src: "src", 19 | dist: "dist", 20 | demo: "demo" 21 | }, 22 | 23 | // Banner definitions 24 | meta: { 25 | banner: "/*\n" + 26 | " * <%= pkg.name %> - <%= pkg.version %>\n" + 27 | " * <%= pkg.description %>\n" + 28 | " * <%= pkg.homepage %>\n" + 29 | " *\n" + 30 | " * Made by <%= pkg.author %> \n" + 31 | " */\n" 32 | }, 33 | 34 | browserSync: { 35 | bsFiles: { 36 | src: [ 37 | "<%= project.demo %>/**/*.html", 38 | "<%= project.demo %>/css/**/*.css", 39 | "<%= project.demo %>/js/**/*.js" 40 | ] 41 | }, 42 | options: { 43 | watchTask: true, 44 | server: { 45 | baseDir: "./demo" 46 | } 47 | } 48 | }, 49 | 50 | //watch for changes 51 | watch: { 52 | scripts: { 53 | files: "<%= project.src %>/**/*.js", 54 | tasks: ["common"] 55 | } 56 | }, 57 | 58 | // Concat definitions 59 | concat: { 60 | options: { 61 | banner: "<%= meta.banner %>" 62 | }, 63 | dist: { 64 | src: ["<%= project.src %>/jquery.email-autocomplete.js"], 65 | dest: "<%= project.dist %>/jquery.email-autocomplete.js" 66 | } 67 | }, 68 | 69 | copy: { 70 | demo: { 71 | files: [{ 72 | expand: true, 73 | cwd: '<%= project.dist %>', 74 | dest: '<%= project.demo %>/js/', 75 | src: ['jquery.email-autocomplete.js'] 76 | }] 77 | } 78 | }, 79 | 80 | // Lint definitions 81 | jshint: { 82 | files: [ 83 | "<%= project.src %>/jquery.email-autocomplete.js", 84 | "Gruntfile.js" 85 | ], 86 | options: { 87 | jshintrc: ".jshintrc" 88 | } 89 | }, 90 | 91 | // Minify definitions 92 | uglify: { 93 | dist: { 94 | src: ["<%= project.src %>/jquery.email-autocomplete.js"], 95 | dest: "<%= project.dist %>/jquery.email-autocomplete.min.js" 96 | }, 97 | options: { 98 | banner: "<%= meta.banner %>", 99 | mangle: true 100 | } 101 | } 102 | 103 | }); 104 | 105 | grunt.registerTask("travis", ["jshint"]); 106 | 107 | grunt.registerTask("common", [ 108 | "jshint", 109 | "concat", 110 | "copy" 111 | ]); 112 | 113 | grunt.registerTask("server", [ 114 | "common", 115 | "browserSync", 116 | "watch" 117 | ]); 118 | 119 | grunt.registerTask("default", [ 120 | "common", 121 | "uglify" 122 | ]); 123 | 124 | }; 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jquery.email-autocomplete.js 2 | 3 | > A jQuery plugin that suggests and autocompletes the domain whenever your users type in an email address field. 4 | 5 | [![Build Status](https://travis-ci.org/yongzhenlow/email-autocomplete.svg?branch=master)](https://travis-ci.org/yongzhenlow/email-autocomplete) 6 | [![CDNJS version](https://img.shields.io/cdnjs/v/email-autocomplete.svg)](https://cdnjs.com/libraries/email-autocomplete) 7 | 8 | 9 | ## What does it do? 10 | 11 | When your user types in "user@gm", the plugin will suggest for e.g. "user@gmail.com", based on the first result from a list of predefined email domains. 12 | 13 | ![diagram](https://raw.github.com/yongzhenlow/email-autocomplete/master/doc_assets/example.png) 14 | 15 | Press the tab-key, or simply click on the suggestion to automatically fill in the rest of the domain. (or tap on the suggestion for mobile users.) 16 | 17 | You can also use the right arrow key. 18 | 19 | See a live demo [here](http://yongzhenlow.github.io/email-autocomplete/demo/). 20 | 21 | ## Installation 22 | 23 | **Bower** 24 | 25 | ```sh 26 | bower install email-autocomplete --save 27 | ``` 28 | 29 | **Download** 30 | 31 | Download or clone this repo and copy `dist/jquery.email-autocomplete.min.js` into your javascripts directory. 32 | 33 | ## Usage (jQuery) 34 | 35 | Add `jquery.email-autocomplete.min.js` into your HTML, before the closing `` tag. 36 | 37 | ```html 38 | 39 | 40 | ``` 41 | 42 | You should also have a email input field. 43 | 44 | ```html 45 | 46 | ``` 47 | 48 | Now, attach the plugin to the email input field. 49 | 50 | ```html 51 | 57 | ``` 58 | 59 | ## Settings 60 | 61 | Option | Type | Default | Description 62 | ------ | ---- | ------- | ----------- 63 | suggClass|string|'eac-sugg'|Classname for the suggestion text element. 64 | domains|array|See list of domains below|Array of domains used for autocompleting. 65 | 66 | ## Styling 67 | 68 | Use the following CSS to style the suggestion text color. Remember to update the classname if you've changed it to a custom one. 69 | 70 | ```css 71 | .eac-sugg { 72 | color: #ccc; 73 | } 74 | ``` 75 | 76 | ## Domains 77 | 78 | These are the plugin default domains if the `domains` option is not supplied. 79 | 80 | * gmail.com 81 | * googlemail.com 82 | * yahoo.com 83 | * yahoo.co.uk 84 | * hotmail.com 85 | * hotmail.co.uk 86 | * live.com 87 | * msn.com 88 | * comcast.net 89 | * sbcglobal.net 90 | * verizon.net 91 | * facebook.com 92 | * outlook.com 93 | * att.net 94 | * gmx.com 95 | * icloud.com 96 | * me.com 97 | * mac.com 98 | * aol.com 99 | 100 | ## Author 101 | 102 | - Low Yong Zhen 103 | 104 | 105 | ## Known Issues 106 | 107 | * On Android stock browser, if "Settings > Accessibility > Scale text up and down" value is not at 100%, the text width will be calculated incorrectly. 108 | -------------------------------------------------------------------------------- /dist/jquery.email-autocomplete.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * email-autocomplete - 0.1.3 3 | * jQuery plugin that displays in-place autocomplete suggestions for email input fields. 4 | * 5 | * 6 | * Made by Low Yong Zhen 7 | */ 8 | 9 | "use strict";!function(a,b,c,d){function e(b,c){this.$field=a(b),this.options=a.extend(!0,{},f,c),this._defaults=f,this._domains=this.options.domains,this.init()}var f={suggClass:"eac-sugg",domains:["yahoo.com","hotmail.com","gmail.com","me.com","aol.com","mac.com","live.com","comcast.net","googlemail.com","msn.com","hotmail.co.uk","yahoo.co.uk","facebook.com","verizon.net","sbcglobal.net","att.net","gmx.com","outlook.com","icloud.com"]};e.prototype={init:function(){Array.prototype.indexOf||this.doIndexOf(),this.fieldLeftOffset=null;var b=a("
").css({display:this.$field.css("display"),position:"static"===this.$field.css("position")?"relative":this.$field.css("position"),fontSize:this.$field.css("fontSize")});this.$field.wrap(b),this.$cval=a("").css({visibility:"hidden",position:"absolute",display:"inline-block",fontFamily:this.$field.css("fontFamily"),fontWeight:this.$field.css("fontWeight"),letterSpacing:this.$field.css("letterSpacing")}).insertAfter(this.$field);var c=(this.$field.outerHeight(!0)-this.$field.height())/2;this.$suggOverlay=a("").css({display:"block","box-sizing":"content-box",lineHeight:this.$field.css("lineHeight"),paddingTop:c+"px",paddingBottom:c+"px",fontFamily:this.$field.css("fontFamily"),fontWeight:this.$field.css("fontWeight"),letterSpacing:this.$field.css("letterSpacing"),position:"absolute",top:0,left:0}).insertAfter(this.$field),this.$field.on("keyup.eac",a.proxy(this.displaySuggestion,this)),this.$field.on("blur.eac",a.proxy(this.autocomplete,this)),this.$field.on("keydown.eac",a.proxy(function(a){39!==a.which&&9!==a.which||this.autocomplete()},this)),this.$suggOverlay.on("mousedown.eac touchstart.eac",a.proxy(this.autocomplete,this))},suggest:function(a){var b=a.split("@");return b.length>1?(a=b.pop(),a.length?(this._domains.filter(function(b){return 0===b.indexOf(a)}).shift()||"").replace(a,""):""):""},autocomplete:function(){if(void 0===this.suggestion||this.suggestion.length<1)return!1;this.$field.val(this.val+this.suggestion),this.$suggOverlay.text(""),this.$cval.text("")},displaySuggestion:function(a){this.val=this.$field.val(),this.suggestion=this.suggest(this.val),this.suggestion.length?a.preventDefault():this.$suggOverlay.text(""),this.$suggOverlay.text(this.suggestion),this.$cval.text(this.val),null===this.fieldLeftOffset&&(this.fieldLeftOffset=(this.$field.outerWidth(!0)-this.$field.width())/2);var b=this.$cval.width();this.$field.outerWidth()>b&&this.$suggOverlay.css("left",this.fieldLeftOffset+b+"px")},doIndexOf:function(){Array.prototype.indexOf=function(a,b){if(void 0===this||null===this)throw new TypeError('"this" is null or not defined');var c=this.length>>>0;for(b=+b||0,Math.abs(b)===1/0&&(b=0),b<0&&(b+=c)<0&&(b=0);b").css({ 32 | display: this.$field.css("display"), 33 | position: this.$field.css("position") === 'static' ? 'relative' : this.$field.css("position"), 34 | fontSize: this.$field.css("fontSize") 35 | }); 36 | this.$field.wrap($wrap); 37 | 38 | //create container to test width of current val 39 | this.$cval = $("").css({ 40 | visibility: "hidden", 41 | position: "absolute", 42 | display: "inline-block", 43 | fontFamily: this.$field.css("fontFamily"), 44 | fontWeight: this.$field.css("fontWeight"), 45 | letterSpacing: this.$field.css("letterSpacing") 46 | }).insertAfter(this.$field); 47 | 48 | //create the suggestion overlay 49 | /* touchstart jquery 1.7+ */ 50 | var heightPad = (this.$field.outerHeight(true) - this.$field.height()) / 2; //padding+border 51 | this.$suggOverlay = $("").css({ 52 | display: "block", 53 | "box-sizing": "content-box", //standardize 54 | lineHeight: this.$field.css('lineHeight'), 55 | paddingTop: heightPad + "px", 56 | paddingBottom: heightPad + "px", 57 | fontFamily: this.$field.css("fontFamily"), 58 | fontWeight: this.$field.css("fontWeight"), 59 | letterSpacing: this.$field.css("letterSpacing"), 60 | position: "absolute", 61 | top: 0, 62 | left: 0 63 | }).insertAfter(this.$field); 64 | 65 | //bind events and handlers 66 | this.$field.on("keyup.eac", $.proxy(this.displaySuggestion, this)); 67 | 68 | this.$field.on("blur.eac", $.proxy(this.autocomplete, this)); 69 | 70 | this.$field.on("keydown.eac", $.proxy(function(e){ 71 | if(e.which === 39 || e.which === 9){ 72 | this.autocomplete(); 73 | } 74 | }, this)); 75 | 76 | this.$suggOverlay.on("mousedown.eac touchstart.eac", $.proxy(this.autocomplete, this)); 77 | }, 78 | 79 | suggest: function (str) { 80 | var str_arr = str.split("@"); 81 | if (str_arr.length > 1) { 82 | str = str_arr.pop(); 83 | if (!str.length) { 84 | return ""; 85 | } 86 | } else { 87 | return ""; 88 | } 89 | 90 | var match = this._domains.filter(function (domain) { 91 | return domain.indexOf(str) === 0; 92 | }).shift() || ""; 93 | 94 | return match.replace(str, ""); 95 | }, 96 | 97 | autocomplete: function () { 98 | if(typeof this.suggestion === "undefined" || this.suggestion.length < 1){ 99 | return false; 100 | } 101 | this.$field.val(this.val + this.suggestion); 102 | this.$suggOverlay.text(""); 103 | this.$cval.text(""); 104 | }, 105 | 106 | /** 107 | * Displays the suggestion, handler for field keyup event 108 | */ 109 | displaySuggestion: function (e) { 110 | this.val = this.$field.val(); 111 | this.suggestion = this.suggest(this.val); 112 | 113 | if (!this.suggestion.length) { 114 | this.$suggOverlay.text(""); 115 | } else { 116 | e.preventDefault(); 117 | } 118 | 119 | //update with new suggestion 120 | this.$suggOverlay.text(this.suggestion); 121 | this.$cval.text(this.val); 122 | 123 | // get input padding, border and margin to offset text 124 | if(this.fieldLeftOffset === null){ 125 | this.fieldLeftOffset = (this.$field.outerWidth(true) - this.$field.width()) / 2; 126 | } 127 | 128 | //find width of current input val so we can offset the suggestion text 129 | var cvalWidth = this.$cval.width(); 130 | 131 | if(this.$field.outerWidth() > cvalWidth){ 132 | //offset our suggestion container 133 | this.$suggOverlay.css('left', this.fieldLeftOffset + cvalWidth + "px"); 134 | } 135 | }, 136 | 137 | /** 138 | * indexof polyfill 139 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill 140 | */ 141 | doIndexOf: function(){ 142 | 143 | Array.prototype.indexOf = function (searchElement, fromIndex) { 144 | if ( this === undefined || this === null ) { 145 | throw new TypeError( '"this" is null or not defined' ); 146 | } 147 | 148 | var length = this.length >>> 0; // Hack to convert object.length to a UInt32 149 | 150 | fromIndex = +fromIndex || 0; 151 | 152 | if (Math.abs(fromIndex) === Infinity) { 153 | fromIndex = 0; 154 | } 155 | 156 | if (fromIndex < 0) { 157 | fromIndex += length; 158 | if (fromIndex < 0) { 159 | fromIndex = 0; 160 | } 161 | } 162 | 163 | for (;fromIndex < length; fromIndex++) { 164 | if (this[fromIndex] === searchElement) { 165 | return fromIndex; 166 | } 167 | } 168 | 169 | return -1; 170 | }; 171 | } 172 | }; 173 | 174 | $.fn[pluginName] = function (options) { 175 | return this.each(function () { 176 | if (!$.data(this, "yz_" + pluginName)) { 177 | $.data(this, "yz_" + pluginName, new EmailAutocomplete(this, options)); 178 | } 179 | }); 180 | }; 181 | 182 | })(jQuery, window, document); 183 | -------------------------------------------------------------------------------- /demo/js/jquery.email-autocomplete.js: -------------------------------------------------------------------------------- 1 | /* 2 | * email-autocomplete - 0.1.3 3 | * jQuery plugin that displays in-place autocomplete suggestions for email input fields. 4 | * 5 | * 6 | * Made by Low Yong Zhen 7 | */ 8 | "use strict"; 9 | 10 | (function ($, window, document, undefined) { 11 | 12 | var pluginName = "emailautocomplete"; 13 | var defaults = { 14 | suggClass: "eac-sugg", 15 | domains: ["yahoo.com" ,"hotmail.com" ,"gmail.com" ,"me.com" ,"aol.com" ,"mac.com" ,"live.com" ,"comcast.net" ,"googlemail.com" ,"msn.com" ,"hotmail.co.uk" ,"yahoo.co.uk" ,"facebook.com" ,"verizon.net" ,"sbcglobal.net" ,"att.net" ,"gmx.com" ,"outlook.com" ,"icloud.com"] 16 | }; 17 | 18 | function EmailAutocomplete(elem, options) { 19 | this.$field = $(elem); 20 | this.options = $.extend(true, {}, defaults, options); //we want deep extend 21 | this._defaults = defaults; 22 | this._domains = this.options.domains; 23 | this.init(); 24 | } 25 | 26 | EmailAutocomplete.prototype = { 27 | init: function () { 28 | 29 | //shim indexOf 30 | if (!Array.prototype.indexOf) { 31 | this.doIndexOf(); 32 | } 33 | 34 | //this will be calculated upon keyup 35 | this.fieldLeftOffset = null; 36 | 37 | //wrap our field 38 | var $wrap = $("
").css({ 39 | display: this.$field.css("display"), 40 | position: this.$field.css("position") === 'static' ? 'relative' : this.$field.css("position"), 41 | fontSize: this.$field.css("fontSize") 42 | }); 43 | this.$field.wrap($wrap); 44 | 45 | //create container to test width of current val 46 | this.$cval = $("").css({ 47 | visibility: "hidden", 48 | position: "absolute", 49 | display: "inline-block", 50 | fontFamily: this.$field.css("fontFamily"), 51 | fontWeight: this.$field.css("fontWeight"), 52 | letterSpacing: this.$field.css("letterSpacing") 53 | }).insertAfter(this.$field); 54 | 55 | //create the suggestion overlay 56 | /* touchstart jquery 1.7+ */ 57 | var heightPad = (this.$field.outerHeight(true) - this.$field.height()) / 2; //padding+border 58 | this.$suggOverlay = $("").css({ 59 | display: "block", 60 | "box-sizing": "content-box", //standardize 61 | lineHeight: this.$field.css('lineHeight'), 62 | paddingTop: heightPad + "px", 63 | paddingBottom: heightPad + "px", 64 | fontFamily: this.$field.css("fontFamily"), 65 | fontWeight: this.$field.css("fontWeight"), 66 | letterSpacing: this.$field.css("letterSpacing"), 67 | position: "absolute", 68 | top: 0, 69 | left: 0 70 | }).insertAfter(this.$field); 71 | 72 | //bind events and handlers 73 | this.$field.on("keyup.eac", $.proxy(this.displaySuggestion, this)); 74 | 75 | this.$field.on("blur.eac", $.proxy(this.autocomplete, this)); 76 | 77 | this.$field.on("keydown.eac", $.proxy(function(e){ 78 | if(e.which === 39 || e.which === 9){ 79 | this.autocomplete(); 80 | } 81 | }, this)); 82 | 83 | this.$suggOverlay.on("mousedown.eac touchstart.eac", $.proxy(this.autocomplete, this)); 84 | }, 85 | 86 | suggest: function (str) { 87 | var str_arr = str.split("@"); 88 | if (str_arr.length > 1) { 89 | str = str_arr.pop(); 90 | if (!str.length) { 91 | return ""; 92 | } 93 | } else { 94 | return ""; 95 | } 96 | 97 | var match = this._domains.filter(function (domain) { 98 | return domain.indexOf(str) === 0; 99 | }).shift() || ""; 100 | 101 | return match.replace(str, ""); 102 | }, 103 | 104 | autocomplete: function () { 105 | if(typeof this.suggestion === "undefined" || this.suggestion.length < 1){ 106 | return false; 107 | } 108 | this.$field.val(this.val + this.suggestion); 109 | this.$suggOverlay.text(""); 110 | this.$cval.text(""); 111 | }, 112 | 113 | /** 114 | * Displays the suggestion, handler for field keyup event 115 | */ 116 | displaySuggestion: function (e) { 117 | this.val = this.$field.val(); 118 | this.suggestion = this.suggest(this.val); 119 | 120 | if (!this.suggestion.length) { 121 | this.$suggOverlay.text(""); 122 | } else { 123 | e.preventDefault(); 124 | } 125 | 126 | //update with new suggestion 127 | this.$suggOverlay.text(this.suggestion); 128 | this.$cval.text(this.val); 129 | 130 | // get input padding, border and margin to offset text 131 | if(this.fieldLeftOffset === null){ 132 | this.fieldLeftOffset = (this.$field.outerWidth(true) - this.$field.width()) / 2; 133 | } 134 | 135 | //find width of current input val so we can offset the suggestion text 136 | var cvalWidth = this.$cval.width(); 137 | 138 | if(this.$field.outerWidth() > cvalWidth){ 139 | //offset our suggestion container 140 | this.$suggOverlay.css('left', this.fieldLeftOffset + cvalWidth + "px"); 141 | } 142 | }, 143 | 144 | /** 145 | * indexof polyfill 146 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill 147 | */ 148 | doIndexOf: function(){ 149 | 150 | Array.prototype.indexOf = function (searchElement, fromIndex) { 151 | if ( this === undefined || this === null ) { 152 | throw new TypeError( '"this" is null or not defined' ); 153 | } 154 | 155 | var length = this.length >>> 0; // Hack to convert object.length to a UInt32 156 | 157 | fromIndex = +fromIndex || 0; 158 | 159 | if (Math.abs(fromIndex) === Infinity) { 160 | fromIndex = 0; 161 | } 162 | 163 | if (fromIndex < 0) { 164 | fromIndex += length; 165 | if (fromIndex < 0) { 166 | fromIndex = 0; 167 | } 168 | } 169 | 170 | for (;fromIndex < length; fromIndex++) { 171 | if (this[fromIndex] === searchElement) { 172 | return fromIndex; 173 | } 174 | } 175 | 176 | return -1; 177 | }; 178 | } 179 | }; 180 | 181 | $.fn[pluginName] = function (options) { 182 | return this.each(function () { 183 | if (!$.data(this, "yz_" + pluginName)) { 184 | $.data(this, "yz_" + pluginName, new EmailAutocomplete(this, options)); 185 | } 186 | }); 187 | }; 188 | 189 | })(jQuery, window, document); 190 | -------------------------------------------------------------------------------- /dist/jquery.email-autocomplete.js: -------------------------------------------------------------------------------- 1 | /* 2 | * email-autocomplete - 0.1.3 3 | * jQuery plugin that displays in-place autocomplete suggestions for email input fields. 4 | * 5 | * 6 | * Made by Low Yong Zhen 7 | */ 8 | "use strict"; 9 | 10 | (function ($, window, document, undefined) { 11 | 12 | var pluginName = "emailautocomplete"; 13 | var defaults = { 14 | suggClass: "eac-sugg", 15 | domains: ["yahoo.com" ,"hotmail.com" ,"gmail.com" ,"me.com" ,"aol.com" ,"mac.com" ,"live.com" ,"comcast.net" ,"googlemail.com" ,"msn.com" ,"hotmail.co.uk" ,"yahoo.co.uk" ,"facebook.com" ,"verizon.net" ,"sbcglobal.net" ,"att.net" ,"gmx.com" ,"outlook.com" ,"icloud.com"] 16 | }; 17 | 18 | function EmailAutocomplete(elem, options) { 19 | this.$field = $(elem); 20 | this.options = $.extend(true, {}, defaults, options); //we want deep extend 21 | this._defaults = defaults; 22 | this._domains = this.options.domains; 23 | this.init(); 24 | } 25 | 26 | EmailAutocomplete.prototype = { 27 | init: function () { 28 | 29 | //shim indexOf 30 | if (!Array.prototype.indexOf) { 31 | this.doIndexOf(); 32 | } 33 | 34 | //this will be calculated upon keyup 35 | this.fieldLeftOffset = null; 36 | 37 | //wrap our field 38 | var $wrap = $("
").css({ 39 | display: this.$field.css("display"), 40 | position: this.$field.css("position") === 'static' ? 'relative' : this.$field.css("position"), 41 | fontSize: this.$field.css("fontSize") 42 | }); 43 | this.$field.wrap($wrap); 44 | 45 | //create container to test width of current val 46 | this.$cval = $("").css({ 47 | visibility: "hidden", 48 | position: "absolute", 49 | display: "inline-block", 50 | fontFamily: this.$field.css("fontFamily"), 51 | fontWeight: this.$field.css("fontWeight"), 52 | letterSpacing: this.$field.css("letterSpacing") 53 | }).insertAfter(this.$field); 54 | 55 | //create the suggestion overlay 56 | /* touchstart jquery 1.7+ */ 57 | var heightPad = (this.$field.outerHeight(true) - this.$field.height()) / 2; //padding+border 58 | this.$suggOverlay = $("").css({ 59 | display: "block", 60 | "box-sizing": "content-box", //standardize 61 | lineHeight: this.$field.css('lineHeight'), 62 | paddingTop: heightPad + "px", 63 | paddingBottom: heightPad + "px", 64 | fontFamily: this.$field.css("fontFamily"), 65 | fontWeight: this.$field.css("fontWeight"), 66 | letterSpacing: this.$field.css("letterSpacing"), 67 | position: "absolute", 68 | top: 0, 69 | left: 0 70 | }).insertAfter(this.$field); 71 | 72 | //bind events and handlers 73 | this.$field.on("keyup.eac", $.proxy(this.displaySuggestion, this)); 74 | 75 | this.$field.on("blur.eac", $.proxy(this.autocomplete, this)); 76 | 77 | this.$field.on("keydown.eac", $.proxy(function(e){ 78 | if(e.which === 39 || e.which === 9){ 79 | this.autocomplete(); 80 | } 81 | }, this)); 82 | 83 | this.$suggOverlay.on("mousedown.eac touchstart.eac", $.proxy(this.autocomplete, this)); 84 | }, 85 | 86 | suggest: function (str) { 87 | var str_arr = str.split("@"); 88 | if (str_arr.length > 1) { 89 | str = str_arr.pop(); 90 | if (!str.length) { 91 | return ""; 92 | } 93 | } else { 94 | return ""; 95 | } 96 | 97 | var match = this._domains.filter(function (domain) { 98 | return domain.indexOf(str) === 0; 99 | }).shift() || ""; 100 | 101 | return match.replace(str, ""); 102 | }, 103 | 104 | autocomplete: function () { 105 | if(typeof this.suggestion === "undefined" || this.suggestion.length < 1){ 106 | return false; 107 | } 108 | this.$field.val(this.val + this.suggestion); 109 | this.$suggOverlay.text(""); 110 | this.$cval.text(""); 111 | }, 112 | 113 | /** 114 | * Displays the suggestion, handler for field keyup event 115 | */ 116 | displaySuggestion: function (e) { 117 | this.val = this.$field.val(); 118 | this.suggestion = this.suggest(this.val); 119 | 120 | if (!this.suggestion.length) { 121 | this.$suggOverlay.text(""); 122 | } else { 123 | e.preventDefault(); 124 | } 125 | 126 | //update with new suggestion 127 | this.$suggOverlay.text(this.suggestion); 128 | this.$cval.text(this.val); 129 | 130 | // get input padding, border and margin to offset text 131 | if(this.fieldLeftOffset === null){ 132 | this.fieldLeftOffset = (this.$field.outerWidth(true) - this.$field.width()) / 2; 133 | } 134 | 135 | //find width of current input val so we can offset the suggestion text 136 | var cvalWidth = this.$cval.width(); 137 | 138 | if(this.$field.outerWidth() > cvalWidth){ 139 | //offset our suggestion container 140 | this.$suggOverlay.css('left', this.fieldLeftOffset + cvalWidth + "px"); 141 | } 142 | }, 143 | 144 | /** 145 | * indexof polyfill 146 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill 147 | */ 148 | doIndexOf: function(){ 149 | 150 | Array.prototype.indexOf = function (searchElement, fromIndex) { 151 | if ( this === undefined || this === null ) { 152 | throw new TypeError( '"this" is null or not defined' ); 153 | } 154 | 155 | var length = this.length >>> 0; // Hack to convert object.length to a UInt32 156 | 157 | fromIndex = +fromIndex || 0; 158 | 159 | if (Math.abs(fromIndex) === Infinity) { 160 | fromIndex = 0; 161 | } 162 | 163 | if (fromIndex < 0) { 164 | fromIndex += length; 165 | if (fromIndex < 0) { 166 | fromIndex = 0; 167 | } 168 | } 169 | 170 | for (;fromIndex < length; fromIndex++) { 171 | if (this[fromIndex] === searchElement) { 172 | return fromIndex; 173 | } 174 | } 175 | 176 | return -1; 177 | }; 178 | } 179 | }; 180 | 181 | $.fn[pluginName] = function (options) { 182 | return this.each(function () { 183 | if (!$.data(this, "yz_" + pluginName)) { 184 | $.data(this, "yz_" + pluginName, new EmailAutocomplete(this, options)); 185 | } 186 | }); 187 | }; 188 | 189 | })(jQuery, window, document); 190 | -------------------------------------------------------------------------------- /demo/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.2 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | main, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | 26 | /** 27 | * Correct `inline-block` display not defined in IE 8/9. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | } 35 | 36 | /** 37 | * Prevent modern browsers from displaying `audio` without controls. 38 | * Remove excess height in iOS 5 devices. 39 | */ 40 | 41 | audio:not([controls]) { 42 | display: none; 43 | height: 0; 44 | } 45 | 46 | /** 47 | * Address styling not present in IE 8/9. 48 | */ 49 | 50 | [hidden] { 51 | display: none; 52 | } 53 | 54 | /* ========================================================================== 55 | Base 56 | ========================================================================== */ 57 | 58 | /** 59 | * 1. Set default font family to sans-serif. 60 | * 2. Prevent iOS text size adjust after orientation change, without disabling 61 | * user zoom. 62 | */ 63 | 64 | html { 65 | font-family: sans-serif; /* 1 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | -webkit-text-size-adjust: 100%; /* 2 */ 68 | } 69 | 70 | /** 71 | * Remove default margin. 72 | */ 73 | 74 | body { 75 | margin: 0; 76 | } 77 | 78 | /* ========================================================================== 79 | Links 80 | ========================================================================== */ 81 | 82 | /** 83 | * Address `outline` inconsistency between Chrome and other browsers. 84 | */ 85 | 86 | a:focus { 87 | outline: thin dotted; 88 | } 89 | 90 | /** 91 | * Improve readability when focused and also mouse hovered in all browsers. 92 | */ 93 | 94 | a:active, 95 | a:hover { 96 | outline: 0; 97 | } 98 | 99 | /* ========================================================================== 100 | Typography 101 | ========================================================================== */ 102 | 103 | /** 104 | * Address variable `h1` font-size and margin within `section` and `article` 105 | * contexts in Firefox 4+, Safari 5, and Chrome. 106 | */ 107 | 108 | h1 { 109 | font-size: 2em; 110 | margin: 0.67em 0; 111 | } 112 | 113 | /** 114 | * Address styling not present in IE 8/9, Safari 5, and Chrome. 115 | */ 116 | 117 | abbr[title] { 118 | border-bottom: 1px dotted; 119 | } 120 | 121 | /** 122 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 123 | */ 124 | 125 | b, 126 | strong { 127 | font-weight: bold; 128 | } 129 | 130 | /** 131 | * Address styling not present in Safari 5 and Chrome. 132 | */ 133 | 134 | dfn { 135 | font-style: italic; 136 | } 137 | 138 | /** 139 | * Address differences between Firefox and other browsers. 140 | */ 141 | 142 | hr { 143 | -moz-box-sizing: content-box; 144 | box-sizing: content-box; 145 | height: 0; 146 | } 147 | 148 | /** 149 | * Address styling not present in IE 8/9. 150 | */ 151 | 152 | mark { 153 | background: #ff0; 154 | color: #000; 155 | } 156 | 157 | /** 158 | * Correct font family set oddly in Safari 5 and Chrome. 159 | */ 160 | 161 | code, 162 | kbd, 163 | pre, 164 | samp { 165 | font-family: monospace, serif; 166 | font-size: 1em; 167 | } 168 | 169 | /** 170 | * Improve readability of pre-formatted text in all browsers. 171 | */ 172 | 173 | pre { 174 | white-space: pre-wrap; 175 | } 176 | 177 | /** 178 | * Set consistent quote types. 179 | */ 180 | 181 | q { 182 | quotes: "\201C" "\201D" "\2018" "\2019"; 183 | } 184 | 185 | /** 186 | * Address inconsistent and variable font size in all browsers. 187 | */ 188 | 189 | small { 190 | font-size: 80%; 191 | } 192 | 193 | /** 194 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 195 | */ 196 | 197 | sub, 198 | sup { 199 | font-size: 75%; 200 | line-height: 0; 201 | position: relative; 202 | vertical-align: baseline; 203 | } 204 | 205 | sup { 206 | top: -0.5em; 207 | } 208 | 209 | sub { 210 | bottom: -0.25em; 211 | } 212 | 213 | /* ========================================================================== 214 | Embedded content 215 | ========================================================================== */ 216 | 217 | /** 218 | * Remove border when inside `a` element in IE 8/9. 219 | */ 220 | 221 | img { 222 | border: 0; 223 | } 224 | 225 | /** 226 | * Correct overflow displayed oddly in IE 9. 227 | */ 228 | 229 | svg:not(:root) { 230 | overflow: hidden; 231 | } 232 | 233 | /* ========================================================================== 234 | Figures 235 | ========================================================================== */ 236 | 237 | /** 238 | * Address margin not present in IE 8/9 and Safari 5. 239 | */ 240 | 241 | figure { 242 | margin: 0; 243 | } 244 | 245 | /* ========================================================================== 246 | Forms 247 | ========================================================================== */ 248 | 249 | /** 250 | * Define consistent border, margin, and padding. 251 | */ 252 | 253 | fieldset { 254 | border: 1px solid #c0c0c0; 255 | margin: 0 2px; 256 | padding: 0.35em 0.625em 0.75em; 257 | } 258 | 259 | /** 260 | * 1. Correct `color` not being inherited in IE 8/9. 261 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 262 | */ 263 | 264 | legend { 265 | border: 0; /* 1 */ 266 | padding: 0; /* 2 */ 267 | } 268 | 269 | /** 270 | * 1. Correct font family not being inherited in all browsers. 271 | * 2. Correct font size not being inherited in all browsers. 272 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. 273 | */ 274 | 275 | button, 276 | input, 277 | select, 278 | textarea { 279 | font-family: inherit; /* 1 */ 280 | font-size: 100%; /* 2 */ 281 | margin: 0; /* 3 */ 282 | } 283 | 284 | /** 285 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 286 | * the UA stylesheet. 287 | */ 288 | 289 | button, 290 | input { 291 | line-height: normal; 292 | } 293 | 294 | /** 295 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 296 | * All other form control elements do not inherit `text-transform` values. 297 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. 298 | * Correct `select` style inheritance in Firefox 4+ and Opera. 299 | */ 300 | 301 | button, 302 | select { 303 | text-transform: none; 304 | } 305 | 306 | /** 307 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 308 | * and `video` controls. 309 | * 2. Correct inability to style clickable `input` types in iOS. 310 | * 3. Improve usability and consistency of cursor style between image-type 311 | * `input` and others. 312 | */ 313 | 314 | button, 315 | html input[type="button"], /* 1 */ 316 | input[type="reset"], 317 | input[type="submit"] { 318 | -webkit-appearance: button; /* 2 */ 319 | cursor: pointer; /* 3 */ 320 | } 321 | 322 | /** 323 | * Re-set default cursor for disabled elements. 324 | */ 325 | 326 | button[disabled], 327 | html input[disabled] { 328 | cursor: default; 329 | } 330 | 331 | /** 332 | * 1. Address box sizing set to `content-box` in IE 8/9. 333 | * 2. Remove excess padding in IE 8/9. 334 | */ 335 | 336 | input[type="checkbox"], 337 | input[type="radio"] { 338 | box-sizing: border-box; /* 1 */ 339 | padding: 0; /* 2 */ 340 | } 341 | 342 | /** 343 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 344 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 345 | * (include `-moz` to future-proof). 346 | */ 347 | 348 | input[type="search"] { 349 | -webkit-appearance: textfield; /* 1 */ 350 | -moz-box-sizing: content-box; 351 | -webkit-box-sizing: content-box; /* 2 */ 352 | box-sizing: content-box; 353 | } 354 | 355 | /** 356 | * Remove inner padding and search cancel button in Safari 5 and Chrome 357 | * on OS X. 358 | */ 359 | 360 | input[type="search"]::-webkit-search-cancel-button, 361 | input[type="search"]::-webkit-search-decoration { 362 | -webkit-appearance: none; 363 | } 364 | 365 | /** 366 | * Remove inner padding and border in Firefox 4+. 367 | */ 368 | 369 | button::-moz-focus-inner, 370 | input::-moz-focus-inner { 371 | border: 0; 372 | padding: 0; 373 | } 374 | 375 | /** 376 | * 1. Remove default vertical scrollbar in IE 8/9. 377 | * 2. Improve readability and alignment in all browsers. 378 | */ 379 | 380 | textarea { 381 | overflow: auto; /* 1 */ 382 | vertical-align: top; /* 2 */ 383 | } 384 | 385 | /* ========================================================================== 386 | Tables 387 | ========================================================================== */ 388 | 389 | /** 390 | * Remove most spacing between table cells. 391 | */ 392 | 393 | table { 394 | border-collapse: collapse; 395 | border-spacing: 0; 396 | } --------------------------------------------------------------------------------