Tab custom file upload button

2.6k Views Asked by At

I've created my own custom file upload button by creating a label and hiding the input element with css. This all works fine, but the problem is I can't use the button by tabbing and enter. I tried adding tabindex=0 to the label. I could then tab to the element ok but there was no action when clicking enter as it is only a label.

Here is the HTML code

<label class="custom-file-upload>
<input type="file">
Choose file
</label>

and css to hide the default file upload button

input[type="file"] {
    display: none;
}

Any help is much appreciated.

4

There are 4 best solutions below

1
KKGupta On

You can rewrite your code something in this manner

<input id="file-upload" type="file">
<label for="file-upload" class="custom-file-upload>Choose file</label>

CSS for input and change css for label accordingly using sibling selector

input[type="file"] {
    opacity: 0;
}
0
KKGupta On

With labels you need to trigger click on keyboard events for enter

<label for ="file-upload1" tabindex="0" class="custom-file-upload">
<input type="file" id="file-upload"/> Choose file
</label> 

CSS

input[type="file"]{
  display:none;
} 

JQuery Code

$('.custom-file-upload').on('keyup',function(event){
  if (event.keyCode === 13) {
    $('#file-upload').trigger('click');
  }
})
0
GrahamTheDev On

This can all be achieved with CSS on modern browsers with the added benefit that:

  • there is no reliance on JavaScript
  • there is no need for a tabindex
  • it activates with space or enter, both of which are expected behaviours.

The key is to make the input visually hidden (using the .visually-hidden class) but still focusable and then use the + to link the label (without wrapping the input in the label).

The key part of the example below is [type="file"]:focus + label

This lets you change the label styling when the input is selected (it is important that the <label> appears immediately after the <input> in the DOM in order for this to work).

I also included the syntax for applying a ::before styling on hover and focus for completeness.

Example (not a production ready solution)

The example given below was a quick and dirty way of demonstrating how to achieve your goal, it has a couple of accessibility issues that need addressing before putting it into production:-

  1. you shouldn't use the same styling for hover and focus -> hover you should change colour and show the icon, focus add a border and show the icon
  2. instead of using a font for the icon you should use an SVG as fonts may break if someone overrides them (i.e. if they have a preferred font due to dyslexia).
  3. make sure that you disable animation of the icon entering if people have indicated they prefer reduced movement by using the prefers-reduced-motion: reduce media query https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion

Also make sure you associate the label with the input using for="inputName" on the label and id="inputName" on the input.

.visually-hidden { 
    position: absolute !important;
    height: 1px; 
    width: 1px;
    overflow: hidden;
    clip: rect(1px, 1px, 1px, 1px);
    white-space: nowrap;
}
[type="file"] + label {
  background: #f15d22;
  border: none;
  border-radius: 5px;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-size: inherit;
  font-weight: 600;
  margin-bottom: 1rem;
  outline: none;
  padding: 1rem 50px;
  position: relative;
  transition: all 0.3s;
  vertical-align: middle;
  background-color: #99c793;
  border-radius: 50px;
  overflow: hidden;
}
[type="file"] + label::before {
  color: #fff;
  content: "\f382";
  font-family: "Font Awesome 5 Pro";
  font-size: 100%;
  height: 100%;
  right: 130%;
  line-height: 3.3;
  position: absolute;
  top: 0px;
  transition: all 0.3s;
}
[type="file"]:hover + label,
[type="file"]:focus + label{
  background-color: #497f42;
}
[type="file"]:hover + label::before, 
[type="file"]:focus + label::before {
  right: 75%;
}
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.2.0/css/all.css" />
<input type="file" id="inputName" class="visually-hidden"/>
<label for="inputName">upload</label>

1
Kevin W. On

I may have a little hack that can help. You can use the focus-within attribute on your parent element that holds your file input and style it with this

parent:focus-within {
  border: 1px solid blue;
  background: yellow;
}