Skip to content

Instantly share code, notes, and snippets.

@bluebeel
Created February 23, 2019 14:33
Show Gist options
  • Save bluebeel/4ee444330a1ac3becb52868547bd7b75 to your computer and use it in GitHub Desktop.
Save bluebeel/4ee444330a1ac3becb52868547bd7b75 to your computer and use it in GitHub Desktop.
Upload Form
<section class="upload-file">
<h1 class="heading">Upload files</h1>
<p class="paragraph">You can upload multiple files.</p>
<div class="files hidden" data-files></div>
<form class="form" enctype="multipart/formdata">
<div class="dropzone" data-dropzone>
<div class="dropzone__field">
<label class="dropzone__label" for="files" data-dropzone-label>Upload files</label>
<input class="dropzone__file" id="files" name="files" type="file" multiple data-dropzone-file>
</div>
<button class="dropzone__upload button" name="uploadFile" type="submit">Upload files</button>
</div>
<button class="button" name="continue" type="submit">Continue</button>
</form>
</section>
function isDragAndDropSupported() {
return typeof document.createElement('div').ondrop != 'undefined';
}
function isFormDataSupported() {
return typeof FormData == 'function';
}
function isFileApiSupported() {
const input = document.createElement('input');
input.type = 'file';
return typeof input.files != 'undefined';
};
if(isDragAndDropSupported() && isFormDataSupported() && isFileApiSupported()) {
var Dropzone = function(container) {
this.dropzone = container;
this.dropzone.classList.add('dropzone--actual');
this.dropzone.querySelector('[data-dropzone-label]').classList.add('button');
this.setupDropzone();
this.setupFileInput();
this.setupStatusBox();
this.setupFiles();
this.setupFileRemove();
};
Dropzone.prototype.setupDropzone = function() {
this.dropzone.addEventListener('dragover', this.onDragOver.bind(this));
this.dropzone.addEventListener('dragleave', this.onDragLeave.bind(this));
this.dropzone.addEventListener('drop', this.onDrop.bind(this));
};
Dropzone.prototype.onDragOver = function(event) {
event.preventDefault();
this.dropzone.classList.add('dropzone--dragover');
};
Dropzone.prototype.onDragLeave = function() {
this.dropzone.classList.remove('dropzone--dragover');
};
Dropzone.prototype.onDrop = function(event) {
event.preventDefault();
this.dropzone.classList.remove('dropzone--dragover');
this.files.classList.remove('hidden');
this.status.innerHTML = 'Uploading files, please wait...';
this.uploadFiles(event.dataTransfer.files);
};
Dropzone.prototype.setupFileInput = function() {
this.fileInput = document.querySelector('[data-dropzone-file]');
this.fileInput.addEventListener('change', this.onFileChange.bind(this));
this.fileInput.addEventListener('focus', this.onFileFocus.bind(this));
this.fileInput.addEventListener('blur', this.onFileBlur.bind(this));
};
Dropzone.prototype.onFileChange = function(event) {
this.files.classList.remove('hidden');
this.status.innerHTML = 'Uploading files, please wait...';
this.uploadFiles(event.currentTarget.files);
};
Dropzone.prototype.onFileFocus = function() {
this.dropzone.querySelector('[data-dropzone-label]').classList.add('dropzone__label--focused');
};
Dropzone.prototype.onFileBlur = function() {
this.dropzone.querySelector('[data-dropzone-label]').classList.remove('dropzone__label--focused');
};
Dropzone.prototype.setupStatusBox = function() {
this.status = document.createElement('div');
this.status.className = 'visually-hidden';
this.status.setAttribute('role', 'status');
this.status.setAttribute('aria-live', 'polite');
this.dropzone.appendChild(this.status);
};
Dropzone.prototype.setupFiles = function() {
this.filesHeading = document.createElement('h2');
this.filesHeading.className = 'heading';
this.filesHeading.innerHTML = 'Files';
this.file = document.createElement('ul');
this.file.className = 'file';
this.files = document.querySelector('[data-files]');
this.files.appendChild(this.filesHeading);
this.files.appendChild(this.file);
};
Dropzone.prototype.setupFileRemove = function() {
document.querySelector('[data-files]').addEventListener('click', this.onFileRemoveClick.bind(this));
};
Dropzone.prototype.onFileRemoveClick = function(event) {
const eventTarget = event.target;
if(eventTarget.hasAttribute('data-file-remove')) {
const listItem = eventTarget.parentNode;
listItem.parentNode.removeChild(listItem);
}
};
Dropzone.prototype.getStatusHtml = function(result, isSuccess) {
this.fileName = document.createElement('span');
this.fileStatus = document.createElement('span');
this.fileStatus.className = 'file__status file__status--error';
this.fileStatus.innerHTML = 'Error';
if(isSuccess) {
this.fileLink = document.createElement('a');
this.fileLink.className = 'anchor';
this.fileLink.setAttribute('href', '#');
this.fileLink.setAttribute('target', '_blank');
this.fileLink.innerHTML = result.name;
this.fileName = document.createElement('div');
this.fileName.appendChild(this.fileLink);
this.fileStatus.className = 'file__status file__status--success';
this.fileStatus.innerHTML = 'Success';
} else
this.fileName.innerHTML = result.name;
this.fileName.className = 'file__name';
this.fileRemove = document.createElement('button');
this.fileRemove.className = 'file__remove button';
this.fileRemove.setAttribute('type', 'button');
this.fileRemove.setAttribute('data-file-remove', '');
this.fileRemove.innerHTML = 'Remove';
this.fileItem = document.createElement('li');
this.fileItem.className = 'file__item';
this.fileItem.appendChild(this.fileName);
this.fileItem.appendChild(this.fileStatus);
this.fileItem.appendChild(this.fileRemove);
return this.fileItem;
};
Dropzone.prototype.uploadFiles = function(files) {
for(const file of files)
this.uploadFile(file);
};
Dropzone.prototype.uploadFile = function(file) {
const formData = new FormData();
formData.append('documents', file);
this.file.appendChild(this.getStatusHtml(file, true));
};
}
if(typeof Dropzone != 'undefined')
new Dropzone(document.querySelector('[data-dropzone]'));
body {
margin: 1rem;
font-family: 'Arial', sans-serif;
}
:focus {
outline: 0;
box-shadow: 0 0 .0625rem .25rem #5e9ed6;
}
.hidden {
display: none;
}
.visually-hidden {
position: absolute !important;
overflow: hidden !important;
width: .0625rem !important;
height: .0625rem !important;
padding: 0 !important;
border: 0 !important;
margin: -.0625rem !important;
clip: rect(0 0 0 0) !important;
}
.anchor {
border-radius: .125rem;
color: #000;
}
.button {
display: block;
padding: .375rem .75rem;
border: none;
border-radius: .125rem;
background-color: #000;
color: #fff;
font-family: inherit;
font-size: inherit;
text-decoration: none;
cursor: pointer;
}
.upload-file {
max-width: 736px;
}
.file {
padding-left: 0;
list-style: none;
&__item {
display: flex;
align-items: center;
padding: .5rem;
border-radius: .125rem;
margin-bottom: .0625rem;
background-color: #f0f0f0;
}
&__name {
flex-basis: 40%;
overflow: hidden;
padding: .3125rem;
margin-right: .5rem;
text-overflow: ellipsis;
white-space: nowrap;
}
&__status {
flex-basis: 40%;
margin-right: .5rem;
&--success {
color: #008000;
}
&--error {
color: #800000;
}
}
&__remove {
flex-basis: 20%;
}
}
.dropzone {
$this: &;
margin-bottom: 1rem;
&--actual {
display: flex;
justify-content: center;
align-items: center;
padding: 3rem;
border: .125rem dashed #000;
border-radius: .125rem;
#{$this}__label {
margin-bottom: 0;
&::after {
content: '';
}
&--focused {
outline: 0;
box-shadow: 0 0 .0625rem .25rem #5e9ed6;
}
}
#{$this}__file {
position: absolute;
left: -9999em;
}
#{$this}__upload {
display: none;
}
}
&--dragover {
background-color: #ddd;
}
&__label {
display: inline-block;
margin-bottom: .5rem;
&::after {
content: ':';
}
}
&__file {
display: block;
padding: .375rem .75rem;
border: .0625rem solid #000;
border-radius: .125rem;
font-family: inherit;
font-size: inherit;
}
&__upload {
margin-top: .5rem;
}
}

Upload Form

An upload form suitable for Web Accessibility standards. It also provides a fallback solution.

A Pen by Ahmet Kurt on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment