안녕하세요. IT여행자입니다.
이번 여행지는 JavaScript를 사용하여 file 태그에 의해 선택된 이미지를 미리 보는 작업을 해볼까 합니다. HTML5가 이제 보편적으로 사용되면서 하나의 file 태그에 의해 하나 이상의 파일들을 한 번에 선택할 수 있게 되었습니다. 아직 몇몇 파일 다운로드 API 중에는 file 태그의 multiple 속성을 사용할 수 없는 것도 있지만 버전업이 되지 않는다면 시장에서 도태되겠지요~
여담이지만, 우리나라도 이제 것 IE에 종속적이었기 때문에 web 개발 능력이 한없이 IT 선진국에 비해 뒤쳐져있는 건 사실일 것입니다. 아니, 정확히 이야기하면 신기술을 사용하지 못하고 그저 IE의 특성만을 답습하다 보니 우리도 모르게 세계 시장에서 도태된 것일 겁니다. 사설 끊고~ 자, 이제 시작해 봅시다.
[시연 동영상]
[요구조건]
- 하나의 file 태그를 사용하여 이미지를 다중 선택할 수 있어야 한다.
- 선택되어 있는 이미지는 삭제될 수 있어야 한다.
- 기존 file 태그를 사용하여 이미지를 추가할 수 있어야 한다.
- 파일 탐색기 등을 통해 파일을 드래그 앤 드롭해도 이미지가 추가되어야 한다.
위와 같은 요구조건에 맞게 JavaScript와 HTML5, CSS를 사용하여 작업해 보도록 하겠습니다.
1. HTML과 CSS를 사용한 레이아웃
간단하게 레이아웃을 작성하였고, CSS를 사용하여 placeholder 기능만 추가하였습니다.
[HTML]
<div id='image_preview'>
<h3>이미지 미리보기 [ IT여행자 ]</h3>
<input type='file' id='btnAtt' multiple='multiple'/>
<div id='att_zone'
data-placeholder='파일을 첨부 하려면 파일 선택 버튼을 클릭하거나 파일을 드래그앤드롭 하세요'></div>
</div>
[CSS]
#att_zone{
width: 660px;
min-height:150px;
padding:10px;
border:1px dotted #00f;
}
#att_zone:empty:before{
content : attr(data-placeholder);
color : #999;
font-size:.9em;
}
[구현 화면]
2. JavaScript 부분
2.1 자동 실행 함수를 사용하여 만들겠습니다.
첫 번째 매개변수인 att_zone은 첨부된 이미지들이 표시될 영역을 나타내는 id값이고, 두 번째 매개변수는 file 태그의 id값을 의미합니다.
( /* att_zone : 이미지들이 들어갈 위치 id, btn : file tag id */
imageView = function imageView(att_zone, btn){
}
)('att_zone', 'btnAtt')
2.2 함수내에서 사용될 변수들 선언
함수의 매개변수로 전달된 id값을 사용하여 각각 attZone, btnAtt 객체를 생성한 뒤, 첨부된 파일의 목록을 저장할 sel_files 변수를 배열로 지정해 둡니다.
( /* att_zone : 이미지들이 들어갈 위치 id, btn : file tag id */
imageView = function imageView(att_zone, btn){
var attZone = document.getElementById(att_zone);
var btnAtt = document.getElementById(btn)
var sel_files = [];
...
}
)('att_zone', 'btnAtt')
2.2 첨부 이미지 영역의 스타일
첨부된 이미지들은 아래의 그림과 같이 relative 속성을 갖는 div안에 버튼과 함께 배치될 것입니다. 이를 위해 CSS부분을 자바 스크립트 안에 임베드시키기 위해 스타일 코드를 작성해 둡니다.
...
// 이미지와 체크 박스를 감싸고 있는 div 속성
var div_style = 'display:inline-block;position:relative;'
+ 'width:150px;height:120px;margin:5px;border:1px solid #00f;z-index:1';
// 미리보기 이미지 속성
var img_style = 'width:100%;height:100%;z-index:none';
// 이미지안에 표시되는 체크박스의 속성
var chk_style = 'width:30px;height:30px;position:absolute;font-size:24px;'
+ 'right:0px;bottom:0px;z-index:999;background-color:rgba(255,255,255,0.1);color:#f00';
...
2.3 file 태그 상태가 바뀌었을 때(파일을 선택했을때)
multiple 속성의 file 태그로부터 n개의 이미지를 전달받아 각각의 이미지를 읽어 화면에 표시해 주는 imageLoader() 함수를 호출합니다.
<script>
...
btnAtt.onchange = function(e){
var files = e.target.files;
var fileArr = Array.prototype.slice.call(files)
for(f of fileArr){
imageLoader(f);
}
}
...
</script>
2.4. 파일이 드롭되었을 때
탐색기 등에서 파일이 드롭되었을 때 처리되는 부분입니다. 이 부분이 없으면 브라우저에 이미지가 드롭되면 브라우저가 이미지 정보를 인식할 수 있기 때문에 그냥 브라우저에 표시되고 맙니다.
<script>
...
// 탐색기에서 드래그앤 드롭 사용
attZone.addEventListener('dragenter', function(e){
e.preventDefault();
e.stopPropagation();
}, false)
attZone.addEventListener('dragover', function(e){
e.preventDefault();
e.stopPropagation();
}, false)
attZone.addEventListener('drop', function(e){
var files = {};
e.preventDefault();
e.stopPropagation();
var dt = e.dataTransfer;
files = dt.files;
for(f of files){
imageLoader(f);
}
}, false)
...
</script>
2.5 이미지 미리 보는 imageLoader() 함수
file 태그에 의해 전달된 파일 하나하나를 sel_files에 추가한 후 이미지와 버튼을 추가하여 하나의 div를 만들어 반환하는 함수 makeDiv() 함수를 호출하여 그 결과를 att_zone에 추가한다.
<script>
...
/*첨부된 이미리즐을 배열에 넣고 미리보기 */
imageLoader = function(file){
sel_files.push(file);
var reader = new FileReader();
reader.onload = function(ee){
let img = document.createElement('img')
img.setAttribute('style', img_style)
img.src = ee.target.result;
attZone.appendChild(makeDiv(img, file));
}
reader.readAsDataURL(file);
}
...
</script>
2.6. 선택된 파일을 삭제할 때
추가할 div 태그를 생성하고, 전달받은 이미지를 div에 추가합니다. 또한 전달받은 파일 정보를 사용하여 삭제 기능을 하는 button 태그를 만들어 div에 붙입니다. 또한 만들어진 삭제 버튼이 클릭되면 div와 sel_files에서 관련 정보를 삭제하는 코드를 입력합니다.
<script>
...
/*첨부된 파일이 있는 경우 checkbox와 함께 attZone에 추가할 div를 만들어 반환 */
makeDiv = function(img, file){
var div = document.createElement('div')
div.setAttribute('style', div_style)
var btn = document.createElement('input')
btn.setAttribute('type', 'button')
btn.setAttribute('value', 'x')
btn.setAttribute('delFile', file.name);
btn.setAttribute('style', chk_style);
btn.onclick = function(ev){
var ele = ev.srcElement;
var delFile = ele.getAttribute('delFile');
for(var i=0 ;i<sel_files.length; i++){
if(delFile== sel_files[i].name){
sel_files.splice(i, 1);
}
}
dt = new DataTransfer();
for(f in sel_files) {
var file = sel_files[f];
dt.items.add(file);
}
btnAtt.files = dt.files;
var p = ele.parentNode;
attZone.removeChild(p)
}
div.appendChild(img)
div.appendChild(btn)
return div
}
...
</script>
[전체 코드] HTML+CSS+JavaScript
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>image preview [ it여행자 ]</title>
<style>
#att_zone {
width: 660px;
min-height: 150px;
padding: 10px;
border: 1px dotted #00f;
}
#att_zone:empty:before {
content: attr(data-placeholder);
color: #999;
font-size: .9em;
}
</style>
</head>
<body>
<div id='image_preview'>
<h3>이미지 미리보기 [ IT여행자 ]</h3>
<input type='file' id='btnAtt' multiple='multiple' />
<div id='att_zone'
data-placeholder='파일을 첨부 하려면 파일 선택 버튼을 클릭하거나 파일을 드래그앤드롭 하세요'></div>
</div>
<script>
( /* att_zone : 이미지들이 들어갈 위치 id, btn : file tag id */
imageView = function imageView(att_zone, btn){
var attZone = document.getElementById(att_zone);
var btnAtt = document.getElementById(btn)
var sel_files = [];
// 이미지와 체크 박스를 감싸고 있는 div 속성
var div_style = 'display:inline-block;position:relative;'
+ 'width:150px;height:120px;margin:5px;border:1px solid #00f;z-index:1';
// 미리보기 이미지 속성
var img_style = 'width:100%;height:100%;z-index:none';
// 이미지안에 표시되는 체크박스의 속성
var chk_style = 'width:30px;height:30px;position:absolute;font-size:24px;'
+ 'right:0px;bottom:0px;z-index:999;background-color:rgba(255,255,255,0.1);color:#f00';
btnAtt.onchange = function(e){
var files = e.target.files;
var fileArr = Array.prototype.slice.call(files)
for(f of fileArr){
imageLoader(f);
}
}
// 탐색기에서 드래그앤 드롭 사용
attZone.addEventListener('dragenter', function(e){
e.preventDefault();
e.stopPropagation();
}, false)
attZone.addEventListener('dragover', function(e){
e.preventDefault();
e.stopPropagation();
}, false)
attZone.addEventListener('drop', function(e){
var files = {};
e.preventDefault();
e.stopPropagation();
var dt = e.dataTransfer;
files = dt.files;
for(f of files){
imageLoader(f);
}
}, false)
/*첨부된 이미리즐을 배열에 넣고 미리보기 */
imageLoader = function(file){
sel_files.push(file);
var reader = new FileReader();
reader.onload = function(ee){
let img = document.createElement('img')
img.setAttribute('style', img_style)
img.src = ee.target.result;
attZone.appendChild(makeDiv(img, file));
}
reader.readAsDataURL(file);
}
/*첨부된 파일이 있는 경우 checkbox와 함께 attZone에 추가할 div를 만들어 반환 */
makeDiv = function(img, file){
var div = document.createElement('div')
div.setAttribute('style', div_style)
var btn = document.createElement('input')
btn.setAttribute('type', 'button')
btn.setAttribute('value', 'x')
btn.setAttribute('delFile', file.name);
btn.setAttribute('style', chk_style);
btn.onclick = function(ev){
var ele = ev.srcElement;
var delFile = ele.getAttribute('delFile');
for(var i=0 ;i<sel_files.length; i++){
if(delFile== sel_files[i].name){
sel_files.splice(i, 1);
}
}
dt = new DataTransfer();
for(f in sel_files) {
var file = sel_files[f];
dt.items.add(file);
}
btnAtt.files = dt.files;
var p = ele.parentNode;
attZone.removeChild(p)
}
div.appendChild(img)
div.appendChild(btn)
return div
}
}
)('att_zone', 'btnAtt')
</script>
</body>
</html>
이상으로 IT 여행자였습니다.
'작은 모듈(IT구슬)' 카테고리의 다른 글
Firefox cache 비활성화 (0) | 2021.05.27 |
---|---|
USB나 이동 디스크에 MySQL Server 설치하기 (0) | 2021.05.26 |
암호 입력을 위한 모달 박스 만들기 (0) | 2021.01.06 |
이클립스 코드 자동 완성기능 (0) | 2020.12.28 |
다음 API를 사용한 우편번호 검색 (0) | 2020.11.01 |