everyday com-eat
작성일
2022. 1. 14. 12:04
작성자
갱수터
728x90

콜백 함수

- 이벤트에 등록된 함수는 콜백함수이다.

<button type="button" id="btn01">콜백함수 테스트</button>
<script type="text/javascript">
	var btn01 = document.querySelector('#btn01');
	btn01.onclick = function(event){
		console.log(this, '이벤트 발동 대상');
		//이벤트 작동에 관련된 정보를 콜백데이타로 전달해준다.
		console.log(event);
	}
</script>

더보기
<div id="textMove" style="position: absolute;">한국스마트정보교육원</div>
<script type="text/javascript">
	var textMove = document.querySelector('#textMove');
	document.onmousemove = function(e){
		//console.log(e);
		textMove.style.top = e.y + 'px';
		textMove.style.left = e.x + 'px';
	}
</script>

onmousemove - 마우스가 움직일 때

textMove라는 id를 가진 div의 top,left의 값을 마우스 좌표로 줘서

마치 마우스를 따라다니는 것처럼 보인다

 

콜백 데이타의 target 속성 확인

<div class="div01">
	<div class="div02">
		<div class="div03">
			<button type="button" class="btn001">btn001</button>
		</div>
	</div>
</div>

<script type="text/javascript">
	var div01 = document.querySelector('.div01');
	div01.onclick = function(e) {
		console.log(e);
		console.log(e.target.classList.contains('div01'));
		console.log([this]);
	}
</script>

 

더보기

ajax 수업 request.done메서드 콜백데이터 이해 안돼서 예시 

// html
<input type="text" id="userName">
<button type="button" id="callbackBtn">콜백테스트</button>

 

function myCallback() {
	this.type='';
	this.target='';
	this.eventTarget='';
}

myCallback.prototype.eventInfo = function(eventTarget, type, target) {
	this.type = type;
	this.target = target;
	this.eventTarget = eventTarget;
	return this;
}

myCallback.prototype.callback = function(fn) {
	
	var searchTarget = document.querySelector(this.eventTarget);
	var searchValueTarget = document.querySelector(this.target);
	
	if(searchTarget != undefined && searchValueTarget != undefined){
		
		searchTarget.addEventListener(this.type, function() {
			fn(searchValueTarget.value);
		});
	}
	
}

var myCall = new myCallback();
console.log(myCall);
myCall
.eventInfo('#callbackBtn', 'click', '#userName')
.callback(function(data){
	console.log(data, '값 가지고 오기');
})

 

 

clone

-선택된 객체를 복제

 

clone처리(객체 복제) 없이 구조 이동

- html tag도 객체(element)이다.
- 검색된 객체를 다른 객체 하위에 삽입 하였을 경우 복제가 아닌 이동이 된다.

<h4>상단</h4>
<ul id="ul01">
	<li class="li01">안녕하세요.</li>
</ul>
<h4>하단</h4>
<ul id="ul02">
</ul>
<button type="button" id="moveBtn">이동</button>

<script type="text/javascript">
	var moveBtn = document.querySelector('#moveBtn');
	var ul01 = document.querySelector('#ul01');
	var ul02 = document.querySelector('#ul02');
	var li01 = document.querySelector('.li01');		
	moveBtn.onclick = function() {			
		ul02.appendChild(li01);
	}
</script>

> 상단에 있던 li가 하단으로 이동한다

 

 

clone처리(객체 복제) 후 삽입

- cloneNode() -> 객체 복제
- cloneNode(true) -> 인수값이 true일 경우 하위 요소 전체 복제
- cloneNode(false) -> 인수값이 false일 경우 선택 영역만 복제

<h4>상단</h4>
<ul id="ul03">
	<li class="li02">안녕하세요.</li>
</ul>
<h4>하단</h4>
<ul id="ul04">
</ul>
<button type="button" id="moveBtn2">복제</button>

