1

Problem

I want to style the default upload buttons. How should I go about doing this?

3

Since this was a bit of pain to work through I thought I'd post a solution for everyone.

The base of the solution comes from this post:

http://geniuscarrier.com/how-to-style-a-html-file-upload-button-in-pure-css/

There are is another solution to CSS only styling for upload buttons, but it involves wrapping the input element in a label, which is difficult with how Drupal handles the markup.

1. Add CSS

First we add the following two chunks to our CSS file, you can read through the explanation at the post above basically we're making the file input button transparent and styling the area behind it.

Then when you try to click on the area behind it, you actually click on the invisible input button.

.form-type-managed-file {
  position: relative;
  overflow: hidden;
}

input.form-file {
  position: absolute;
    top: 25px;
    right: 0;
    margin: 0;
    padding: 0;
    font-size: 20px;
    cursor: pointer;
    opacity: 0;
    filter: alpha(opacity=0);
}

Now you may have noticed that we want to style the area behind it. To that we're going to need to add an HTML element to the managed file output which we can then style:

2. Change HTML output

Copy the image file function into our template file (for this example we'll be changing the image upload style) and then add two divs after <div class="image-widget-data">

function MY_THEME_image_widget($variables) {
  $element = $variables['element'];
  $output = '';
  $output .= '<div class="image-widget form-managed-file clearfix">';

  if (isset($element['preview'])) {
    $output .= '<div class="image-preview">';
    $output .= drupal_render($element['preview']);
    $output .= '</div>';
  }

  $output .= '<div class="image-widget-data"><div class="bold file-button">Choose File</div>
  <div class="file-name file-button margin_10_top_bot">No File Selected</div>';
  if ($element['fid']['#value'] != 0) {
    $element['filename']['#markup'] .= ' <span class="file-size">(' . format_size($element['#file']->filesize) . ')</span> ';
  }
  $output .= drupal_render_children($element);
  $output .= '</div>';
  $output .= '</div>';

  return $output;
}

If you don't care about the filename of the uploaded file remove the second div and you're done. (You'll need to alter the CSS to make the invisible button sit above your added upload div.)

However if you want the name of the file then leave it in and we move onto a little JS.

3. Add JS

It's impossible the get the name of the file without JS so we add a quick JS script to get it to run, first we call it in the template file:

function MY_THEME_preprocess_page (&$vars) {
    drupal_add_js('sites/all/themes/MY_THEME/js/file_upload.js', 'file');
}

Then we add the following script:

(function ($) {
  Drupal.behaviors.uploadfunctionAddClass = {
    attach: function (context, settings) {
      $(":file").change(function(){
        $(this).prev().text($(this).val().replace(/C:\\fakepath\\/i, ''));
      });
    }
  };
})(jQuery);

It looks for any changes to elements of type file, which in the output for a drupal managed file field is one.

Grabs the element directly before it (the second div we added in step 2) and changes the text in it to it's new value. The path will have C:\fakepath\ added to it, so we remove that as well.

The reason we use Drupal Behaviors rather than document ready, is because we need the script to apply after an AJAX call. If someone uploads a picture, removes it and adds a different one then the script won't have applied to the new elements because they've been added with AJAX. The Drupal Behaviors wrapper causes them to be called every time.

Congrats we're all done.

| improve this answer | |
  • Great solution - only one problem - if you cancel the file upload, you get left without a button anymore - is there a way to get the Upload File text back in this scenario? – Simo Jul 11 '16 at 12:45
2

Hey You can do this as i have did on my site like this:-

1.Like i have file field

        $form['title']=array(
          '#title'=>'file',
          '#type'=>'file',
          '#attributes'=>array('class'=>(array('file_button'))),
        );
$form['markup']=array(
'#type'=>'markup',
'#markup'=>'<span class="file_span" onclick="click_file()">File</span>'
)


2.in css do this 
.file_button{visibility:hidden;width:1px;}
.file_span{backgrounf:#4e0056;color:#ffffff;padding:4px 16px}


3. in javascript add this function
 function click_file()
    {
        jQuery('.file_button').click();
    }

and get value like

$_FILES['files']['name']['title']

it contains file value.

Hope this will work for you.

| improve this answer | |
  • Cheers, this also a nice way of doing it if you only have a couple forms. I wanted it to be site wide, so I ended up changing the widget rather than the form. – Dominic Woodman Jul 21 '14 at 23:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.