먹고 기도하고 코딩하라

[이미지테스트 페이지 만들기] 5~7일 - JS, 소셜 공유 버튼 달기 본문

개발일지

[이미지테스트 페이지 만들기] 5~7일 - JS, 소셜 공유 버튼 달기

사과먹는사람 2020. 4. 30. 22:47
728x90
728x90
 

나를 알아보는 10가지 질문

사람들이 보는 나는 어떤 이미지일까?

dev-dain.github.io

이전 글 보기

 

[이미지테스트 페이지 만들기] 2~4일 - 미디어 쿼리로 반응형 웹 만들기, CSS 애니메이션

나를 알아보는 10가지 질문 사람들이 보는 나는 어떤 이미지일까? dev-dain.github.io 이전 글 보기 [이미지테스트 페이지 만들기] 0~1일 - 기획, 구조 짜기 나를 알아보는 10가지 질문 사람들이 보는 나�

dev-dain.tistory.com

 

5일

이 날은 만들다가 문득 그런 생각이 들더군요.

'탭 버튼으로도 이동할 수 있게 만들어야 할 텐데 되나?'

시작 버튼에서 해봤는데 당연히 안 됐습니다... -_- 시작 버튼을 button 태그가 아니라 div 태그로 만들었기 때문이죠.

탭 연타해봐야 input 태그와 푸터의 a 태그로 연결된 로고들만 왔다갔다할 뿐이었습니다.

시작 버튼도 탭 버튼으로 포커스할 수 있게 button 태그로 바꿔줬습니다.

qna 페이지의 answer 안의 a box들도 원래는 div였지만, button 태그가 자식으로 붙을 수 있게 꾸며야 했죠.

자바스크립트를 새로 시작했습니다.

 

 

data.js

질문(q)과 답변(a.answer, a.score)을 하나로 묶은 객체 배열을 만들었습니다. 

 

 

main.js

일단 전역변수로 STATUS (10, 질문 항목 수), qIdx(몇 번째 질문을 답하고 있는지 가리키는 인덱스), score(대답에 따른 점수를 담을 변수), 불리언 변수 beginFlag(시작 버튼을 연타하는 일을 막기 위해 만듦. 나중에 disabled 로 고치면서 지움), select 배열(답변자가 고른 선택의 인덱스를 담음. 추후 score 계산을 한꺼번에 하기 위함) 등이 있습니다.

 

load() 함수:

페이지에 접속하면 동시에 실행되는 함수입니다. 여기서는 input 태그의 value.length 가 1이 넘는지, 즉 이름으로 뭔가를 입력했는지 확인하기 위한 조건문을 만들었습니다. input 태그를 비워놓고 시작 버튼을 누르거나 input 태그에서 포커싱을 벗어나면 붉은 글씨로 이름을 입력해야 한다는 경고문이 나옵니다.

그리고 이름이 입력되고 버튼이 눌리면 비로소 begin 함수가 호출되며 다음 단계로 넘어가게 됩니다.

 

begin() 함수:

begin 함수에서는 header 와 footer, welcome 컨테이너, 즉 처음 접속했을 때 화면을 이루는 모든 커다란 요소들을 id로 잡아서 display: none을 만들었습니다. 그 과정에서 애니메이션도 넣었습니다. 그리고 display: none 해뒀던 qna 페이지의 display를 block으로 바꾸고 애니메이션을 넣었습니다. delay 말고 setTimeout을 써도 되겠다는 생각이 이제야 드네요.

-> 처음에 start였다가 뜯어고쳤습니다. qna 페이지의 display를 none에서 block으로 바꾸는 걸 goNext()라는 다음 질문으로 넘어가는 함수에 넣었습니다. setTimeout으로 1초 뒤 welcome 요소의 모든 박스들이 display: none이 되도록 했습니다.

 

goNext() 함수:

goNext()에 와서 제일 처음 하게 되는 건 qIdx와 STATUS가 같은지, 즉 질문자가 마지막 질문까지 다 대답한 다음인지 확인하는 일입니다. 만약 true라면 end()로 가고 바로 return합니다.