<script type="text/javascript">
	var moveBtn = document.querySelector('#moveBtn2');
	var ul03 = document.querySelector('#ul03');
	var ul04 = document.querySelector('#ul04');
	var li02 = document.querySelector('.li02');		
	moveBtn2.onclick = function() {
		//검색된 li객체 복제
		//복제 후 화면에는 추가가 안된 상태
		var cloneLi = li02.cloneNode(true);
		//복제된 대상을 화면에 삽입
		ul04.appendChild(cloneLi);
	}
</script>

> 상단에 있는 li가 복제되어 하단에 삽입되기 때문에 삽입 버튼을 누르면 계속하여 하단에 li가 복제된다

더보기
<ul id="inputWrap">
	<li class="li02">
		<input type="text">
		<button type="button" class="addBtn">추가</button>
		<button type="button" class="delBtn">제거</button>
	</li>
</ul>

추가와 제거 버튼을 누를 시 해당 행들이 추가/제거 될 수 있도록

var inputWrap = document.querySelector('#inputWrap');		
var addBtn = document.querySelectorAll('.addBtn');
var delBtn = document.querySelectorAll('.delBtn');

for(var i=0; i<addBtn.length; i++){
	addBtn[i].onclick = function() {
		//#inputWrap 첫번째 하위요소 선택
		var firstLi = inputWrap.firstElementChild;
		console.log(firstLi);
		
		//첫번째 하위요소를 복제
		var clone = firstLi.cloneNode(true);
		//복제된 대상의 하위 input 태그 검색
		var cloneInput = clone.querySelector('input'); 
		//value값을 공백으로 삽입
		cloneInput.value = '';
		
		inputWrap.appendChild(clone);
		/*
			읽어진 대상에만 이벤트가 등록이 되어 있으며,
			추가된 대상에는 이벤트를 별도로 등록해야 한다.
			이러한 행위를 '동적 바인딩'이라고 한다.
		*/				
	}
}

> 첫번째 행엔 이벤트가 등록되어 있어서 추가가 잘 되는데, cloneNode로 삽입된 추가행들엔 이벤트가 걸려있지 않아 버튼을 눌러도 이벤트가 실행되지 않는다. 이럴 때 동적바인딩을 이용하면 된다.

 

 

바인딩

- 바인딩 : 이벤트 등록
- 동적 바인딩 : 동적으로 추가된 객체에 이벤트 등록

<ul id="bind01">
	<li>
		<button type="button" class="addBtn01">추가</button>
		<button type="button" class="delBtn01">제거</button>
	</li>
</ul>
<script type="text/javascript">
	var bind01 = document.querySelector('#bind01');
	bind01.onclick = function(e) {
		//콜백데이타로 이벤트 작동 정보가 매개변수로 넘어온다.
		//target 속성은 이벤트 작동의 행위 대상이다.
		
		var targetClass = e.target.classList;
		console.log(e, targetClass.contains('addBtn01'));
		//addBtn01클래스 버튼 클릭을 했다면
		if(targetClass.contains('addBtn01')){
			var clone = e.target.parentElement.cloneNode(true);
			this.appendChild(clone);
		}
		//delBtn01클래스 버튼 클릭을 했다면
		if(targetClass.contains('delBtn01')){
			e.target.parentElement.remove();
		}
	}
</script>

> 복제되어 새로 삽입된 행들의 버튼들도 제대로 이벤트가 구동된다

 

 

addEventListener

- addEventListener 메서드를 활용하여 이벤트 등록이 가능하다.
- html 요소 이벤트(on속성이벤트)는 동일한 이벤트 한번만 등록 가능한 반면,addEventListener로 이벤트 등록시 동일한 이벤트 1개 이상 등록 가능하다.
- addEventListener 이벤트 등록시 여러 이벤트를 한번에 등록이 가능하다.

<button type="button" id="aelBtn01">이벤트 중첩</button>
<input type="text" id="aelInput01">

<script type="text/javascript">
	var aelBtn01 = document.querySelector('#aelBtn01');
	//첫번째 인수: 이벤트명 , 두번째 인수: 함수
	aelBtn01.addEventListener('click', function(e) {
		console.log('클릭1',e)
	});
	aelBtn01.addEventListener('click', function(e) {
		console.log('클릭2',e)
	});
	
	var aelInput01 = document.querySelector('#aelInput01');
	aelInput01.addEventListener('blur', function(e){
		console.log(this.value);
	})
