document.querySelector로 요소를 찾았다면, 이제 그 요소의 **글자(textContent)**와 **클래스(classList)**를 바꿔 화면을 움직일 차례입니다. 여기서 가장 중요한 원리는 자바스크립트 변수를 바꿨다고 해서 화면이 저절로 바뀌지는 않는다는 점입니다.
1글자 읽기와 바꾸기: textContent
요소 안에 들어 있는 글자를 읽거나 새 글자로 바꿀 때는 el.textContent 속성을 씁니다.
<p id="count" class="count">
전체 3개 중 2개 남음
</p>여러 줄로 들여쓴 HTML 요소의 textContent를 읽으면 줄바꿈과 앞뒤 공백이 함께 따라옵니다. 그래서 글자를 읽어 비교할 때는 **.trim()**으로 앞뒤 공백을 깎아 냅니다(HTML·CSS 과목의 브라우저 규칙 2).
'use strict';
const countEl = document
.querySelector('#count');
console.log(
countEl.textContent.trim(),
);
countEl.textContent =
'전체 3개 중 1개 남음';
전체 3개 중 2개 남음
(#count 글자 → 1개 남음)반대로 countEl.textContent에 '전체 3개 중 1개 남음'처럼 새 문자열을 대입하면 화면의 글자가 그 자리에서 바뀝니다.
2변수를 바꿔도 화면은 저절로 바뀌지 않는다
2단원의 규칙 2(식은 그 줄을 실행할 때 한 번 계산되어 값으로 바뀐다)는 브라우저 화면에서도 그대로 적용됩니다.
아래 코드에서 n을 2에서 1로 바꿨을 때, countEl.textContent를 다시 대입하지 않으면 어떻게 되는지 보세요.
let n = 2;
const el = { textContent: '' };
el.textContent = `남음 ${n}개`;
n = 1;
console.log(el.textContent);
el.textContent = `남음 ${n}개`;
console.log(el.textContent);
남음 2개
남음 1개- 3번 줄에서
`남음 ${n}개`는 그 순간의 값인'남음 2개'로 계산되어el.textContent에 들어갑니다. - 4번 줄에서
n = 1로 변수를 바꿔도, 이미 들어가 있는el.textContent는 저절로 바뀌지 않고'남음 2개'그대로입니다. - 7번 줄처럼 바뀐 변수로
el.textContent에 다시 대입해야 화면 글자가'남음 1개'로 바뀝니다.
3클래스 붙였다 떼기: classList
할 일 줄에 취소선을 긋거나 회색으로 바꿀 때 자바스크립트에서 글자 색과 줄 스타일을 일일이 적지 않습니다. CSS 파일(style.css)에 이미 .done .text 취소선(text-decoration: line-through) 규칙이 준비되어 있으므로, 자바스크립트는 **classList**로 'done' 클래스만 붙였다 똅니다.
'use strict';
const item = document
.querySelector(
'#todo-list li',
);
item.classList.add('done');
item.classList.remove('done');
item.classList.toggle('done');
console.log(
item.classList
.contains('done'),
);
true
(첫째 줄에 취소선이 그어짐)classList.add('done'):'done'클래스를 붙입니다(이미 있으면 그대로 둡니다).classList.remove('done'):'done'클래스를 똅니다.classList.toggle('done'):'done'이 없으면 붙이고, 있으면 똅니다. 두 번째 인자로 불리언을 넘겨toggle('done', t.done)처럼 쓰면 둘째 인자가 참일 때 붙이고 거짓일 때 똅니다.classList.contains('done'):'done'클래스가 붙어 있으면true, 없으면false를 돌려줍니다.
예제
결과를 먼저 생각한 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.
초기 상태가 <li class="todo">우유 사기</li>인 요소를 li로 찾았습니다. 아래 네 줄을 차례로 실행한 뒤 li.classList.contains('done')과 화면 글자는 무엇일까요?
'use strict';
let label = '우유 사기';
li.textContent = label;
label = '치즈 사기';
li.classList.toggle('done');
li.classList.toggle('done');
결과 보기
contains('done'): false
화면 글자: 우유 사기toggle('done')을 두 번 불렀으므로 붙었다가 다시 떨어져false입니다.label = '치즈 사기'로 변수만 바꾸고li.textContent = label을 다시 대입하지 않았으므로 화면 글자는'우유 사기'그대로입니다.
자주 틀리는 곳
- 변수를 바꾸면 화면도 저절로 바뀐다고 생각한다:
let count = 2;를count = 1;로 바꿨다고 해서 이전에 넣은countEl.textContent가 따라 바뀌지 않습니다. 상태를 바꾼 뒤에는 늘 화면 요소의textContent나classList를 다시 갱신해야 합니다. - 사용자가 입력한 글자를
innerHTML에 넣는다:el.innerHTML = text는 문자열 속의<script>나 HTML 태그를 실제 태그로 해석해 실행하므로 악성 스크립트가 실행되는 보안 문제(XSS)가 생깁니다. 글자를 넣을 때는 태그를 글자 그대로 안전하게 표시하는 **textContent**를 씁니다. classList대신el.style로 모양을 직접 고친다: 자바스크립트에서el.style.color = 'gray'처럼 직접 스타일을 박아 넣으면 다크 모드나 CSS 규칙과 충돌합니다. 모양은 CSS 클래스(.done,.active)에 맡기고 자바스크립트는classList만 바꿉니다.- 자식 요소가 있는 부모에
textContent = '글자'를 대입한다: 체크박스와<span class="text">, 삭제 단추가 들어 있는li에 바로li.textContent = '치즈'를 대입하면 안쪽의 체크박스와 삭제 단추가 통째로 지워집니다. 글자만 바꿀 때는 안쪽span.text를 찾아 바꿔야 합니다.
확인 문제
el.classList.toggle('active')를 실행할 때,el에 이미'active'클래스가 붙어 있었다면 어떻게 되나요?
정답
'active' 클래스가 제거됩니다(toggle은 있으면 떼고, 없으면 붙입니다).
- 사용자가 입력 칸에 친 할 일 글자
text를span요소에 넣으려고 합니다.span.innerHTML = text대신span.textContent = text를 써야 하는 까닭을 쓰세요.
정답
innerHTML은 입력 글자 안의 태그 기호를 HTML 태그로 해석해 의도하지 않은 스크립트가 실행될 위험(XSS)이 있지만, textContent는 어떤 글자든 안전한 일반 텍스트로만 넣기 때문입니다.
- 다음 코드가 끝난 뒤 화면의
#count문단에 보이는 글자는'1개'인가요,'2개'인가요?'1개'가 보이게 하려면 마지막에 어떤 한 줄을 더해야 하나요?
'use strict';
let left = 2;
const c = document
.querySelector('#count');
c.textContent = `${left}개`;
left = left - 1;
정답
화면에는 여전히 '2개'가 보입니다. 변수 left를 바꾼 뒤 마지막 줄에 c.textContent = ... 대입을 한 번 더 실행해야 '1개'로 바뀝니다.