HTML components

by Sam Fereday

HTML

<div class="styled-upload default-theme">
  <input type="file" name="file1" id="file1" class="inputFile" data-multiple-caption="{count} files selected" multiple />
  <label for="file1" class="fileLabel">
    <span class="output">No file chosen</span>
    <span class="button"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" viewBox="0 0 20 17"><path d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"></path></svg>Choose a file</span>
  </label>
</div>

<div class="styled-upload dark-theme">
  <input type="file" name="file2" id="file2" class="inputFile" data-multiple-caption="{count} files selected" multiple />
  <label for="file2" class="fileLabel">
    <span class="output">No file chosen</span>
    <span class="button"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" viewBox="0 0 20 17"><path d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"></path></svg>Choose a file</span>
  </label>
</div>

SCSS

// Required
.styled-upload {
  float: left;
  margin-bottom: 1em;
  .inputFile {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1;
  }
  .inputFile + label {
    display: inline-block;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    vertical-align: top;
    cursor: pointer;
    /* "hand" cursor */
  }
  .inputFile + label * {
    pointer-events: none;
  }
}

// Theme
.theme-defaults {
  font-family: arial;
  .inputFile + label {
    color: white;
    background: #fff;
    border: 1px solid #d3394c;
    font-size: 1.25em;
    font-weight: 700;
    transition: all 0.2s ease;
  }
  .inputFile:focus + label,
  .inputFile + label:hover {
    background-color: #eee;
  }
  .inputFile:focus + label {
    outline: 1px dotted #000;
    outline: -webkit-focus-ring-color auto 5px;
  }
  .inputFile + label .output {
    display: block;
    float: left;
    min-width: 10em;
    padding: 0.45em 0.6em 0.45em 0.4em;
    color: #333;
  }
  .inputFile + label .button {
    display: block;
    float: left;
    padding: 0.45em 0.6em 0.45em 0.4em;
    color: #f1e5e6;
    background-color: #d3394c;
  }
  .fileLabel svg {
    width: 1em;
    height: 1em;
    vertical-align: middle;
    fill: #fff;
    margin-top: -0.25em;
    margin-right: 0.25em;
  }
}

.styled-upload.default-theme {
  @extend .theme-defaults;
}

.styled-upload.dark-theme {
  @extend .theme-defaults;
  svg {
    display: none;
  }
  .inputFile + label {
    background-color: transparent;
    border: 0;
  }
  .inputFile + label .button,
  .inputFile:focus + label,
  .inputFile + label:hover {
    background-color: transparent;
  }
  .inputFile + label .button {
    padding: 0.6em 0.9em;
    background: #222;
    border-radius: 32px;
    font-weight: 500;
    font-size: 0.9em;
  }
  .inputFile + label .output {
    float: right;
  }
}

JavaScript

class Uploader {

  constructor() {
    this.inputFile = null;
    this.fileLabel = null;
    this.labelVal = '';
  }

  attach(container) {

    this.inputFile = container.querySelector('.inputFile');
    this.fileLabel = container.querySelector('.fileLabel');
    this.labelVal = this.fileLabel.innerHTML;

    this.inputFile.addEventListener('change', this.onChange.bind(this));

  }

  onChange(e) {

    let fileName = '';

    if (this.files && this.files.length > 1)
      fileName = (this.getAttribute('data-multiple-caption') || '').replace('{count}', this.files.length);
    else
      fileName = e.target.value.split('\\').pop();

    if (fileName)
      this.fileLabel.querySelector('.output').innerHTML = fileName;
    else
      this.fileLabel.innerHTML = this.labelVal;

  }

}

var inputs = document.querySelectorAll('.styled-upload');

for (let i = 0; i < inputs.length; i++) {
  new Uploader().attach(inputs[i]);
}