There are few things that are more frustrating to deal with in CSS than file inputs. You have very limited control (if any) of what you can style, even in today’s modern browsers. Designers often feel disappointed in the lack of options available for its visual appearance.
While working away on a top secret project, it was crucial to maintain consistent branding and styling throughout the site, which included custom file uploads. During our first phase, we didn’t have time to implement Flash - which would’ve been the more robust choice - so it was good ole CSS and JavaScript (with a hefty dollop of the Prototype framework) to the rescue.
As it turns out, it was a less daunting task than it seemed, thanks to people like PPK and Shaun Inman laying the ground work for us. Basically, I frankensteined the two methods together.
Here is the demo, which mimic’s Shaun Inman’s: stylized file input.
The only difference in the HTML markup from Sean’s is I’ve wrapped the file input in a div, which I did because the input must be wrapped in a block element and I am lazy, so it’s one less CSS property I had to write!
<div class="file">
<input type="file" />
</div>
Here are the simple CSS rules:
.solidhex-stylized div.file {
background: url('browse.png') no-repeat 0 0;
width: 128px;
height: 38px;
overflow: hidden;
cursor: pointer;
position: relative;
}
.solidhex-stylized div.file input {
opacity: 0;
-moz-opacity: 0;
filter:alpha(opacity=0);
height: 100%;
width: auto;
display: block;
cursor: pointer;
position: relative;
}
The real difference is in the JavaScript, since I am making hefty use of my favorite library - Prototype. Here is the code followed by explanation:
(function() {
function stylizeInput (el) {
if (!el) return;
var input = el.down('input[type=file]');
if (input) {
el.observe("mousemove", function(event) {
input.setStyle({
left: (event.pointerX() - this.positionedOffset()['left']) - (input.getWidth() - 30) + 'px'
});
}).wrap('div', {'class' : 'solidhex-stylized'});
}
}
function init () {
$$('div.file').each(stylizeInput);
}
document.observe("dom:loaded", init)
})();
The basic principles are the same. Search through the document to see if there are any divs with the class name ‘file.’ If there are, grab the file input from the matched div(s), and register a mousemove event to to move the browse button depending where the user has her mouse inside the ‘file’ div. Then, we wrap the entire ‘file’ div inside another div with the class name ’solidhex-stylized’ so all the styles necessary for the trick to work are applied.
I also wrapped it another div for one more reason: I needed to write the file name next to the button. This can be done pretty easily with more help from Prototype. Here’s an example: stylized file input with file status.
Keep in mind the latter sample is a quick ‘n dirty demo; the file updating bit could be less redundant I’m sure. You’ll also probably wont to manipulate the string value so it doesn’t get too long. The reason the first demo only has the button is that I have no way of knowing what kind of design restrictions that other people are working with, so you’ll have to tweak as needed if you’d like to use it.
For now, it’s pretty concise and works without JS or CSS being enabled, so that’s a win-win situation. It also appears to be working fine in the following browsers: IE6+7, Safari 3 and FF3.
Let me know if you find it useful! Like I said, I can’t take too much credit, I stole bits and pieces from Shaun Inman, PPK and my buddy Cedric (although he’ll be unhappy - I ported his tricks from jQuery to Prototype :p)
I realize that’s probably not the best explanation in the world, but just drop me a comment if you have any questions!
Here is the demo in zipped format: demo