여기서 바보짓을 했는데... status(소문자) 변수를 만들어서 qna 페이지의 진행 바인 status-bar 내의 status 클래스 div를 쿼리 셀렉터로 할당하려고 했는데 자꾸 안 돼서 아 이건 안 되는 거구나 하고 포기하려고 했는데 제가 전역 변수로 STATUS를 선언했더라고요?? 개그도 이런 개그가 없습니다. (그렇습니다. 나의 가장 큰 적은 바로 나입니다.) 이 당시엔 이걸 모르고 왜 안 되지? 하면서 div widen width + javascript 하면서 별 검색어로 검색을 다 돌렸습니다 ㅋㅋㅋ

그 다음에 이 질문에 답변이 몇 개까지 있는지 계산했습니다.

const endIdx = qNum.a.length-1;

이 endIdx가 각 질문에 선택지가 몇 개까지 있는지 담는 것입니다. 이 개수로 for문을 돌리면서 addAnswer(answerTxt, idx) 함수를 호출하는데, 이 addAnswer는 선택지 버튼을 생성하는 함수입니다.

 

addAnswer(answerTxt, idx) 함수:

일단 answer 클래스(이 button이 붙을 부모 요소)를 찾아둡니다. 이는 addAnswer에서 만들 엘리먼트를 자식으로 추가할 부모를 미리 찾아주는 것입니다. 그 다음 button 엘리먼트를 만들고 미리 만들어둔 클래스 스타일을 적용하기 위해 addClass 함수(후술)로 클래스를 발라줍니다. 

answer의 innerHTML(혹은 text)는 answerTxt로 채웁니다. 이 answerTxt는 qNum.a.answer[idx]입니다. idx는 이 선택지가 이 질문에서 몇 번째 선택지인지 알려주는 인덱스입니다.

모든 answer들에는 다 click 이벤트에 대응하는 이벤트 리스너가 붙습니다. 어떤 답변이 선택될지 모르기 때문이죠. 그리고 어떤 답변이 선택되든 한 질문이 끝나면 모든 answer은 다 사라져야 합니다. 사라지지 않으면 유효하지 않은 답변이 계속 자리를 차지하게 되니까요. 그래서 click 이벤트가 발생하면 부모 노드를 찾아 그 부모 노드의 innerHTML을 없애줘야 합니다.

이 모든 게 완료되었다면 click 이벤트 리스너 바깥에 아까 찾아둔 부모 요소의 자식으로 해당 엘리먼트를 appendChild 해줍니다.

 

addClass() 함수:

클래스를 더해주는 함수입니다. jQuery에 있는 addClass 함수를 바닐라 자바스크립트에서도 쓰려고 흉내내서 만든 함수지만 나중에 그냥 지워버렸습니다.

참고로 바닐라 자바스크립트에선 이렇게 하면 됩니다.

function addClass(elem, className) {
    elem.className += " "+className;
}

아니면 classList.add() 해도 됩니다.

 

 

 

 

PC, 태블릿 welcome 화면
모바일 welcome-qna 화면

 

문제점:
1. input 태그의 오른쪽 쏠림
2. answer 내 박스들은 괜찮아졌지만 q box 가 오른쪽으로 쏠림
3. 한 질문에서 답을 연타하거나 복수 개를 선택하면 그 숫자만큼 질문을 건너뛰게 됨. 게다가 넘어간 질문에 그만큼 답변이 배로 불어남
4. 질문 끝까지 가면 다음 화면으로 넘어가지 않고, 마지막 질문도 사라지지 않음

 

 

PC, 태블릿 qna 화면. 오류가 생긴다
질문이 끝나도 calc로 자동으로 넘어가지 않는다
PC, 태블릿 calc 화면
모바일 calc 화면

 

저번에 글씨가 다 안 보이던 문제를 모바일에서 font-size를 조정하는 것으로 해결

 

 

PC, 태블릿 result 화면
모바일 result 화면

 

코드 보기

 

 

6일

삽질할 줄 알았던 자바스크립트 작업이 의외로 싱겁고 쉽게 끝났습니다.. ㅎㅎ

