반응형

비밀번호등을 받기 위해 숫자나 특정 단어 하나씩 입력하고 다음칸으로 이동하는 형태의 UI를 많이 접할 수 있습니다.

해당 포스팅에서는 하나의 Input태그마다 입력을 받으면 다음칸으로 이동하고 숫자만 받는 형태를 작성해보겠습니다.

 

단순하게 maxlength와 number값 타입을 지정하더라도 영문 'e' 또는 한글 자음 모음등이 입력되는 현상😓을 볼 수 있는데, 정규식과 사용할 수 있는 이벤트등을 활용하여 처리하였습니다.😋

 

좀 더 좋은 깔끔한 방식이 있다면 댓글 부탁드립니다!🙆‍♀️

<input type="tel" maxlength="1" min="0" max="9" onlyNumber>
$(function(){
    $(document).on("keypress keyup keydown", "input[onlyNumber]", function(e){
        console.log(e.which);
        if(/[a-z|ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/g.test(this.value)){ //한글 막기
            e.preventDefault();
            this.value = "";
        }else if (e.which != 8 && e.which != 0 //영문 e막기
            && e.which < 48 || e.which > 57    //숫자키만 받기
            && e.which < 96 || e.which > 105){ //텐키 받기
            e.preventDefault();
            this.value = "";
        }else if (this.value.length >= this.maxLength){ //1자리 이상 입력되면 다음 input으로 이동시키기
            this.value = this.value.slice(0, this.maxLength);
            if($(this).next("input").length > 0){
                $(this).next().focus();
            }else{
                $(this).blur();
            }
        }
    });    
});

태그 input에 type을 tel로 한 이유는 모바일 환경에서 넘버패드로 먼저 동작하도록 하기 위해 처리하였고, input태그 내부에 onlyNumber라는 속성이 있으면 동일하게 동작하도록 처리하였습니다.

 

마지막 else if문에서 길이를 체크하고 maxlength값만큼 커지거나 같아지면 다음 input이 있는지 체크 후 자동으로 포커싱하는 스크립트입니다.

 

 

동작테스트는 아래 codePen⌨을 활용해주세요!

See the Pen Get one number by one by myhappyman (@myhappyman) on CodePen.

 

 

 

 

반응형
반응형

UI를 작성하다보면 input 박스 안에 버튼 이미지를 위치시키고 입력받은 값에 따라 보여주고 사라지고 제어등을 하게 되는 경우가 있다.

 

조건은 아래와 같다.

1. 입력하고자 하는 input에 focus를 잃는 경우(blur 발생) 버튼을 사라지게 하는 이벤트를 추가

2. 입력하는 input에 focus를 얻은 상태에서 입력중이면 버튼이 활성화

3. 활성화된 버튼을 클릭하면 input의 데이터가 다 지워짐

 

 

문제는 click보다 blur가 더 빨리 동작한다는 것이다.

 

stackoverflow의 글 덕분에 꼼수로 간단하게 해결하였다.

https://stackoverflow.com/questions/10652852/jquery-fire-click-before-blur-event

 

jQuery: fire click() before blur() event

I have an input field, where I try to make autocomplete suggestion. Code looks like

 
On input's blur() event I want to...

 

stackoverflow.com

 

 

click대신 mousedown 이벤트를 사용하였다.

 

아래는 실제 동작시킨 스크립트 코드이다.

$('.inp_box').on("mousedown", ".inp_remove_btn", function(){ //click 사용안됨 //blur가 더 빨리 동작함
    $(this).siblings("input").val("");

}).on("change keyup input", "input", function(e){ //입력이 시작되면 삭제버튼 활성화
    $(this).siblings(".inp_remove_btn").addClass("active");

}).on("blur", "input", function(e){ //input focus를 잃으면 삭제버튼 비활성화
    $(this).siblings(".inp_remove_btn").removeClass("active");

}).on("focus", "input", function(e){ //input focus를 얻으면 길이 체크하여 삭제버튼 활성화 유무체크
    if($(this).val().length > 0){
        $(this).siblings(".inp_remove_btn").addClass("active");
    }
});
반응형
반응형

대한민국 권역을 선택 후 지역 특별시나 도를 선택 마지막에 시군구를 선택하는 select박스 예제입니다.

See the Pen Korea Area select Control by myhappyman (@myhappyman) on CodePen.

 

version1.js

관련 js는 아래와 같으며 areaSelectMaker에 제일 부모가 되는 select태그 값을 넣어주면 됩니다.

$(function(){
    areaSelectMaker("select[name=addressRegion]");
});

var areaSelectMaker = function(target){
    if(target == null || $(target).length == 0){
        console.warn("Unkwon Area Tag");
        return;
    }

    var area = {
        "수도권" :{
            "서울특별시" : [ "강남구", "강동구", "강북구", "강서구", "관악구", "광진구", "구로구", "금천구", "노원구", "도봉구", "동대문구", "동작구", "마포구", "서대문구", "서초구", "성동구", "성북구", "송파구", "양천구", "영등포구", "용산구", "은평구", "종로구", "중구", "중랑구" ],
            "경기도" : [ "수원시 장안구", "수원시 권선구", "수원시 팔달구", "수원시 영통구", "성남시 수정구", "성남시 중원구", "성남시 분당구", "의정부시", "안양시 만안구", "안양시 동안구", "부천시", "광명시", "평택시", "동두천시", "안산시 상록구", "안산시 단원구", "고양시 덕양구", "고양시 일산동구",
                "고양시 일산서구", "과천시", "구리시", "남양주시", "오산시", "시흥시", "군포시", "의왕시", "하남시", "용인시 처인구", "용인시 기흥구", "용인시 수지구", "파주시", "이천시", "안성시", "김포시", "화성시", "광주시", "양주시", "포천시", "여주시", "연천군", "가평군",
                "양평군" ],
            "인천광역시" : [ "계양구", "미추홀구", "남동구", "동구", "부평구", "서구", "연수구", "중구", "강화군", "옹진군" ]			
        },
        "강원권" :{
            "강원도" : [ "춘천시", "원주시", "강릉시", "동해시", "태백시", "속초시", "삼척시", "홍천군", "횡성군", "영월군", "평창군", "정선군", "철원군", "화천군", "양구군", "인제군", "고성군", "양양군" ]			
        },
        "충청권" :{
            "충청북도" : [ "청주시 상당구", "청주시 서원구", "청주시 흥덕구", "청주시 청원구", "충주시", "제천시", "보은군", "옥천군", "영동군", "증평군", "진천군", "괴산군", "음성군", "단양군" ],
            "충청남도" : [ "천안시 동남구", "천안시 서북구", "공주시", "보령시", "아산시", "서산시", "논산시", "계룡시", "당진시", "금산군", "부여군", "서천군", "청양군", "홍성군", "예산군", "태안군" ],
            "대전광역시" : [ "대덕구", "동구", "서구", "유성구", "중구" ],
            "세종특별자치시" : [ "세종특별자치시" ]			
        },
        "전라권" :{
            "전라북도" : [ "전주시 완산구", "전주시 덕진구", "군산시", "익산시", "정읍시", "남원시", "김제시", "완주군", "진안군", "무주군", "장수군", "임실군", "순창군", "고창군", "부안군" ],
            "전라남도" : [ "목포시", "여수시", "순천시", "나주시", "광양시", "담양군", "곡성군", "구례군", "고흥군", "보성군", "화순군", "장흥군", "강진군", "해남군", "영암군", "무안군", "함평군", "영광군", "장성군", "완도군", "진도군", "신안군" ],
            "광주광역시" : [ "광산구", "남구", "동구", "북구", "서구" ]			
        },
        "경상권" : {
            "경상북도" : [ "포항시 남구", "포항시 북구", "경주시", "김천시", "안동시", "구미시", "영주시", "영천시", "상주시", "문경시", "경산시", "군위군", "의성군", "청송군", "영양군", "영덕군", "청도군", "고령군", "성주군", "칠곡군", "예천군", "봉화군", "울진군", "울릉군" ],
            "경상남도" : [ "창원시 의창구", "창원시 성산구", "창원시 마산합포구", "창원시 마산회원구", "창원시 진해구", "진주시", "통영시", "사천시", "김해시", "밀양시", "거제시", "양산시", "의령군", "함안군", "창녕군", "고성군", "남해군", "하동군", "산청군", "함양군", "거창군", "합천군" ],
            "부산광역시" : [ "강서구", "금정구", "남구", "동구", "동래구", "부산진구", "북구", "사상구", "사하구", "서구", "수영구", "연제구", "영도구", "중구", "해운대구", "기장군" ],
            "대구광역시" : [ "남구", "달서구", "동구", "북구", "서구", "수성구", "중구", "달성군" ],
            "울산광역시" : [ "남구", "동구", "북구", "중구", "울주군" ]			
        },
        "제주권" : {
            "제주특별자치도" : [ "서귀포시", "제주시" ]			
        }
    };

    for(i=0; i<$(target).length; i++){
        (function(z){
            var a1 = $(target).eq(z);
            var a2 = a1.next();
            var a3 = a2.next();

            //초기화
            init(a1, true);

            //권역 기본 생성
            var areaKeys1 = Object.keys(area);
            areaKeys1.forEach(function(Region){
                a1.append("<option value="+Region+">"+Region+"</option>");
            });

            //변경 이벤트
            $(a1).on("change", function(){
                init($(this), false);
                var Region = $(this).val();
                var keys = Object.keys(area[Region]);
                keys.forEach(function(Do){
                    a2.append("<option value="+Do+">"+Do+"</option>");    
                });
            });

            $(a2).on("change", function(){
                a3.empty().append("<option value=''>선택</option>");
                var Region = a1.val();
                var Do = $(this).val();
                var keys = Object.keys(area[Region][Do]);
                keys.forEach(function(SiGunGu){
                    a3.append("<option value="+area[Region][Do][SiGunGu]+">"+area[Region][Do][SiGunGu]+"</option>");    
                });
            });
        })(i);        

        function init(t, first){
            first ? t.empty().append("<option value=''>선택</option>") : "";
            t.next().empty().append("<option value=''>선택</option>");
            t.next().next().empty().append("<option value=''>선택</option>");
        }
    }
}

 

 

version2.js

2가지 버전으로 만들었는데 아래는 유니크한 id별로 구성하였을 경우 또는, UI상 select 태그가  서로 떨어져 있는 경우 위 소스로 동작하지 않을 경우를 대비하여 만들었습니다.

$(function(){
    areaSelectMaker("#addressRegion1", "#addressDo1", "#addressSiGunGu1");
    areaSelectMaker("#addressRegion2", "#addressDo2", "#addressSiGunGu2");
});

var areaSelectMaker = function(a1, a2, a3){
    if(a1 == null || a2 == null || a3 == null){
        console.warn("Unkwon Area Tag");
        return;
    }

    var area = {
        "수도권" :{
            "서울특별시" : [ "강남구", "강동구", "강북구", "강서구", "관악구", "광진구", "구로구", "금천구", "노원구", "도봉구", "동대문구", "동작구", "마포구", "서대문구", "서초구", "성동구", "성북구", "송파구", "양천구", "영등포구", "용산구", "은평구", "종로구", "중구", "중랑구" ],
            "경기도" : [ "수원시 장안구", "수원시 권선구", "수원시 팔달구", "수원시 영통구", "성남시 수정구", "성남시 중원구", "성남시 분당구", "의정부시", "안양시 만안구", "안양시 동안구", "부천시", "광명시", "평택시", "동두천시", "안산시 상록구", "안산시 단원구", "고양시 덕양구", "고양시 일산동구",
                "고양시 일산서구", "과천시", "구리시", "남양주시", "오산시", "시흥시", "군포시", "의왕시", "하남시", "용인시 처인구", "용인시 기흥구", "용인시 수지구", "파주시", "이천시", "안성시", "김포시", "화성시", "광주시", "양주시", "포천시", "여주시", "연천군", "가평군",
                "양평군" ],
            "인천광역시" : [ "계양구", "미추홀구", "남동구", "동구", "부평구", "서구", "연수구", "중구", "강화군", "옹진군" ]			
        },
        "강원권" :{
            "강원도" : [ "춘천시", "원주시", "강릉시", "동해시", "태백시", "속초시", "삼척시", "홍천군", "횡성군", "영월군", "평창군", "정선군", "철원군", "화천군", "양구군", "인제군", "고성군", "양양군" ]			
        },
        "충청권" :{
            "충청북도" : [ "청주시 상당구", "청주시 서원구", "청주시 흥덕구", "청주시 청원구", "충주시", "제천시", "보은군", "옥천군", "영동군", "증평군", "진천군", "괴산군", "음성군", "단양군" ],
            "충청남도" : [ "천안시 동남구", "천안시 서북구", "공주시", "보령시", "아산시", "서산시", "논산시", "계룡시", "당진시", "금산군", "부여군", "서천군", "청양군", "홍성군", "예산군", "태안군" ],
            "대전광역시" : [ "대덕구", "동구", "서구", "유성구", "중구" ],
            "세종특별자치시" : [ "세종특별자치시" ]			
        },
        "전라권" :{
            "전라북도" : [ "전주시 완산구", "전주시 덕진구", "군산시", "익산시", "정읍시", "남원시", "김제시", "완주군", "진안군", "무주군", "장수군", "임실군", "순창군", "고창군", "부안군" ],
            "전라남도" : [ "목포시", "여수시", "순천시", "나주시", "광양시", "담양군", "곡성군", "구례군", "고흥군", "보성군", "화순군", "장흥군", "강진군", "해남군", "영암군", "무안군", "함평군", "영광군", "장성군", "완도군", "진도군", "신안군" ],
            "광주광역시" : [ "광산구", "남구", "동구", "북구", "서구" ]			
        },
        "경상권" : {
            "경상북도" : [ "포항시 남구", "포항시 북구", "경주시", "김천시", "안동시", "구미시", "영주시", "영천시", "상주시", "문경시", "경산시", "군위군", "의성군", "청송군", "영양군", "영덕군", "청도군", "고령군", "성주군", "칠곡군", "예천군", "봉화군", "울진군", "울릉군" ],
            "경상남도" : [ "창원시 의창구", "창원시 성산구", "창원시 마산합포구", "창원시 마산회원구", "창원시 진해구", "진주시", "통영시", "사천시", "김해시", "밀양시", "거제시", "양산시", "의령군", "함안군", "창녕군", "고성군", "남해군", "하동군", "산청군", "함양군", "거창군", "합천군" ],
            "부산광역시" : [ "강서구", "금정구", "남구", "동구", "동래구", "부산진구", "북구", "사상구", "사하구", "서구", "수영구", "연제구", "영도구", "중구", "해운대구", "기장군" ],
            "대구광역시" : [ "남구", "달서구", "동구", "북구", "서구", "수성구", "중구", "달성군" ],
            "울산광역시" : [ "남구", "동구", "북구", "중구", "울주군" ]			
        },
        "제주권" : {
            "제주특별자치도" : [ "서귀포시", "제주시" ]			
        }
    };

    //초기화
    init(true, true);

    //권역 기본 생성
    var areaKeys1 = Object.keys(area);
    areaKeys1.forEach(function(Region){
        $(a1).append("<option value="+Region+">"+Region+"</option>");
    });

    //변경 이벤트
    $(document).on("change", a1, function(){
        init(false, true);
        var Region = $(this).val();
        var keys = Object.keys(area[Region]);
        keys.forEach(function(Do){
            $(a2).append("<option value="+Do+">"+Do+"</option>");    
        });
    }).on("change", a2, function(){
        init();
        var Region = $(a1).val();
        var Do = $(this).val();
        var keys = Object.keys(area[Region][Do]);
        keys.forEach(function(SiGunGu){
            $(a3).append("<option value="+area[Region][Do][SiGunGu]+">"+area[Region][Do][SiGunGu]+"</option>");    
        });
    });

    function init(first, second){
        first ? $(a1).empty().append("<option value=''>선택</option>") : "";
        second ? $(a2).empty().append("<option value=''>선택</option>") : "";
        $(a3).empty().append("<option value=''>선택</option>");
    }
}
반응형
반응형

파일 업로드 형태의 게시판 등 웹 UI에서 파일을 긁어와서 드롭하였을때 등록하고 ajax를 통해 업로드 하는 예제까지 진행해보겠습니다.(Spring Legacy 기준)

 

Drag And Drop

html, css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<style>
    *{padding:0;margin:0}
    html, body, .wrap{width: 100%;}
    .clear{clear:both;}
    .wrap>.fileBox{padding: 20px;}
    .fileBox input, textarea{width: 100%;}
    .fileBox textarea{resize:none;}
    .fileBox .fileDrop{display: inline-block;width: 700px;height: 75px;border: 1px solid #000;overflow: auto;}
    .fileDrop .fileList .fileName{padding-left: 20px;}
    .fileDrop .fileList .fileSize{padding-right: 20px; float:right;}
</style>
<body>
    <div class="wrap">
        <div class="fileBox">
            <form id="fileForm" name="fileForm" enctype="multipart/form-data" method="post">
                <table>
                    <tr>
                        <td><input type="text" name="title"></td>
                    </tr>
                    <tr>
                        <td><textarea name="contents"></textarea></td>
                    </tr>
                    <tr>
                        <td><div id="fileDrop" class="fileDrop"></div></td>
                    </tr>
                </table>
                <div class="buttonBox">
                    <button type="button" id="save">저장</button>
                </div>
            </form>
        </div>
    </div>
</body>
</html>

js

var fileList = []; //파일 정보를 담아 둘 배열
$(function(){

    //드래그앤드랍
    $("#fileDrop").on("dragenter", function(e){
        e.preventDefault();
        e.stopPropagation();
    }).on("dragover", function(e){
        e.preventDefault();
        e.stopPropagation();
        $(this).css("background-color", "#FFD8D8");
    }).on("dragleave", function(e){
        e.preventDefault();
        e.stopPropagation();
        $(this).css("background-color", "#FFF");
    }).on("drop", function(e){
        e.preventDefault();

        var files = e.originalEvent.dataTransfer.files;
        if(files != null && files != undefined){
            var tag = "";
            for(i=0; i<files.length; i++){
                var f = files[i];
                fileList.push(f);
                var fileName = f.name;
                var fileSize = f.size / 1024 / 1024;
                fileSize = fileSize < 1 ? fileSize.toFixed(3) : fileSize.toFixed(1);
                tag += 
                        "<div class='fileList'>" +
                            "<span class='fileName'>"+fileName+"</span>" +
                            "<span class='fileSize'>"+fileSize+" MB</span>" +
                            "<span class='clear'></span>" +
                        "</div>";
            }
            $(this).append(tag);
        }

        $(this).css("background-color", "#FFF");
    });

    //저장
    $(document).on("click", "#save", function(){
        var formData = new FormData($("#fileForm")[0]);
        if(fileList.length > 0){
            fileList.forEach(function(f){
                formData.append("fileList", f);
            });
        }         

        $.ajax({
            url : "서버 맵핑 URL",
            data : formData,
            type:'POST',
            enctype:'multipart/form-data',
            processData:false,
            contentType:false,
            dataType:'json',
            cache:false,
            success:function(res){
                alert("저장에 성공하셨습니다.");
            },error:function(res){
                alert("오류 발생.\n관리자에게 문의해주세요.");
            }
        });
    });
});

drag관련 이벤트를 처리하고 싶은 개체에 등록하여 enter, over, leave, drop에 따른 처리를 각각 처리하였습니다.

drop하였을때는 파일을 내려놨을때 드래그앤드롭의 동작 중 가지고 있던 데이터의 정보를 확인하기 위해 dataTransfer 파일 정보를 가져옵니다.

 

이후 파일 정보가 담긴 object를 통해 하나씩 확인하여 tag를 생성하고 별도의 파일정보는 배열에 담아두었다가 서버에 전송시 사용합니다.

 

java - Server Controller

@ResponseBody
@RequestMapping(value = { "uploadPath" }, method = RequestMethod.POST, produces = "json/plain;charset=UTF-8")
public int uploadPath(MultipartHttpServletRequest mtfRequest, 
		final HttpServletRequest request, 
		final HttpServletResponse response) {
	int res = 1;
	System.out.println("제목 > " + request.getParameter("title"));
	System.out.println("내용 > " + request.getParameter("contents"));
	if(mtfRequest != null) {
		List<MultipartFile> fileList = mtfRequest.getFiles("fileList");
		for(int i=0; i<fileList.size(); i++) {
			MultipartFile multi = fileList.get(i);
			if(multi == null) {
				return 0;
			}else if(multi.getSize() == 0) {
				return 0;
			}else {
				System.out.println("파일명 : " + multi.getOriginalFilename() + " / 파일 사이즈 : " + multi.getSize());
			}
		}
	}
		
	return res;
}

ajax에서 전달한 데이터를 받을 컨트롤러입니다. 별도의 서비스나 처리는 하지 않았습니다.

전달된 데이터의 파라미터가 출력된 콘솔 결과는 아래와 같습니다.

컨트롤러에 전달된 데이터 정보

 

동작결과

반응형
반응형

체크박스가 포함된 게시판형태의 데이터들을 다루다보면 전체 선택 해제 기능이 자주 들어갑니다.

전체 선택에 따른 하위 체크박스들을 선택하거나 해제하고 전체박스가 선택된 상태에서 하위 체크박스를 해제하면 전체체크박스의 상태값도 해제되도록 변경되는 예제를 진행해보겠습니다.

 

체크박스 전체 선택, 해제 제어하기

allcheckbox.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<body>
    <table>
        <tr>
            <th>
                <input type="checkbox" name="check" class="allcheck">
            </th>
            <th>순번</th>
            <th>제목</th>
        </tr>
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td>1</td>
            <td>제목입니다.</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td>2</td>
            <td>제목입니다.</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td>3</td>
            <td>제목입니다.</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="check"></td>
            <td>4</td>
            <td>제목입니다.</td>
        </tr>
    </table>
</body>
</html>

css

*{margin:0;padding:0;}
table{width:800px;text-align:center;border-collapse:collapse;border-left:1px solid #ddd;border-right: 1px solid #ddd;}
table tr{border-bottom: 1px solid #ddd;}

JS

$(function(){
    $("[type=checkbox][name=check]").on("change", function(){ //0
        var check = $(this).prop("checked"); //1
        //전체 체크
        if($(this).hasClass("allcheck")){ //2
            $("[type=checkbox][name=check]").prop("checked", check);

        //단일 체크
        }else{ //3
            var all = $("[type=checkbox][name=check].allcheck");
            var allcheck = all.prop("checked")
            if(check != allcheck){ //3-1
                var len = $("[type=checkbox][name=check]").not(".allcheck").length; //3-2
                var ckLen = $("[type=checkbox][name=check]:checked").not(".allcheck").length; //3-2
                if(len === ckLen){ //3-3
                    all.prop("checked", true);
                }else{
                    all.prop("checked", false);
                }
            }
        }
    });
});

저는 모든 체크박스의 이름을 check로 두었고 전체선택기능의 체크박스와 일반체크박스를 class에 allcheck라는 이름으로 구분하였습니다.

 

주석번호와 매칭하여 설명을 확인하시면 됩니다.

0. jQuery를 활용하여 페이지가 로드되면 체크박스에 이벤트를 추가합니다.

1. input type이 checkbox이면서 name값이 check인 요소에 change 이벤트가 발생하면 현재 발생한 요소의 checked 값을 받아옵니다.

2. 이벤트가 발생한 요소의 클래스에 allcheck가 존재하면 전체 체크박스로 판단하고 전체 체크를 진행합니다.

(해제가 되었으면 전부다 해제, 선택이면 전부 다 선택 처리이므로 자신의 상태값과 맞춰줍니다.) 

3. 단일 체크일 경우

   3-1. 전체 체크박스의 상태값과 자신의 상태값을 비교합니다. 상태가 다를 경우 확인 작업이 필요합니다.

   3-2. 페이지에 노출된 체크 박스의 개수와 체크된 개수가 같은지 확인을 하기 위해 값을 가져옵니다.

         여기서 .not()메소드를 사용하여 전체 체크박스는 제외하였습니다.

   3-3. 가져온 개수가 서로 같다면 전체가 이미 선택된 것이므로 전체체크박스에도 선택처리를 합니다.

         반대의 경우 전체가 체크된게 아니므로 전체체크박스에 해제처리를 합니다.

 

동작 결과

잘 동작한다!

반응형
반응형

xhr(XMLHttpRequest) 객체는 서버와 데이터를 확인하기 위해 사용됩니다.

 

ajax또한 xhr 규격을 사용하여 동작하고 있고, 다양한 메소드를 통해 요청의 상태값이나 시간, 결과, 진행상태 등을 확인 할 수 있습니다.

 

여기서 xhr의 upload.onprogress 메소드를 사용하여 파일의 업로드 진행상황을 확인하고 UI적으로 페이지 진행상태를 표현 할 수 있습니다.

 

대량의 파일이나 다중으로 여러 파일을 넘길 때 상태를 알 수 있다보니 아무래도 기다리는데, 도움이 될 것 같습니다.

 

바로 예제를 통해 확인해 보겠습니다.

 

xhr.upload.progress

사용법 ajax로 넘길때 xhr메소드를 추가하고 내부에 upload.onprogress메소드를 추가하여 정의하고자 하는 파일 내용을 추가하면 됩니다.

xhr: function(){
	var xhr = $.ajaxSettings.xhr();
	xhr.upload.onprogress = function(e){
		var per = e.loaded * 100 / e.total;
		console.log(per);
	};
	return xhr;
},

 

 

사용예제

fileupload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<style>
	*{margin:0;padding:0}
	.progressContainer{position:relative;width: 450px;padding:20px 10px;border: 1px solid #eee;margin-top: 15px;background:#000;height:20px;}
	.progress{position:absolute;width: calc(100% - 20px);height: 20px;}
	.progressTotal{background: #5D5D5D;border-radius: 10px;}
	.progressNow{width: calc(0% - 20px);background: #FFF;border-radius: 10px;}
	.progressPer{background: transparent; text-align:center;color:#A6A6A6;}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<body>
<h1>
	File Upload Test
</h1>

<P>  The time on the server is ${serverTime}. </P>
	<div style="width: 100%;padding: 25px;">
		<form id="fileForm" action="/upload.do" method="post" enctype="multipart/form-data">
			<input type="file" name="uploadFile" multiple>
			<button type="button" id="btn">전송</button>
		</form>
		<div class="progressContainer">
			<div class="progress progressTotal"></div>
			<div class="progress progressNow"></div>
			<div class="progress progressPer">0 %</div>
		</div>
	</div>
</body>
<script>
	$(function(){	
		$("#btn").on("click", function(){
			console.log("click Time : " + new Date);
			
			var form = $("#fileForm")[0];
			var formData = new FormData(form);
			$.ajax({
				type: "POST",
				enctype: 'multipart/form-data',
				url: "/upload.do",
				data: formData,
				processData: false,
				contentType: false,
				cache: false,
				xhr: function(){
					var xhr = $.ajaxSettings.xhr();
					xhr.upload.onprogress = function(e){
						var per = e.loaded * 100 / e.total;
						progressBar(per);
					};
					return xhr;
				},
				success: function (data) {
					console.log("SUCCESS : ", data);
				},
				error: function (e) {
					console.log("ERROR : ", e);
				}
			});
		});
	});
	
	function progressBar(per){
		if(per > 55){
			$(".progressPer").css("color", "#000");
		}
		per = per.toFixed(1);
		$(".progressPer").text(per+" %");
		$(".progressNow").css("width", "calc(" + per + "% - 20px)");
	}
</script>
</html>

btn이라는 버튼을 클릭하면 file에 존재하는 데이터를 넘기고 xhr메소드에 정의된 onprogress 메소드에 의해 결과 값을 노출 하는 예제입니다.

 

 

결과

극단적으로 퍼센트를 잘 보기위해 3천개의 텍스트파일을 업로드하는 테스트를 진행해봤습니다.

 

좀 더 자세한 xhr에 대해서는 아래 URL을 참조바랍니다.

 

developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest/upload

 

XMLHttpRequest.upload

XMLHttpRequest upload 프로퍼티는 업로드 진행 상황을 모니터링 할 수 있는 XMLHttpRequestUpload 객체를 반환합니다.

developer.mozilla.org

 

반응형
반응형

jQuery-ui를 사용하여 드래그 이벤트와 리사이즈 이벤트를 추가 할 수 있는데, 직사각형의 영역을 서로 침범할 경우 기존 위치로 원위치 시키는 방법을 알아보겠습니다.

 

겹침 처리 방법

1. 일반 2차원 배열에 0으로 채워넣고, 도형이 있는 위치는 1로 채웁니다.

도형이 존재하는 x,y에 넓이 높이 만큼 영역을 1로 채웁니다.

 

2. 도형 위치마다 1로 더하기때문에 겹치는 영역이 생기면 아래처럼 2가 존재하게 됩니다.

겹치는 영역은 2가 된다.

 

3. 각 도형마다의 위치와 넓이, 높이를 기억하고 있다가 겹치면 원위치 시킨다.

 

 

실적용 소스

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        #box {
            width: 500px;
            height: 500px;
            border: 1px solid black;
            background: #eee;
        }

        #box .rect {
            position: absolute;
            width: 50px;
            height: 50px;
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <div id="box">
        <div class="rect" idx=0></div>
        <div class="rect" idx=1></div>
    </div>
    <script src="./draggable_rect.js"></script>
</body>

</html>

 

draggable_rect.js

var rectArr = [];
var canvasArr = [];

$(function () {
    //초기 세팅
    for (i = 0; i < $(".rect").length; i++) {
        var rect = $($(".rect")[i])
        var idx = rect.attr("idx");
        var x = rect.offset().left;
        var y = rect.offset().top;

        var left = rect.css("left");
        var top = rect.css("top");
        var w = rect.width();
        var h = rect.height();
        rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
    }



    var width = $("#box").width();
    var height = $("#box").height();
    for (i = 0; i < height; i++) {
        canvasArr[i] = new Array(width);
    }

    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            canvasArr[i][j] = 0;
        }
    }
});

$(".rect").draggable({
    containment: "parent",
    start: function (event, ui) {

    },
    stop: function (event, ui) {
        var idx = $(this).attr("idx");
        var left = $(this).css("left");
        var top = $(this).css("top");
        var x = $(this).offset().left;
        var y = $(this).offset().top;
        var w = $(this).width();
        var h = $(this).height();
        var thisObj = { x: x, y: y, w: w, h: h, idx: idx };
        if (overLapChecker(".rect", thisObj)) {
            //통과
            rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
        } else {
            //겹침 - 초기화
            var obj = rectArr[idx];
            $(".rect[idx=" + idx + "]").css("top", obj.top);
            $(".rect[idx=" + idx + "]").css("left", obj.left);
        }
    }
}).resizable({
    containment: "parent",
    start: function (event, ui) {

    },
    stop: function (event, ui) {
        var idx = $(this).attr("idx");
        var left = $(this).css("left");
        var top = $(this).css("top");
        var x = $(this).offset().left;
        var y = $(this).offset().top;
        var w = $(this).width();
        var h = $(this).height();
        var thisObj = { x: x, y: y, w: w, h: h, idx: idx };
        if (overLapChecker(".rect", thisObj)) {
            //통과
            rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
        } else {
            //겹침 - 초기화
            var obj = rectArr[idx];
            $(".rect[idx=" + idx + "]").css("top", obj.top);
            $(".rect[idx=" + idx + "]").css("left", obj.left);
            $(".rect[idx=" + idx + "]").css("width", obj.w);
            $(".rect[idx=" + idx + "]").css("height", obj.h);
        }
    }
})



function overLapChecker(className, thisObj) {
    var len = $(className).length;
    if (len > 0) {
        var thisMapArr = JSON.parse(JSON.stringify(canvasArr)); //빈 배열을 복사한다.
        var x = thisObj.x;
        var y = thisObj.y;
        var w = thisObj.w;
        var h = thisObj.h;
        for (i = y; i < y + h; i++) {
            for (j = x; j < x + w; j++) {
                thisMapArr[i][j] = thisMapArr[i][j] + 1;
            }
        }
        for (z = 0; z < len; z++) {
            var idx = $($(className)[z]).attr("idx");
            if (idx == thisObj.idx) {
                continue;
            } else {
                var checkerArr = JSON.parse(JSON.stringify(thisMapArr));
                var x2 = $(".rect[idx=" + idx + "]").offset().left;
                var y2 = $(".rect[idx=" + idx + "]").offset().top;
                var w2 = $(".rect[idx=" + idx + "]").width();
                var h2 = $(".rect[idx=" + idx + "]").height();

                for (i = y2; i < y2 + h2; i++) {
                    for (j = x2; j < x2 + w2; j++) {
                        checkerArr[i][j] = checkerArr[i][j] + 1;
                        if (checkerArr[i][j] > 1) {
                            return false;
                        }
                    }
                }
            }
        }

    }
    return true;
}

드래그, 리사이즈 이벤트 처리를 하고 각 도형의 위치를 특정 배열에 담아둡니다.

 

드래그 또는 리사이즈 이벤트가 멈추면 멈추는 동시에 위치를 체크합니다. 겹치는 동선이 존재하여 배열에(2)가 처리되면 원위치 시킵니다.

 

적용 모습

겹침이 방지되었다!

반응형
반응형

 

css를 활용하여 div를 동그라미로 만들었고 jQuery-ui의 Draggable 이벤트를 추가하였습니다.

 

기본적으로 position을 활용하기 때문에 서로 두개의 도형을 위 이미지형태로 처리하면 겹치게 되는데, 두 개 이상의 원형이 있을경우 겹치지 않도록 처리해보겠습니다.

 

소스를 보기전에 간단하게 겹침처리 방지에 사용한 방식을 설명하겠습니다.

위 그림의 공식을 사용하였습니다.

각 도형의 좌측, 상단의 좌표값을 알때 중간점을 구할수 있고, 중간점과 중간점의 거리를 구해서

그 거리값이 반지름 + 반지름한 값보다 작다면 겹쳤다고 판단하여 원래 위치로 돌리는 소스입니다.

 

 

드래그이벤트 원형 겹침 방지

See the Pen BaLQdbG by myhappyman (@myhappyman) on CodePen.

반응형