</script>
더보기

실습1. esc키를 눌렀을 경우 'esc키는 사용 하시면 안됩니다.'라는 문구를 띄우는 코드를 작성하도록 하시오.

document.addEventListener('keyup', function(e) {
	console.log(e);
    if(e.key == 'Escape'){ //keyCode로 조건을 줘도 된다
		alert('esc키는 사용 하시면 안됩니다.');
	}
})

> 이벤트명에 뭘 써야할지 몰라서 처음에 조금 헤맸다. 선생님이 힌트를 주셔서 풀었음

 

실습2. 아래의 input태그를 텍스트 입력 후 엔터키 눌렀을 경우 'ksmart.or.kr'웹사이트로 이동시키도록 하시오.

<input type="text" id="moveInput" >

풀이

var moveInput = document.querySelector('#moveInput');
moveInput.addEventListener('keyup', function(e){
	//console.log(e);
	//console.log(this.value);
	if(e.keyCode==13){
		if(this.value == 'ksmart.or.kr'){
			location.href = 'http://ksmart.or.kr';
		}
	}
})

 

실습3. 추가 버튼을 클릭시 행추가, 삭제 버튼 클릭시 해당 행 제거를 하도록 하시오.

<table border="1" id="myTable">
	<tr>
		<td>
			<input type="text">
		</td>
		<td>
			<button type="button" class="delBtn">제거</button> 
		</td>
	</tr>
</table>
<button type="button" id="addRowBtn">추가</button>

풀이

var addRowBtn = document.querySelector('#addRowBtn');
var myTable = document.querySelector('#myTable');
var tr = myTable.querySelector('tr');

addRowBtn.addEventListener('click', function() {
	//console.log(e.target);					
	//console.log(tr);
	var trClone = tr.cloneNode(true);
	trClone.querySelector('input').value = '';
	myTable.appendChild(trClone);
	
});		

myTable.addEventListener('click', function(e) {
	//console.log(e.target);
	if(e.target.classList.contains('delBtn')){
		if(myTable.querySelectorAll('tr').length>1){
			this.querySelector('tr').remove();
		}else {
			alert('삭제하실 수 없습니다');
		}
	}
});

> 나는 첫번째 행이 삭제되도 추가 버튼을 누르면 다시 정상적으로 표가 생기게 코드를 짰는데 그럴 필요없이 그냥 첫번째 행은 삭제가 되지 않도록 조건을 걸어줘야 했음

더보기

실습. 아래의 추가 및 삭제 버튼 클릭시 해당 행의 추가와 삭제가 되도록 하여라. 
(input 및 check에 관련된 내용은 추가시 초기화가 되어야함)

<ul id="inputGroup">
	<li>
		<div class="contant-group">
			<input type="text" name="userName" placeholder="회원명">
			<input type="text" name="userAddr" placeholder="회원주소">
			성별 : 
			<label>
				<input type="radio" name="userGender" value="남"> 남
			</label>
			<label>
				<input type="radio" name="userGender" value="여"> 여
			</label>
		</div>
		<button type="button" class="addBtn">추가</button>
		<button type="button" class="delBtn">삭제</button>
	</li>
</ul>

풀이

var inputGroup = document.querySelector('#inputGroup');		

inputGroup.addEventListener('click', function(e) {
	//console.log(e.target.classList);
	var targetClass = e.target.classList;
	
	if(targetClass.contains('addBtn')){
		//console.log(this.querySelector('li'));
		var cloneLi = e.target.parentElement.cloneNode(true);
		
		var cloneLiText = cloneLi.querySelectorAll('input[type=text]');
		for(var i=0; i<cloneLiText.length; i++){
			cloneLiText[i].value = '';
		}
		
		var cloneLiRadio = cloneLi.querySelectorAll('input[type=radio]');
		for(var j=0; j<cloneLiRadio.length; j++){
			cloneLiRadio[j].checked = false;
		}
		
		this.appendChild(cloneLi);
	}
	
	if(targetClass.contains('delBtn')){
		//console.log(e.target);				
		if(inputGroup.querySelectorAll('li').length >1){
			e.target.parentElement.remove();
		}else {
			alert('삭제할 수 없습니다');
		}
	}
})

