One of the quirks of html is that while we can do wonderful stuff with css to style all the fields and buttons in our forms, for browser security reasons the file input field used to select a file for uploading is annoyingly resistant to styling.
This quirk has long frustrated me with my Front End File Uploader addon, so while developing a v2.0 upgrade I decided to do something about it.
There have been several work-arounds published on various web sites, all based on an original concept by Michael McGrady. Unfortunately the original link no longer exists, but there are many second and third generation postings if you Google for "Styling File Input Fields".
The concept is based on creating a button styled as you want, then using absolute positioning and z-indexes to place it directly underneath a file input field. You then style the file input field to be completely transparent. So the file input field is there and clickable, but everyone can see through it to the dummy field below.
The code here is a simplified outline of the technique applied within my block edit dialog. Anyone coding this should know enough to add t() functions, wrap it in a form and flesh out the details.
On the page
First, the html and php:
<div class="upload_file_wrapper">
<span class="upload_file_styling_input">
<?php
echo $form->file('upload_file');
?>
</span>
<span class="upload_file_styling_button">
<?php
$ih = Loader::helper('concrete/interface');
echo $ih->button('My own upload label', '#', null, null);
?>
<span class="upload_file_repeater">
<?php
echo 'no file chosen';
?>
</span>
</span>
</div>
Because even adding styles like transparency and positioning to a file input field can be resisted by browsers, both the file input field and my button are wrapped in span elements that I use for the actual styling and positioning. The span upload_file_repeater is used to display a repeat of the value selected.
Styles
The corresponding css is:
.upload_file_wrapper{
position:relative;
height:40px;
}
.upload_file_styling_input{
position:absolute;
left:10px;
top:10px;
opacity:0;
filter:alpha(opacity:0);
cursor:pointer;
}
.upload_file_styling_button{
position:absolute;
left:10px;
top:10px;
z-index:-1;
cursor:pointer;
}
Even then, I had some trouble getting the styling wrapping the file input field to accept a positive z-index, so got round this by moving the button back a layer with a negative z-index.
jQuery
The final component is some jQuery to copy values from the file input into the repeater.
$('.upload_file_wrapper').on('change', 'input[name="upload_file"]', function(){
var ulfn = $(this).val();
repeater = $(this).closest('.upload_file_wrapper').find('.upload_file_repeater').text(ulfn)
});
Again, this is vastly simplified because my actual code also cope with in-form validation and the case of replacing the empty input with the default text.
Browser Compatibility
Another part of the solution not detailed in this note is browser detection and fallback.
As far as I can tell, the above works for all modern browsers, but not for IE9 or less. Anyone surprised by that can go straight to the back of the classroom!
You may have noticed in the css above that an IE specific filter rule is included. Some of the sources I referenced included such a rule. So my guess is that the trick may have worked on some much older versions of IE.
In my addon I used IE conditional comments to just present a regular file input for all old Internet Explorer versions. To provide further flexibility, I also left a hook for an override of the IE version decision to be coded into a site config constant.
If it all goes wrong?
Perhaps the above trick runs into a browser that does not cooperate and is not handled by the IE exception. In this case the file input field is on top and still usable. Beneath it could be a messy background of the styled button.