37

I would like to style <input type="file" /> using CSS3.

Alternatively, I would like user to press on a div (that I will style) and this will open the Browse window.

Is that possible to do that using HTML, CSS3, and Javascript / jQuery only ?

Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746
  • 1
    Yes, of course, but I didn't found a good enough solution. – Misha Moroshko Jul 12 '10 at 06:46
  • Check this great trick: http://stackoverflow.com/q/6638434/809356 – Sophivorus Nov 23 '11 at 15:43
  • 2
    Just use [this method](http://stackoverflow.com/a/21842275/1256925). It's really the simplest one, and works perfectly in all browsers, without requiring any JavaScript at all. – Joeytje50 Feb 18 '14 at 00:59
  • If you don't want to have to directly deal with the style-related tricks described here, are ok with using Polymer/web components, and don't need support for IE9 or older, check out a custom element I created: `` at https://github.com/garstasio/file-input. It gives file inputs a bunch of other features as well. – Ray Nicholus Jun 30 '14 at 04:27

12 Answers12

53

I have this rough example that you might want to get some idea...

html​

<div id="file">Chose file</div>
<input type="file" name="file" />​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

CSS

#file {
    display:none;
}​

jQuery

var wrapper = $('<div/>').css({height:0,width:0,'overflow':'hidden'});
var fileInput = $(':file').wrap(wrapper);

fileInput.change(function(){
    $this = $(this);
    $('#file').text($this.val());
})

$('#file').click(function(){
    fileInput.click();
}).show();

demo

​​​​​​​​​​​​​​

Reigel Gallarde
  • 64,198
  • 21
  • 121
  • 139
  • This could be a great solution, but it does not work in Firefox ! `fileInput.click();` does not open the dialog box. – Misha Moroshko Jul 12 '10 at 06:47
  • I think the .trigger('click') event should work in all browsers. – Benbob Jul 23 '10 at 02:15
  • @Keyo, even that won't work on Firefox... it's because of how Firefox implemented File input boxes.... – Reigel Gallarde Jul 23 '10 at 03:38
  • 3
    In IE when you click submit this will blank out the selected file and cancel the submit (security feature?) because the user didn't actually do the clicking on the file input, any way to get around this? FF4 works fine though. – invertedSpear Apr 22 '11 at 00:56
  • Thanks, it works very good on my tests, in FF too. re FF issues, on what version do you have issues? – Vasil Popov Jan 30 '13 at 16:52
  • 1
    For multiple files, same idea. Tested on ff only. http://jsfiddle.net/2ezhd/ – fotanus Mar 12 '13 at 02:27
  • you have to use visibility: hidden instead of display: none. Can you imagine its quise insecure otherwise and thats why some browsers will not get it working as it is – chesscov77 Mar 15 '13 at 03:28
  • 1
    @Juan "rough example"... – Reigel Gallarde Mar 15 '13 at 03:44
  • `$('#file').click(fileInput.trigger.bind(fileInput,'click')).show();` – 000 Jun 25 '14 at 17:50
  • You could achieve the same in a fastest way just using CSS3. Have a look to my Pen : http://codepen.io/Despertaweb/pen/MawOyO – Despertaweb Sep 08 '15 at 11:00
  • @Despertaweb at the time of writing, there was a bug on firefox that self-closed elements can't have pseudo element 'before' and 'after'.. And I think it's still not fixed... try opening your demo on firefox.... – Reigel Gallarde Sep 08 '15 at 15:33
  • 1
    @Reigel you're rightm I thought I checked it out long time ago.... Thanx! – Despertaweb Sep 10 '15 at 09:36
19

After checking Reigels idea, and this one, I wrote this simple solution to the common problem of styling a type="file" input field (tested it on Firefox, Safari and Chrome).

<div style="position:relative;">
<div id="file" style="position:absolute;">Click here to select a file</div>
<input type="file" name="file" style="opacity:0; z-index:1;" onchange="document.getElementById('file').innerHTML = this.value;">
</div>

Then you can of course style the "file" div as you want.

And if you want to use a type="text" input instead of a div, simply change innerHTML for value:

<div style="position:relative;">
<input type="text" id="file" style="position:absolute;" placeholder="Click here to select a file">
<input type="file" name="file" style="opacity:0; z-index:1;" onchange="document.getElementById('file').value = this.value;">
</div>

