├── .gitignore ├── LICENSE ├── README.md ├── bootstrap.file-input.js ├── bower.json ├── demo-style.css ├── demo.html ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Bootstrap File Input 2 | Copyright 2013 Gregory Pike 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Check out the demo of Bootstrap file input at ~~http://gregpike.net/demos/bootstrap-file-input/demo.html~~ 2 | -------------------------------------------------------------------------------- /bootstrap.file-input.js: -------------------------------------------------------------------------------- 1 | /* 2 | Bootstrap - File Input 3 | ====================== 4 | 5 | This is meant to convert all file input tags into a set of elements that displays consistently in all browsers. 6 | 7 | Converts all 8 | 9 | into Bootstrap buttons 10 | Browse 11 | 12 | */ 13 | (function($) { 14 | 15 | $.fn.bootstrapFileInput = function() { 16 | 17 | this.each(function(i,elem){ 18 | 19 | var $elem = $(elem); 20 | 21 | // Add [processed] class to avoid double processing of input file element 22 | if (typeof $elem.attr('data-bfi-processed-class') != 'undefined') { 23 | // Check if the element already has the [processed] flag on it and skip it if it does 24 | if ($elem.hasClass($elem.attr('data-bfi-processed-class'))) { 25 | return; 26 | } 27 | $elem.addClass($elem.attr('data-bfi-processed-class')); 28 | } 29 | 30 | // Maybe some fields don't need to be standardized. 31 | if (typeof $elem.attr('data-bfi-disabled') != 'undefined') { 32 | return; 33 | } 34 | 35 | // Set the word to be displayed on the button 36 | var buttonWord = 'Browse'; 37 | 38 | if (typeof $elem.attr('title') != 'undefined') { 39 | buttonWord = $elem.attr('title'); 40 | } 41 | 42 | var className = ''; 43 | 44 | if (!!$elem.attr('class')) { 45 | className = ' ' + $elem.attr('class'); 46 | } 47 | 48 | // Now we're going to wrap that input field with a Bootstrap button. 49 | // The input will actually still be there, it will just be float above and transparent (done with the CSS). 50 | $elem.wrap('').parent().prepend($('').html(buttonWord)); 51 | }) 52 | 53 | // After we have found all of the file inputs let's apply a listener for tracking the mouse movement. 54 | // This is important because the in order to give the illusion that this is a button in FF we actually need to move the button from the file input under the cursor. Ugh. 55 | .promise().done( function(){ 56 | 57 | // As the cursor moves over our new Bootstrap button we need to adjust the position of the invisible file input Browse button to be under the cursor. 58 | // This gives us the pointer cursor that FF denies us 59 | $('.file-input-wrapper').on('mousemove', function(cursor) { 60 | 61 | var input, wrapper, 62 | wrapperX, wrapperY, 63 | inputWidth, inputHeight, 64 | cursorX, cursorY; 65 | 66 | // This wrapper element (the button surround this file input) 67 | wrapper = $(this); 68 | // The invisible file input element 69 | input = wrapper.find("input"); 70 | // The left-most position of the wrapper 71 | wrapperX = wrapper.offset().left; 72 | // The top-most position of the wrapper 73 | wrapperY = wrapper.offset().top; 74 | // The with of the browsers input field 75 | inputWidth= input.width(); 76 | // The height of the browsers input field 77 | inputHeight= input.height(); 78 | //The position of the cursor in the wrapper 79 | cursorX = cursor.pageX; 80 | cursorY = cursor.pageY; 81 | 82 | //The positions we are to move the invisible file input 83 | // The 20 at the end is an arbitrary number of pixels that we can shift the input such that cursor is not pointing at the end of the Browse button but somewhere nearer the middle 84 | moveInputX = cursorX - wrapperX - inputWidth + 20; 85 | // Slides the invisible input Browse button to be positioned middle under the cursor 86 | moveInputY = cursorY- wrapperY - (inputHeight/2); 87 | 88 | // Apply the positioning styles to actually move the invisible file input 89 | input.css({ 90 | left:moveInputX, 91 | top:moveInputY 92 | }); 93 | }); 94 | 95 | $('body').on('change', '.file-input-wrapper input[type=file]', function(){ 96 | 97 | var fileName; 98 | fileName = $(this).val(); 99 | 100 | // Remove any previous file names 101 | $(this).parent().next('.file-input-name').remove(); 102 | if (!!$(this).prop('files') && $(this).prop('files').length > 1) { 103 | var filesLabel = $(this).data('files-label'); 104 | if (!filesLabel) { 105 | filesLabel = 'files'; 106 | } 107 | fileName = $(this)[0].files.length+' '+filesLabel; 108 | } 109 | else { 110 | fileName = fileName.substring(fileName.lastIndexOf('\\') + 1, fileName.length); 111 | } 112 | 113 | // Don't try to show the name if there is none 114 | if (!fileName) { 115 | return; 116 | } 117 | 118 | var selectedFileNamePlacement = $(this).data('filename-placement'); 119 | if (selectedFileNamePlacement === 'inside') { 120 | // Print the fileName inside 121 | $(this).siblings('span').html(fileName); 122 | $(this).attr('title', fileName); 123 | } else { 124 | // Print the fileName aside (right after the the button) 125 | $(this).parent().after(''+fileName+''); 126 | } 127 | }); 128 | 129 | }); 130 | 131 | }; 132 | 133 | // Add the styles before the first stylesheet 134 | // This ensures they can be easily overridden with developer styles 135 | var cssHtml = ''; 140 | $('link[rel=stylesheet]').eq(0).before(cssHtml); 141 | 142 | })(jQuery); 143 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grevory/bootstrap-file-input", 3 | "version": "1.0.0", 4 | "description": "Standardizes the file input field to look like a Bootstrap button in all browsers", 5 | "license": "https://raw.githubusercontent.com/grevory/bootstrap-file-input/master/LICENSE", 6 | "ignore": [ 7 | "demo-*", 8 | "**/*.md" 9 | ], 10 | "main": ["./bootstrap.file-input.js"], 11 | "dependencies": { 12 | "jquery": "~1.7.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /demo-style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 10px; 3 | } 4 | 5 | body .navbar .brand { 6 | color: #FFFFFF; 7 | } 8 | 9 | .hero-unit h1 { 10 | margin: 0 0 10px 0; 11 | } 12 | 13 | .hero-unit .file-input-wrapper { 14 | margin: 0 0 10px 0; 15 | } 16 | 17 | pre.prettyprint { 18 | padding: 10px; 19 | margin-top: 10px; 20 | } -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
File input fields look differently in all browsers. It's a pain in the arse to design something that looks nice in all browsers and it sucks that support for this is not available in Twitter Bootstrap.
42 |This jQuery plugin is designed to make all file input fields look like standard Twitter Bootstrap buttons
43 | 44 |If you use Twitter Bootstrap then you know how awesome it is for styling a website with minimal effort.
49 | 50 |jQuery
http://jquery.com/Twitter Bootstrap
http://twitter.github.com/bootstrap/Bootstrap File Input
bootstrap.file-input.js67 | $('input[type=file]').bootstrapFileInput(); 68 | $('.file-inputs').bootstrapFileInput(); 69 |70 | 71 |
76 | <!-- Change the wording using a title tag --> 77 | <input type="file" title="Search for a file to add">78 | 79 | 80 | 81 |
82 | <!-- Disable the styling --> 83 | <input type="file" data-bfi-disabled>84 | 85 | 86 | 87 |
88 | <!-- Display filename inside the button instead of its label --> 89 | <input type="file" data-filename-placement="inside">90 | 91 |
Tested in Chrome, Firefox, Opera, and even Internet Explorer 8.
94 | 95 |Attributes used on the file input elements. At the moment all file inputs at the time of load have use of these attributes.
98 | 99 |Attribute | 103 |Type | 104 |Action | 105 |
---|---|---|
title |
110 | String | 111 |The text that will appear in the button | 112 |
data-bfi-disabled |
115 | No value required | 116 |When this attributes exists it prevents the file input from being styled | 117 |
data-filename-placement |
120 | String ['outside', 'inside'] Default: 'outside' |
121 | Defines where the selected file name will be displayed :
122 |
|
127 |