이번 포스팅에서는 드래그 앤 드롭과 파일 업로드 버튼 두가지를 동시에 이용할 수 있는 파일 업로드 UI를 작성해보려고 합니다.
파일 업로드는 <input type="file" /> 태그로 사용해야 하는데, 이 태그는 우리가 원하는 스타일이 적용되지 않습니다. 따라서 다들 속임수를 써서 디자인을 하고 있습니다.
우선, input 태그와 label 태그를 같이 사용하면 label에 특별한 이벤트가 발생합니다. 예를들어, 타입이 라디오 버튼(type="radio")이나 체크박스(type="checkbox")의 경우에는 label 태그의 단어만 클릭해도 선택이 됩니다.
이러한 특별한 이벤트가 파일업로드(type="file")인 경우에는 label을 선택 시, 파일 업로드 창이 뜨는 형태로 발생합니다.
그래서 우리는 기존의 input 태그를 숨기고, label 태그를 디자인 하여서 버튼을 만들 수 있습니다.
<label class="file-label" for="chooseFile">Choose File</label>
<input class="file" id="chooseFile"
type="file"
onchange="dropFile.handleFiles(this.files)"
accept="image/png, image/jpeg, image/gif"
>
.file-label {
margin-top: 30px;
background-color: #5b975b;
color: #fff;
text-align: center;
padding: 10px 0;
width: 65%;
border-radius: 6px;
cursor: pointer;
}
.file {
display: none;
}
위에있는 그림과 같이, 파일을 드래그해서 업로드를 할 수 있도록 도와주는 상자를 만드는 것은 단순히 div 만을 활용해도 문제가 없습니다. 드래그한 파일이 저 상자 안에서 놓였을 때, 무언가 동작을 할 수 있는 drop 이라는 이벤트를 웹에서 제공해 주고 있기 때문입니다!
<div class="upload-box">
<div id="drop-file" class="drag-file">
<img src="https://img.icons8.com/pastel-glyph/2x/image-file.png" alt="파일 아이콘" class="image">
<p class="message">Drag files to upload</p>
<img src="" alt="미리보기 이미지" class="preview">
</div>
</div>
.upload-box {
width: 100%;
box-sizing: border-box;
margin-right: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.upload-box .drag-file {
position: relative;
width: 100%;
height: 360px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 3px dashed #dbdbdb;
}
.upload-box .drag-file.highlight {
border: 3px dashed red;
}
.upload-box .drag-file .image {
width: 40px;
}
.upload-box .drag-file .message {
margin-bottom: 0;
}
.upload-box .drag-file .preview {
display: none;
position: absolute;
left: 0;
height: 0;
width: 100%;
height: 100%;
}
브라우저에서 드랍 관련한 기본 이벤트가 존재하기 때문에, 기존의 이벤트를 다 막아준 다음에 시작해야 정상적으로 동작합니다. 막은 다음에는 업로드 된 파일을 출력하는 형태까지만 만들어 보겠습니다.
preventDefault 함수는 이벤트의 추가 전파를 막습니다. 예를들어 자신을 둘러싸고있는 태그에 이벤트가 적용되어 있다고 한다면, 그 이벤트를 나까지는 오게 하지 말아주세요! 라는 의미 입니다.
stopPropagation 함수는 브라우저가 해당 이벤트에 대해 수행하는 기본적인 작업을 막습니다. 예를들어, 파일을 내려놓는 경우에 파일의 내용을 새 탭에서 보여주는 크롬 브라우저의 동작을 막을 때는 이 함수가 적절합니다.
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
파일을 드래그 해서 상자 안으로 가져갔을 때 표시를 해주기 위한 이벤트 입니다. 파일이 상자 안으로 들어가면 highlight 함수를, 파일이 상자 밖으로 벗어나면 unhighlight 함수를 사용하여 스타일은 변경해 줍니다.
const dropArea = document.getElementById("drop-file");
function highlight(e) {
preventDefaults(e);
dropArea.classList.add("highlight");
}
function unhighlight(e) {
preventDefaults(e);
dropArea.classList.remove("highlight");
}
dropArea.addEventListener("dragenter", highlight, false);
dropArea.addEventListener("dragover", highlight, false);
dropArea.addEventListener("dragleave", unhighlight, false);
상자안에 파일을 드래그해서 내려놓았을 때 drop 이벤트가 발생하는데, 그 이벤트 객체에서 files 라는 파일 배열을 가져올 수 있습니다. 이 배열은 선택한 여러개의 파일 데이터를 가지고 있습니다.
function handleDrop(e) {
unhighlight(e);
let dt = e.dataTransfer;
let files = dt.files;
console.log(files);
// addToFileList
// ...
}
파일이 선택이 되면, 파일의 데이터를 가지고 이미지를 만들어 주는 함수(renderFile)를 사용하여 업로드 칸을 이미지로 변경할 수 있어야 합니다.
function renderFile(file) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function () {
let img = dropArea.getElementsByClassName("preview")[0];
img.src = reader.result;
img.style.display = "block";
};
}
See the Pen Pure javascript imageupload ui by 홍지성 (@lnbvocxe) on CodePen.
[많이쓰는 UI] 파일 업로드 (0) | 2021.07.12 |
---|---|
[많이쓰는 UI] 댓글 (0) | 2021.07.12 |
[많이쓰는 UI] 탭 뷰 (0) | 2021.07.12 |
[많이쓰는 UI] 로딩 화면 (0) | 2021.07.12 |
[많이쓰는 UI] 모달 팝업 (0) | 2021.07.12 |