Here is my original answer using jQuery:

<div style="position:relative;">
<div id="file" style="position:absolute;">Click here to select a file</div>
<input type="file" name="file" style="opacity:0; z-index:1;" onchange="$('#file').text($(this).val());">
</div>
Sophivorus
  • 3,008
  • 3
  • 33
  • 43
  • 1
    The downside of this technique is that the file input is still rendered as a clickable target (which you can't see). If you style the "file" div such that it is smaller than the hidden , you've left transparent click targets hanging around it or other form elements. As long as you style your div to be larger than any of the browsers' native file elements it works fine. – user85461 May 21 '12 at 15:16
  • Set width and height of the file select element to 100% of its parent container's area, and it should be sized appropriately no matter how the browser renders the native gui element. At least, this worked for me in the latest FF, Chrome, Safari and IE9/10. – jerseyboy Jun 27 '13 at 12:01
6

I made a custom style for this as well. Check it out

JS Fiddle Demo - Custom Input type="file"

HTML

<input type="file" id="test">
<div class="button-group"> 
<a href="#" id="browse" class="button primary">Browse</a>
<a href="#" id="save" class="button">Save</a>
<a href="#" id="clear" class="button danger">Clear</a>
</div>
<input type="text" id="testfile"></input>

CSS

body {
padding:100px;
}
input[type="file"] {
position:absolute;
display:none;
}
#testfile {
height: 26px;
text-decoration: none;
background-color: #eee;
border:1px solid #ccc;
border-radius:3px;
float:left;
margin-right:5px;
overflow:hidden;
text-overflow:ellipsis;
color:#aaa;
text-indent:5px;
}
#actionbtnBrowse, #actionbtnSave {
margin:0 !important;
width:60px;
}

JQuery

$("#browse").click(function () {
$("#test").click();
})

$("#save").click(function () {
alert('Run a save function');
})

$("#clear").click(function () {
$('#testfile').val('');
})

$('#test').change(function () {
$('#testfile').val($(this).val());
})

Also add to external resources tab:

https://github.com/necolas/css3-github-buttons/blob/master/gh-buttons.css

andibra
  • 153
  • 4
  • 15
6

Here is how to do it using HTML, CSS and Javascript (without any frameworks):

The idea is to have the <input type='file'> button hidden and use a dummy <div> that you style as a file upload button. On click of this <div>, we call the hidden <input type='file'>.

Demo:

// comments inline

document.getElementById("customButton").addEventListener("click", function(){
  document.getElementById("fileUpload").click();  // trigger the click of actual file upload button
});

document.getElementById("fileUpload").addEventListener("change", function(){
  var fullPath = document.getElementById('fileUpload').value;
  var fileName = fullPath.split(/(\\|\/)/g).pop();  // fetch the file name
  document.getElementById("fileName").innerHTML = fileName;  // display the file name
}, false);
body{
  font-family: Arial;
}

#fileUpload{
  display: none; /* do not display the actual file upload button */
}

#customButton{  /* style the dummy upload button */
  background: yellow;
  border: 1px solid red;
  border-radius: 5px;
  padding: 5px;
  display: inline-block;
  cursor: pointer;
  color: red;
}
<input type="file" id="fileUpload"> <!-- actual file upload button -->
<div id="customButton">Browse</div>  <!-- dummy file upload button which can be used for styling ;) -->
<span id="fileName"></span> <!-- the file name of the selected file will be shown here -->
Rahul Desai
  • 15,242
  • 19
  • 83
  • 138
  • 1
    Awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome, awesome. – Rasmus Sep 14 '15 at 18:39
5

The fake div is not needed! No Js no extra html. Using only css is possible.

The best way is using the pseudo element :after or :before as an element overt the de input. Then style that pseudo element as you wish. I recomend you to do as a general style for all input files as follows:

input[type="file"]:before {
  content: 'Browse';
  background: #FFF;
  width: 100%;
  height: 35px;
  display: block;
  text-align: left;
  position: relative;
  margin: 0;
  margin: 0 5px;
  left: -6px;
  border: 1px solid #E0E0E0;
  top: -1px;
  line-height: 35px;
  color: #B6B6B6;
  padding-left: 5px;
  display: block;
}

--> DEMO

Rahul Desai
  • 15,242
  • 19
  • 83
  • 138
Despertaweb
  • 1,672
  • 21
  • 29
4

In addition of Reigel,

here is more simpler implementation. You can use this solution on multiple file input fields, too. Hope this helps some people ;-)

HTML (single input)

<input type="file" name="file" />

HTML (multiple input)

<!-- div is important to separate correctly or work with jQuery's .closest() -->
<div>
  <input type="file" name="file[]" />
</div>

<div>
  <input type="file" name="file[]" />
</div>

<div>
  <input type="file" name="file[]" />
</div>

JavaScript

// make all input fields with type 'file' invisible
$(':file').css({
  'visibility': 'hidden',
  'display': 'none'
});

// add a textbox after *each* file input
$(':file').after('<input type="text" readonly="readonly" value="" class="fileChooserText" /> <input type="button" value="Choose file ..." class="fileChooserButton" />');

// add *click* event to *each* pseudo file button
// to link the click to the *closest* original file input
$('.fileChooserButton').click(function() {
    $(this).parent().find(':file').click();
}).show();

// add *change* event to *each* file input
// to copy the name of the file in the read-only text input field
$(':file').change(function() {
    $(this).parent().find('.fileChooserText').val($(this).val());
});
Patrick Hillert
  • 2,309
  • 4
  • 22
  • 37
2

Here's an example that I'm using that utilizes jQuery, I've tested against Firefox 11, and Chrome 18, as well as IE9. So its pretty compatible with browsers in my book, though i only work with those three.

HTML

Here's a basic "Customizable" HTML structure.

<span>
    File to Upload<br />
    <label class="smallInput" style="float:left;">
        <input type="file" name="file" class="smallInput" />
    </label>
    <input type="button" class="upload" value="Upload" style="float:left;margin-top:6px;margin-left:10px;" />
</span>

CSS

Here's a sample of my CSS

label.smallInput {
    background:url(images/bg_s_input.gif) no-repeat;
    width:168px;
}

JavaScript

This is the heavy lifter.

/* File upload magic form?? */
$("input.smallInput[type=file]").each(function(i){
    var id      = "__d_file_upload_"+i;
    var d_wrap  = $('<div/>').attr('id',id).css({'position':'relative','cursor':'text'});

    $(this).wrap(d_wrap).bind('change blur focus keyup click',function(){
        $("#"+id+" input[type=text]").val($(this).val());
    }).css({'opacity':0,'zIndex':9999,'position':'absolute'}).removeClass('smallInput');

    obj = $(this);

    $("#"+id).append($("<input/>").addClass('smallInput').attr('type','text').css({'zIndex':9998,'position':'absolute'}).bind('click',function(e){obj.trigger('click');$(this).blur();}));
    obj.closest('span').children('input.upload[type=button]').bind('click',function(e){
        obj.trigger('click');
        $(this).blur();
    });
});
/* ************************ */

Explanation

The HTML is pretty straight forward, just a simple element, i include the button so it can be named independently from the rest, sure this could be included in the JavaScript, but simply put, I'm a bit on the lazy side. The code searches for all inputs with a class of smallInput that have the type of file this allows you to define default HTML and fallback form structure in case a browser decides to be a pain.

This method only uses JavaScript to ensure delivery, it does not alter any browser behaviors in regards to the file input.

You can modify the HTML and JavaScript to make it very robust, this code suffices my current project so i doubt I'll be making any changes to it.

Caveats

  • Different browsers treat the value of the file input differently, which in chrome results in c:\fakeroot\ on windows machines.
  • Uses anonymous functions, (for lack of a better word) which means if you have too many file inputs you can cause the browser to behave slowly on processing.
Code Maverick
  • 20,171
  • 12
  • 62
  • 114
Destreyf
  • 21
  • 1
2

Ran into the same issue today, but it seems there's an easy way to have your own styles - hide the input, and style the associated label:

<div class="upload">
  <label for="my-input"> Upload stuff </label>
  <input type="file" id="my-input" name="files[]" />
</div>

CSS:

.upload input{
  display: none;
}

.upload label{
  background: DarkSlateBlue;
  color: white;
  padding: 5px 10px;
}

Works in latest Chrome, Firefox and IE 10. Didn't test others

nice ass
  • 16,471
  • 7
  • 50
  • 89
2

