자바스크립트의 용도 중에서 가장 많이 쓰이는 용도가 바로 이
감추기,보이기 야.
문서의 특정요소를 선택해서 안보이게 하거나, 반대로 숨겼던 내용을 보여 주는 거지.
이 작업을 하기 위해서는 특정요소를 가져 오는게 우선이야.
주로 <div> <p> <h1,2..> <ul> 이러한 태그 단위로 지정해서 해당 요소를 조작하는 거야.
예제로 배워 보자구.
여러분의 js.html 내용을 아래 내용을 변경해. 가장 전형적인 html 파일의 내용이야.
CSS는 나중에 공부할 수 있도록 앞서 CSS 학습에서 배웠던 w3.css 를 사용해서 각 요소를 눈으로 구분하기 쉽게 메뉴와 본문은 class="w3-border" 를 사용해서 테두리를 보이게 했어.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>알찬만두</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body>
<div>
<button onclick="hide()">감추기</button>
<button onclick="show()">보이기</button>
</div>
<h1>홈피소개</h1>
<div id="menu" class="w3-border">
<ul>
<li> 홈피소개 </li>
<li> 게시판 </li>
</ul>
</div>
<div id="content" class="w3-border">
<h2> 제목 </h2>
<p id="note"> 아버지는 나귀타고 장에 가시고 <p>
</div>
<script>
</script>
</body>
</html>
위 내용을 js.html 파일 안에 넣고 저장해서 브라우저에서 열어봐.
■ 감추기
우리는 우선 연습으로 [감추기] 버튼을 클릭하면 hide() 이라는 함수를 실행해서 본문 쪽에 있는 메뉴 div 를 감춰 볼거야.
현재 위 파일에는 div 요소가 2개가 있어 메뉴를 가진 div 와 본문을 담당하는 div 가 있어.
이 2개 중에서 메뉴 <div id="menu">~</div> 를 찾아서 감춰야 해.
특정 이름을 가진 요소를 가져올 때 사용하는 명령이 바로 document.getElementById 야.
이름만 봐도 "문서에서 id 로 요소를 가져오기" 같은 의미잖아.
아래와 같이 소스 아래쪽 <script>~</script> 사이에 hide() 함수를 수정해
function hide() {
let m = document.getElementById('menu');
m.style.setProperty("display", "none");
}
document.getElementById('menu') 로 가져온 요소를 m 이라는 변수에 할당했어.
(이때 이 변수 이름은 m 가 아니고 어떤 이름이어도 상관없어. - mn, menu, m1..)
이렇게 복사(?)된 m 은 id="menu" 이름을 가지는 <div> 요소의 모든 특성을 가지게 돼.
즉 이제 m 는 <div id="menu">~</div> 자체라고 생각하면 돼. 즉 하나의
'객체'야.
즉 바로 객체지향 프로그램 기법이야.
이 말이 처음에 좀 어려운데 워낙 많이 사용하니 그냥 그런가 보다... 생각하면 돼.
둘째줄 m.style.setProperty("display", "none"); 은 이렇게 가져온 객체의 스타일속성(style)에서 display 속성값을 변경하는 거야.
이것도 자바스크립트를 사용할 때 엄청 많이 사용하는 명령이야.
결국 해석하자면 <div id="menu">~</div> 전체를 m 이라는 객체에 할당하고, 이 객체의 속성을 변경해서 감추라는 명령이야.
이 코드를 수행하면 결국 원래의 <div id="menu"> 코드는 내부적으로 아래와 같이 변경 되어 화면에서 안보이게 돼.
<div id="menu" style="display: none">.... </div>
스타일에서 display: none 은 아마 처음 보는 사람도 많을거야.
왜냐하면 Html 문서는 보여 주려고 만들지 안보이는 내용을 입력할 필요가 없으니까.
그러나 자바스크립트에서는 아주 유용하게 사용하는 스타일 속성이야.
자바스크립트로 속성을 바꿔서 특정 요소를 감추었다고 원래 이 파일의 소스가 바뀌는 것은 아니야.
자바스크립트의 위 코드를 실행해서 일시적으로 안보이게 한거야. 화면을 새로고침하면 다시 원래대로 보이게 돼.
■ 보이기
이번에는 반대로 감춰져 있는 요소를 보이게 하는 것을 해볼까?
위에서 감추기 버튼을 눌러 감춰진 div 를 다시 보이게 하는 show() 함수를 만들어 보자구.
hide() 함수 아래에 아래와 같은 show() 함수를 추가해.
function show() {
let m = document.getElementById('menu');
m.style.setProperty("display", "block");
}
거의 똑같아.
다른 점은 setProperty("display", "block") 에서 속성이 block 라는 점이야.
이렇게 block 로 하면 내부적으로 <div id="menu" style="display: none">.... </div> 로 작동해서 숨겨져 있던 요소가 보이게 돼.
■ 인자(Parameter)를 넣어서 함수 만들기
지금 만든 hide(), show() 함수는 id="menu" 라는 하나의 요소에서만 사용하는 함수야.
다른 요소들을 감출 때도 사용할 수 없을까?
전에 배웠던 인자를 이용한 함수를 만들어서 해결할 수 있어.
위 hide, show 함수를 아래와 같이 수정해 봐.
함수 ( ) 안에 인자 ele 를 넣고, document.getElementById(ele) 에서 인자를 사용했어.
여기서 ele 는 내가 정한 이름으로 어떤 이름이어도 괜찮아. 일종의 변수이름이야.
ele 라는 이름은 Elements 의 약자로 많은 자바스크립트 개발자들이 사용하는 이름이어서 여기서도 그렇게 써봤어.
function hide(ele) {
let m = document.getElementById(ele);
m.style.setProperty("display", "none");
}
function show(ele) {
let m = document.getElementById(ele);
m.style.setProperty("display", "block");
}
이제 상단 button 에서 함수를 호출할 때 아래와 같이 숨기기, 보이기를 할 요소의 id 를 지정하면 돼.
이번에는 본문을 감춰 볼까? 아래와 같이 코드를 수정하고 테스트 해봐.
<button onclick="hide('content')">감추기</button><button onclick="show('content')">보이기</button><br>
이렇게 하면 id="content" 값을 가지는 요소(여기서는 div)를 인자로 함수에 넘겨주게 되겠지?
그리고 함수에서는 이것을 id 라는 변수에 넣어서 작동을 하게 돼. 이제 여기서 id 변수의 값은 'content'가 되는거야.
결국 document.getElementById(id) 는 document.getElementById('content') 와 똑같이 작동하게 돼.
이렇게 인자를 넣어서 함수를 만들면 인자만 바꿔서 여러곳에서 사용할 수 있어.
■ class 속성 다루기
지금까지 화면의 특정 요소를 찾아 내기 위해 id 를 이용했어.
이번에는 class 를 검색해서 요소를 찾는 방법을 알아 볼까?
class 이름으로 Html 문서 안의 특정 요소를 찾는 명령은 document.getElementsByClassName() 야.
맨 뒤에 ClassName 이라는 이름을 보면 짐작할 수 있겠지?
여기서 잠깐!
주의할 점은 이름을 자세히 보면 getElementsByClassName 중간의 Elements 에 맨 뒤에 s 가 있어.
영어 단어에서 맨 뒤에 s 는 복수를 뜻하는거 알지? 즉 Class 는 같은 이름을 가진 요소가 여러개 있을 수 있다는 뜻이야.
id 요소를 가져올 때 사용한 getElementById 를 보면 Element 에 s 가 없어. 즉 1개 만 있다는 거야.
Html/CSS 를 공부하면서 id 와 class 를 배웠어도 차이점을 잘 모르는 분들이 많아.
또 이 id 와 class 는 CSS 에서 뿐만 아니라 자바스크립에서도 아주 중요한 차이점이 있어.
Html 문서 안에서 id 의 이름은 딱 한 번만 사용되고, class 는 여러번 사용할 수 있어.
이 특성을 이용해서 문서에서 딱 1개만 찍어 선택, 조작해야 할 때는 id 를 사용하고, 여러 개를 함께 선택해서 다 동시에 조작할 때는 class 를 사용해.
그럼 한 id 의 이름은 문서에서 한 번만 사용된다고 했는데 2번 사용하면 어떻게 될까?
문서가 안보일까? 에러가 나오나?
사실 대부분의 경우 아무일도 일어나지는 않아.
그리고 getElementById 를 사용하면 같은 이름이 여러개 있는 경우 대부분 첫번째 요소를 가져 오게 돼.
그러나 확실하지는 않아. 왜냐하면 원래의 사용법에 어긋나니까..
즉 id 는 한 번, class 는 여러번 사용한다는 것은 그냥 규칙으로 개발자나 Html 문서 작성자 간의 효율적인 협업을 위한 약속이라고 할까?
그런데 getElementById 나 getElementsByClassName 같은 이름 너무 긴 것 같지 않아?
나도 자바스크립의 이런 긴 작명방법 싫어.
그렇지만 공부해 가다 보면 함수나 라이브러리를 통해 실제 이 긴 이름을 모두 타이핑하면 쓸 일은 그리 많지 않아. 미리 너무 걱정하지 마..
연습으로 바로 확인해 볼까?
지금 우리의 예제 Html 파일에는 맨 위의 <h1>~</h1> 이외에는 모든 div 에 테두리를 주기 위해 w3-boder 스타일을 주었어.
그러므로 이 스타일을 가진 요소들을 감추면 전체가 다 보이지 않겠지?
우선 [감추기] 부터 한 번 해보자구.
상단 onclick 부분과 아래 함수를 아래와 같이 수정하고 테스트해 봐.
<button onclick="hide('w3-border')">감추기</button><button onclick="show('w3-border')">보이기</button><br>
function hide(ele) {
let m = document.getElementsByClassName(ele);
m.style.setProperty("display", "none");
}
[감추기] 버튼 클릭하면 내용들이 모두 감춰져?
안되지? 왜 그럴까?
이유는 getElementsByClassName 에 의해 찾아지는 요소가 1개가 아니기 때문이야.
Html 문서에서 클래스 이름이 같은 요소는 여러개가 나올 수 있기 때문에 첫줄
let m = document.getElementsByClassName(ele) 에서 생성된 m 변수는 배열변수로 만들어 져.
우리의 연습문서에서는 w3-border 라는 클래스 이름을 사용하는 것이 총 2 개가 있어.(메뉴와 본문)
여기서는 m[0] 은 첫번째 메뉴부분의 div 를 가르키고, m[1] 은 두번째 본문 div 를 뜻 가르키게 돼.
그러므로 위 코드를 작동되게 하려면 아래와 같이 적어 주어야해.
function hide(ele) {
let m = document.getElementsByClassName(ele);
m[0].style.setProperty("display", "none");
m[1].style.setProperty("display", "none");
}
그런데 동일한 이름을 가진 요소가 아주 많다면?
일일이 m[0] m[1] m[2] m[3] m[4]... 이렇게 똑같이 쓰려면 코드만 길어지잖아.
더 큰 문제는 배열요소가 정확인 몇 개인지 알아야 할 것 아냐.
바로 이때 전에 배운 배열의 갯수를 찾아내는 length 속성과 for 반복문을 이용하면 해결할 수 있어.
function hide(ele) {
let m = document.getElementsByClassName(ele);
for (i=0; i < m.length; i++) {
m[i].style.setProperty("display", "none");
}
}
위 코드가 이해 돼? 작동하는 순서대로 설명해 볼께
- 인자로 "w3-border"를 받아서 실행되므로 인자변수인 cls 의 값은 w3-border 가 돼
- document.getElementsByClassName(ele) 에서 찾은 요소 2개를 m[0] 과 m[1]에 저장해
- m.length 는 m배열의 갯수를 알려 주므로 2 가 될거야
- for 루프를 2번(i=0, i=1) 돌면서 각 m 객체를 감춰
즉 for 안의 m[i] 는 i 의 값에 따라 처음에는 m[0] 이 되고 두번째 돌 때는 m[1] 이 되겠지?
이제 정상적으로 작동할거야.
hide 버튼을 누르면 h1 을 제외한 모든 내용이 다 안보이게 되겠지?
그럼 다시 보이게 하는 show() 도 똑같은 방식으로 복사해 만들면 되지 않겠어?
멋지게 작동하지?
■ querySelector , querySelectorAll
문서에서 특정 요소를 가져오는 명령에는 이외에도 getElementsByTagName 도 있는데 이것은 태그 이름을 사용해서 요소들을 가져오는 명령이야.
이렇듯 getElementBy* 명령은 가져 올 요소의 종류에 따라 각각 다른 명령을 내려야 해.
그런데 똑똑한 학생이라면 지금까지 배운 hide(), show() 를 보면서 이런 생각을 했을거야.
"getElementById 와 getElementsByClassName, getElementsByTagName 를 꼭 이렇게 따로 사용해야 할까? 하나로 합쳐서 쓸 수 없을까?"
당연히 방법이 있지.
자바스크립트 개발자들이 사용자들의 원성에 대답하려고 만든 명령이 바로 이 querySelector 야.
이 명령 하나로 id, class, tag 모두 가져 가져올 수 있어.
대신 인자에서 css 에서 사용하는 # . 기호를 이용해서 id 와 class 를 구분해.
무슨 말인지 예제를 보면 바로 이해 될거야.
먼저 처음으로 했던 id="menu" 를 감추는 명령을 querySelector 를 사용하면 다음과 같아.
아래와 같이 소스를 변경하고 테스트를 해봐.
<button onclick="hide('#menu')">감추기</button><button onclick="show('#menu')">보이기</button><br>
function hide(ele) {
let m = document.querySelector(ele);
m.style.setProperty("display", "none");
}
전의 getElementById 와 다른 점은 함수를 호출하기 위해 인자를 넣을 때 '#menu' 와 같이 앞에 # 기호를 넣어서 찾고자 하는 요소가 id 라는 것을 알려 주었어.
두 번째 class="w3-border"를 찾는 방법은 다음과 같아.
<button onclick="hide('.w3-border')">감추기</button><button onclick="show('.w3-border')">보이기</button><br>
function hide(ele) {
let m = document.querySelector(ele);
m.style.setProperty("display", "none");
}
함수 function hide(ele) 의 내용은 두 개가 똑같아.
단지 다른 점은 함수를 호출할 때 id 를 찾기 위해서는 onclick="hide('#menu')" 로 # 기호를 사용했고, class 를 찾을 때는 onclick="hide('.w3-border')" 로 점(.) 기호를 사용했어.
태그를 사용할 때는 아래와 같이 태그 이름을 그대로 적어 주면 돼.
onclick="hide('h1')"
그런데 뭐가 조금 이상하지 않아?
위에서 공부하면서 class 이름으로 찾을 때는 한 문서 안에 같은 이름의 class 가 여러개 있을 수 있어서 배열변수를 사용한다고 했는데 위에서는 그냥 일반 변수를 사용했잖아.
맞아~
그것은 querySelector 명령은 1개만 찾는 명령이기 때문이야.
클래스 이름에 querySelector 을 사용하면 같은 이름의 클래스가 여러개 사용되도 첫번째 요소만 가져와.
우리의 예제에서는 m[0] 만 가져 오고 그 뒤는 있어도 무시하기 때문에 id 처럼 일반변수 m 에 해당 정보를 저장해.
그러면 모든 클래스(또는 태그)의 위치정보(즉, 객체)를 다 가져 오려면 어떻게 할까?
그때 쓰는 명령이 바로
querySelectorAll 이야.
이름만 봐도 알겠지?
즉, 앞서 연습 처럼 w3-border 클래스 이름을 가진 요소들을 모두 감추려면 다음과 같이 적어 줘야해.
querySelectorAll 은 getElementsByClassName 처럼 검색해서 찾은 모든 요소를 배열에 저장해서 반납 하기 때문에 아래와 같이 해야 정확하게 작동하겠지.
function hide(ele) {
let m = document.querySelecotorAll(ele);
for (i=0; i < m.length; i++) {
m[i].style.setProperty("display", "none");
}
}
자, 그럼 여기서 왜 이렇게 했을까?
실제로 자바스크립트를 이용해서 코딩을 하다 보면 여러 요소를 다 함께 조작하는 경우는 별로 없어.
id 로 특정 요소를 조작하는 경우가 대부분이고, class 를 조작하는 경우도 보통은 1개만 조작하는 경우가 많아.
그래서 querySelector 는 id 이든 class 이든 첫번째 만나는 1개만 조작할 때 사용하고,
여러 개의 요소를 다 같이 조작할 필요가 있을 때는 querySelectorAll 을 사용할 수 있도록 했어.
(또 여러 요소를 다 건드릴 때에는 예전의 getElementsByClassName 과 getElementsByTagName 도 있으니까..)
이제 이해가 좀 되지?
querySelector 가 짧고 더 편하기 때문에 이 명령만 사용해도 좋아.
그러나 getElementBy* 명령도 알고 있어야 하는 이유는 구글등에서 검색하면 getElementBy* 명령을 훨씬 더 많이 보기 때문이야.
과거 대부분의 수백만개의 소스들은 다 그걸 사용했으니 모르면 코드를 이해할 수 없잖아.
그래서 내 코드에는 하나 만 쓰더라도 알기는 둘 다 알아야 해.
결론은 둘 다 사용법만 약간 다르고 같은 명령이야.
참고로 getElementById 가 querySelector 보다 0.0000...01 초 더 빨라.
왜냐하면 정확하게 1개만 있는 id 를 찾는 것이니까....ㅋ
여기에 대해 좀 더 자세히 파고 싶으면 :
https://ko.javascript.info/searching-elements-dom#ref-879