5일에 하나 안 되던 게 있었습니다. 처음에는 a 박스들이 모두 부드럽게 사라지는데 두 번째 질문부터는 그렇지 않고 그냥 뚝 정지돼 있다가 갑자기 사라지는 현상이었습니다. fade out 효과가 안 들어간 거죠. 그리고 만약 버튼을 더블 클릭하거나 버튼 하나를 누르고 없어지기 전에 재빨리 다른 버튼을 누르면 한 질문을 건너뛰게 되고 선택지도 1질문과 2질문 선택지가 한 페이지에 연이어서 나왔습니다. 설마 누가 이럴까 싶긴 했지만 꽤 치명적인 오류이므로 그걸 고쳐보려 했습니다.

 

작은 문제 1 : 트위터 공유 링크에 해시태그인 '#'이 들어가지 않았습니다. url 인코딩하는 부분에서 뭔가 문제가 생긴 것 같았습니다. 그 뒷부분부터 문자열들이 쭉 잘렸습니다.

해결 : '#'이 %23으로 인코딩됨. %23을 써주면 됨

 

작은 문제 2 : 태블릿과 PC에서 헤더 fix를 위해 welcome 컨테이너에 margin-top을 115px, 150px 줬는데 이게 qna 페이지까지 그대로 넘어가서 윗부분이 너무 많이 남아서 보기에 별로 안 좋았습니다.

해결 : 후술

 

임시로 트위터와 페이스북 로고에 공유 링크를 달았습니다.

 

 

data.js

qnaList 외에 객체 배열을 하나 더 만들었습니다. 결과를 나타내는 infoList를 만들었는데 여긴 점수 범위인 from과 to, mLeft(점수 바에서 핀이 갖는 왼쪽 마진값), 결과 동물의 이름인 name, 설명인 desc가 있습니다.

 

 

main.js

begin() 함수:

결국 goNext()에 있던 qna.display = 'block'을 begin 함수로 다시 데려왔습니다. goNext() 직전에 실행되는 명령입니다.

여기서 사용자의 기기가 태블릿인지 PC인지 검사해서 margin-top 값을 조정할 수 있도록 했습니다.

pcMQL.matches 와 tabletMQL.matches 를 쓰면 사용자의 기기 종류를 검사할 수 있습니다.

 

goNext() 함수:

진행 바의 퍼센테이지를 10%씩 늘려나가기 위해 질문 갯수와 qIdx 갯수를 적당히 곱해 바꿔 나갔습니다. STATUS 로 되어 있던 전역 변수 이름이 status 변수와 겹친다는 뒤늦게 알아차리고 STATUS를 ENDPOINT로 이름을 바꿨습니다. 

전에 addAnswer 함수에서 삽질을 했는데요, 부모 클래스를 선택해서 그 length로 자식들 애니메이션을 줬는데 개발자 도구 켜서 봐도 변하는 게 없었고 두 번째부터는 어김없이 그냥 사라지는 문제 때문이었습니다.

그래서 이 날은 아예 click 이벤트 리스너 안에서 미리 찾아둔 answer 클래스 엘리먼트가 아니라 지금 button의 parentNode로 접근했습니다. 그래서 그 childrenNodes들을 잡아서 그 length로 for 문을 돌려서 한 개가 클릭되면 그걸 포함해서 모든 childrenNode들이 다 disable이 되게 했습니다. 이렇게 해서 중복 클릭돼서 겪는 오류를 미연에 방지할 수 있었습니다. (button 태그의 유익함)

하지만 여전히 애니메이션은 좀 뻣뻣했습니다. 나중에 해결보기로 했습니다.

 

end() 함수:

goNext에서 ENDPOINT와 qIdx-1 이 맞아떨어지면(처음에 전역변수 qIdx를 -1로 초기화함) 호출되는 함수입니다.

qna.style.animation = ''을 했는데 이거 이렇게 하는 게 아니더군요 ㅋㅋㅋㅋㅋㅋ 나중에 해결법을 찾았습니다.