> 라디오 타입이 하나밖에 체크가 안되는데 clone으로 name이 모두 userGender로 가져와져서 초기화는 되는데 하나밖에 체크가 안된다

방법은 두가지 있는데

1. radio의 name 속성에 그냥 숫자변수를 지정해서 증감연산자를 사용하면

삭제를해도 숫자는 올라가기 때문에 name이 겹치지는 않는다

코드는 쉽지만 나중에 서버에서 받아 올 때 쉽지않음....

2. 추가/삭제 할 때마다 radio의 count를 다시 세는 코드를 따로 짜주는 방법이 있는데

나중에 서버에서 받아올 때 편하고 정말 서버에서 받을려고 radio뿐만 아니라 다른 input태그들의 name속성들도 바꿔주는게 좋다(이름이 같아서 배열로 받아오기 때문에)

 

function radioCount(){
	var liList = inputGroup.children;
	for(var i=0; i<liList.length; i++){
		var radio = liList[i].querySelectorAll('[type=radio]');
		for(var j=0; j<radio.length; j++){
			radio[j].name = 'userGender_' + i;
		}
	}
}

// 추가/삭제되는 코드 아래에 함수를 호출해주면 된다

 

만든 폼 직접 서버에서 값 받아보기

ul을 form태그로 감싸고 action속성에 userInfo.jsp를 준다.

 

유효성 검사

/*********************************
	2022-01-14 진수경
	회원 추가 및 삭제 유효성 검사
**********************************/

var saveProcessBtn = document.querySelector('#saveProcessBtn');
var userForm = document.querySelector('#userForm'); //실제 작업할 땐 변수선언은 위쪽에 몰아서 써주는게 좋음

saveProcessBtn.addEventListener('click', function() {
	var liList = inputGroup.children;
	for(var i=0; i<liList.length; i++){
		//var inputs = liList[i].querySelectorAll('input'); -> 이렇게 찾으면 name속성으로 찾아야돼서 gender값 찾기가 번거로워짐
		var userName = liList[i].querySelector('[name=userName]');
		var userAddr = liList[i].querySelector('[name=userAddr]');
		var userGender = liList[i].querySelectorAll('[name=userGender_'+i+']');
		
		if(userName.value.trim()==''){
			alert('회원명을 입력해주세요.');
			userName.focus();
			return; //반복문 중단, 함수 빠져나가기
		}
		
		if(userAddr.value.trim()==''){
			alert('회원주소를 입력해주세요.');
			userAddr.focus();
			return;
		}
		
		var cnt = 0;
		for(var j=0; j<userGender.length; j++){
			if(userGender[j].checked) cnt++;
		}
		if(cnt==0){
			alert('회원 성별을 입력해주세요.');
			userGender[0].focus();
			return;
		}				
	}
	userForm.submit();
})

 

userInfo.jsp

request.setCharacterEncoding("UTF-8");
String[] userNameArray = request.getParameterValues("userName");
String[] userAddrArray = request.getParameterValues("userAddr");

if(
	userNameArray != null &&
	userAddrArray != null &&
	userNameArray.length==userAddrArray.length
){
	for(int i=0; i<userNameArray.length; i++){
    	//radio는 뒤에 count 번호를 붙이고, 추가/삭제시
        //count번호를 재정렬되게 함수를 호출해뒀으므로 반복문 안에서 String으로 받아온다
		String userGender = request.getParameter("userGender_"+i);
		
		out.println((i+1) + "번째 폼 시작---------------------------");
		out.println("<br>이름 : "+ userNameArray[i]);
		out.println("<br>주소 : "+ userAddrArray[i]);
		out.println("<br>성별 : "+ userGender+"<br><br>");
	}
}

 

결과

post방식

 

 

728x90