While Reigel's answer conveys the idea, it doesn't really have any style attached to it. I came across this problem recently and despite the plethora of answers on Stack Overflow, none really seemed to fit the bill. In the end, I ended up customizing this so as to have a simple and an elegant solution.

I have also tested this on Firefox, IE (11, 10 & 9), Chrome and Opera, iPad and a few android devices.

Here's the JSFiddle link -> http://jsfiddle.net/umhva747/

$('input[type=file]').change(function(e) {
    $in = $(this);
    $in.next().html($in.val());
    
});

$('.uploadButton').click(function() {
    var fileName = $("#fileUpload").val();
    if (fileName) {
        alert(fileName + " can be uploaded.");
    }
    else {
        alert("Please select a file to upload");
    }
});
body {
    background-color:Black;
}

div.upload {
    background-color:#fff;
    border: 1px solid #ddd;
    border-radius:5px;
    display:inline-block;
    height: 30px;
    padding:3px 40px 3px 3px;
    position:relative;
    width: auto;
}

div.upload:hover {
    opacity:0.95;
}

div.upload input[type="file"] {
    display: input-block;
    width: 100%;
    height: 30px;
    opacity: 0;
    cursor:pointer;
    position:absolute;
    left:0;
}
.uploadButton {
    background-color: #425F9C;
    border: none;
    border-radius: 3px;
    color: #FFF;
    cursor:pointer;
    display: inline-block;
    height: 30px;
    margin-right:15px;
    width: auto;
    padding:0 20px;
    box-sizing: content-box;
}

.fileName {
    font-family: Arial;
    font-size:14px;
}

.upload + .uploadButton {
    height:38px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form action="" method="post" enctype="multipart/form-data">
    <div class="upload">
        <input type="button" class="uploadButton" value="Browse" />
        <input type="file" name="upload" accept="image/*" id="fileUpload" />
        <span class="fileName">Select file..</span>
    </div>
    <input type="button" class="uploadButton" value="Upload File" />
</form>

Hope this helps!!!

Satwik Nadkarny
  • 5,086
  • 2
  • 23
  • 41
1

This is my method if i got your point

HTML

<form action="upload.php">
    <input type="file" id="FileInput" style="cursor: pointer;  display: none"/>
    <input type="submit" id="Up" style="display: none;" />
</form>

jQuery

<script type="text/javascript">
    $( "#FileInput" ).change(function() {
      $( "#Up" ).click();
    });
</script>
SystemX
  • 481
  • 4
  • 10
1

Here is a solution with a text field where the user types in the (relative) pathname of the file copy on the server (if authorized) and a submit button to browse the local system for a file and send the form:

<form enctype="multipart/form-data" action="" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
<p><input type="file" name="upload_file" id="upload_file" size="40"/></p>
<p><input type="text" id="upload_filename" name="upload_filename" size="30" maxlength="100" value="<?php echo htmlspecialchars($filename, ENT_COMPAT, 'UTF-8'); ?>"/>
<input type="submit" class="submit submit_upload" id="upload_upload" name="upload_upload" value="Upload"/></p>
</form>

The scripting part hides the file input, clicks it if the user clicks on the submit button, submits the form if the user has picked up a file. If the user tries to upload a file without entering a filename, the focus is first moved to the text field for the filename.

<script type="text/javascript">
var file=$('#upload_file');
var filename=$('#upload_filename');
var upload=$('#upload_upload');

file.hide().change(function() {if (file.val()) {upload.unbind('click').click();}});

upload.click(function(event) {event.preventDefault();if (!filename.val()) {filename.focus();} else {file.click();}});
</script>

Simply style the submit button for a perfect result:

.submit {padding:0;margin:0;border:none;vertical-align:middle;text-indent:-1000em;cursor:pointer;}
.submit_upload {width:100px;height:30px;background:transparent url(../images/theme/upload.png) no-repeat;}
frasq
  • 71
  • 3
-4

When you retreive the value of an input field, browser will return a fake path (literally C:\fakepath[filename] in Chrome). So I would add the following to the Javascript solutions:

val=$('#file').val(); //File field value

val=val.replace('/','\\'); //Haven't tested it on Unix, but convert / to \ just in case
val=val.substring(val.lastIndexOf('\\')+1);

$('#textbox').val(val);

Ofc, it could be done in a single line.

Gabriel
  • 2,170
  • 1
  • 17
  • 20