이 때까지(글을 쓰는 지금인 4월 22일 21시 42분까지도) status bar와 q box에는 애니메이션이 적용되지 않는데 그래도 goNext()에서 end()로 가려면 진행 바와 질문 박스가 갑자기 사라져서 찬물 끼얹는 일은 없어야 했기에 꼼수를 썼습니다. setInterval을 만들어서 opacity를 0.05초에 0.1씩 줄여서 0.5초에 걸쳐 0이 되도록 했습니다. 그리고 clearTimeout으로 이 setInterval 함수의 실행 시간을 제한했습니다. 이걸 제대로 제한을 안 하고 그냥 했더니 opacity가 음수로 한참 떨어져서 clearTimeout이 꼭 필요했습니다.

setTimeout으로 qna.display: none을 해주고 calc.display: block을 했습니다. 그리고 9초짜리 setTimeout을 하나 더 뒀습니다. 어차피 calc 페이지에서는 사용자가 조작하는 게 없기 때문에 그냥 9초 고정으로 setTimeout을 둬서 자연스럽게 사라지는 애니메이션을 부여하고 display: none이 될 수 있도록 했습니다. 그 다음 goResult() 함수가 호출됩니다.

 

goResult() 함수:

여기서는 header와 footer가 다시 block이 되고 result도 block이 되어 welcome 페이지와 비슷한 모습으로 나타납니다. 

여기서는 begin()에서 줄여놨던 margin-top 값을 다시 되돌려놓습니다. (헤더 자리를 마련해줘야 하니까)

그런 다음 점수를 계산하는 calcScore 함수를 호출하고 grade(이게 등급은 아니지만 좋은 변수명이 생각나지 않았음)를 구하기 위해 다시 그 점수를 매개변수로 하는 sortResult 함수를 호출합니다.

뭐 나머지는 뻔합니다. 아까 처음에 받은 답변자 이름과 점수를 넣고, 상태바에서 핀의 위치를 정하고 결과 이미지 다음에 들어갈 설명의 innerHTML에 결과를 넣는 것입니다.

 

calcScore과 sortResult는 너무 쉬운 함수라서 생략합니다.

 

 

 

모바일 화면

 

5일차에 발생한 문제 해결
1. 쏠린 input 태그를 마진값 조절로 가운데로 위치시킴
2. q box도 마진값과 width 조절로 가운데로 위치시킴
3. 답변이 클릭되면 a 버튼들이 모두 disabled됨
4. calc의 font-size 되돌아옴

문제
a box에 적용한 애니메이션은 1번 질문 이후로는 적용되지 않음

 

 

 

PC, 태블릿 qna 화면

 

PC, 태블릿 calc 화면

 

PC, 태블릿 result 화면

코드 보기

 

 

7일

월요일에 드럼 치고 이거저거 할 일이 좀 생겨서 많이는 못 했습니다.

소셜 공유를 할 수 있게 공유하는 링크를 만들었습니다.

 

카카오톡과 카카오스토리 공유를 하려고 했지만 슬래시('/')가 url에 들어간 링크는 유효한 호스트 url이 아니라면서 거절당하는 바람에 못 했습니다(참고로 이건 깃허브 페이지이기 때문에 제 깃허브/레파지토리 이름 식의 url입니다. 슬래시가 필연적으로 들어갈 수밖에 없는 링크라 등록이 불가능합니다). 입맛이 씁니다

 

트위터, 페이스북, 네이버, 네이버 밴드 총 4개에 공유할 수 있게 만들었습니다.

 

 

 

 

모바일 화면

 

 

 

PC, 태블릿 welcome 화면

 

PC, 태블릿 qna 화면

 

PC, 태블릿 calc 화면 

 

PC, 태블릿 result 화면

 

 

 

소셜 공유 화면

 

코드 보기

 

 

다음 글 보기

 

[이미지테스트 페이지 만들기] 8~9일 - 다크 모드 지원, 토글 버튼, 색상 정리

나를 알아보는 10가지 질문 사람들이 보는 나는 어떤 이미지일까? dev-dain.github.io 다크 모드 지원을 할 수 있게 만들어봤습니다. 얼마 전 geekNews에서 한 줄로 다크 모드 지원을 할 수 있는 코드를 ��

dev-dain.tistory.com

 

728x90
반응형
Comments