Created
July 17, 2017 01:02
-
-
Save sokcuri/7ef7202af89589d197fee5ae85cf9eec to your computer and use it in GitHub Desktop.
우아한테크캠프 Tab UI Refactoring 과제
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
height: 100%; | |
} | |
body { | |
font-family: "Spoqa Han Sans"; | |
} | |
body, header, nav, section, div, footer, ul, dd { | |
margin: 0; | |
padding: 0; | |
} | |
li { | |
list-style: none; | |
} | |
dt { | |
font-weight: bold; | |
font-size: 1.2em; | |
margin-bottom: 5px; | |
} | |
.tab * { | |
pointer-events: none; | |
} | |
dl { | |
float: left; | |
width : 75%; | |
} | |
dd { | |
font-size: 0.9em; | |
} | |
#wrapper { | |
min-height: 100%; | |
position: relative; | |
} | |
.infoArea { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
width: 160px; | |
height: 130px; | |
background: rgba(255, 255, 255, 0.6); | |
border-radius: 3%; | |
background: url(https://raw.githubusercontent.com/sokcuri/StoreBox/master/woowahan_techcamp.png); | |
background-size: contain; | |
background-repeat: no-repeat; | |
} | |
.headerArea { | |
height:200px; | |
text-align: center; | |
padding-top: 20px; | |
line-height: 1.4em; | |
background: url(https://i.ytimg.com/vi/nRLsWw1-v94/maxresdefault.jpg); | |
background-size: cover; | |
} | |
.profilePic { | |
display: inline-block; | |
width: 100px; | |
height: 100px; | |
border: 3px solid white; | |
border-radius: 100%; | |
background: url(https://avatars1.githubusercontent.com/u/1456761?v=3&s=140); | |
background-size: contain; | |
} | |
.userId { | |
font-size: 1.2em; | |
font-weight: bold; | |
line-height: 2rem; | |
} | |
.userId > span { | |
border-bottom: 1px dashed #ddd; | |
text-shadow: 1px 1px #dedede; | |
line-height: 10px; | |
} | |
.userMessage, .userSNSInfo { | |
font-size: 0.9em; | |
color: seashell; | |
text-shadow: -1px -1px 0 #333, | |
1px -1px 0 #000, | |
-1px 1px 0 #555, | |
1px 1px 0 black; | |
} | |
.userSNSInfo { | |
display: inline-block; | |
overflow: auto; | |
margin: 0px auto; | |
} | |
.userSNSInfo > li { | |
float: left; | |
margin-right: 5px; | |
} | |
.userSNSInfo span.count { | |
color : chartreuse; | |
} | |
.mainView { | |
margin: 0 auto; | |
min-height: 600px; | |
} | |
.mainView > nav { | |
height:42px; | |
font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, Sans-Serif; | |
padding-left: 8%; | |
padding-right: 8%; | |
background: #3d3d3e; | |
} | |
.tabArea ul { | |
padding: 0; | |
} | |
/* Tab 선택시에만 badge를 볼 수 있게 */ | |
.tabArea .badge { | |
display: none; | |
} | |
.tabArea .selectedTab .badge { | |
display: block; | |
width: 24px; | |
height: 24px; | |
position: absolute; | |
background: #9e9e9e; | |
color: white; | |
font-weight: bold; | |
border-radius: 100px; | |
/* border: 2px solid #5a5a5a; */ | |
z-index: 1; | |
top: -10px; | |
right: 8px; | |
font-size: 0.6rem; | |
line-height: 23px; | |
} | |
.tabArea ::selection { | |
background: transpaent; | |
color: inherit; | |
text-shadow: none; | |
} | |
.tabArea section { | |
position: relative; | |
float: left; | |
text-align: center; | |
list-style: none; | |
color: white; | |
padding: 8px; | |
height: 100%; | |
box-sizing: border-box; | |
cursor: pointer; | |
} | |
.tabArea section:hover { | |
background-color: #888; | |
} | |
.tabArea section.selectedTab { | |
background-color: #ccc; | |
color: black; | |
} | |
.tabArea span { | |
overflow: hidden; | |
display: block; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
font-size: 0.95rem; | |
} | |
.tabArea .selectedTab span:after { | |
content: ""; | |
position: absolute; | |
top: 100%; | |
left: 50%; | |
border-top: 16px solid #ccc; | |
border-left: 16px solid transparent; | |
border-right: 16px solid transparent; | |
margin-left: -16px; | |
} | |
/* Can CSS detect the number of children an element has? */ | |
/* https://stackoverflow.com/questions/8720931/can-css-detect-the-number-of-children-an-element-has */ | |
/* one item */ | |
.tabArea section:first-child:nth-last-child(1) { | |
width: 100%; | |
} | |
/* two items */ | |
.tabArea section:first-child:nth-last-child(2), | |
.tabArea section:first-child:nth-last-child(2) ~ section { | |
width: 50%; | |
} | |
/* three items */ | |
.tabArea section:first-child:nth-last-child(3), | |
.tabArea section:first-child:nth-last-child(3) ~ section { | |
width: 33.3333%; | |
} | |
/* four items */ | |
.tabArea section:first-child:nth-last-child(4), | |
.tabArea section:first-child:nth-last-child(4) ~ section { | |
width: 25%; | |
} | |
/* five items */ | |
.tabArea section:first-child:nth-last-child(5), | |
.tabArea section:first-child:nth-last-child(5) ~ section { | |
width: 20%; | |
} | |
/* six items */ | |
.tabArea section:first-child:nth-last-child(6), | |
.tabArea section:first-child:nth-last-child(6) ~ section { | |
width: 16.6667%; | |
} | |
/* seven items */ | |
.tabArea section:first-child:nth-last-child(7), | |
.tabArea section:first-child:nth-last-child(7) ~ section { | |
width: 14.2857%; | |
} | |
/* eight items */ | |
.tabArea section:first-child:nth-last-child(8), | |
.tabArea section:first-child:nth-last-child(8) ~ section { | |
width: 12.5%; | |
} | |
.tab:last-child { | |
border-right: 0px; | |
} | |
.sectionArea > div { | |
display:none; | |
padding:8%; | |
line-height: 1.5em; | |
} | |
.sectionArea > div.eleDisplayShow { | |
display: block; | |
padding: 30px 8%; | |
line-height: 1.5em; | |
} | |
.myName { | |
font-size: 1.2em; | |
font-weight: bold; | |
} | |
.myDesc { | |
font-size: 0.8em; | |
} | |
.eleDisplayShow li { | |
margin-bottom: 8%; | |
} | |
.sectionArea .pageHeader { | |
padding-bottom: 9px; | |
margin: 40px 0 20px; | |
border-bottom: 1px solid #ddd; | |
} | |
.sectionArea h1:after, .sectionArea h2:after, .sectionArea h3:after { | |
background-color: #1f5c99; | |
bottom: 0; | |
content: ""; | |
margin-top: 10px; | |
margin-bottom: -1px; | |
display: block; | |
width: 50px; | |
height: 3px; | |
} | |
.sectionArea h1, .sectionArea h2, .sectionArea h3 { | |
font-family: "Open Sans", Helvetica, Arial, sans-serif; | |
margin-bottom: -10px; | |
color: #555555; | |
} | |
.sectionArea h3 { | |
font-weight: 400; | |
} | |
.sectionArea ::selection { | |
background: #269ccb; | |
color: #fff; | |
} | |
.footerView { | |
position: absolute; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
background: #3d3d3e; | |
color: white; | |
text-align: right; | |
font-size: 0.7rem; | |
z-index: 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, user-scalable=no"> | |
<title>nigayo -- WoowaTechCamp</title> | |
<link rel="stylesheet" href="http://spoqa.github.io/spoqa-han-sans/css/SpoqaHanSans-kr.css"> | |
<link rel="stylesheet" href="https://spoqa.github.io/spoqa-han-sans/css/SpoqaHanSans-kr.css"> | |
<link rel="stylesheet" href="./tabUI.css"> | |
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js"></script> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js"></script> | |
<script src=./tabUI.js></script> | |
</head> | |
<body> | |
<div id="wrapper"> | |
<div class="infoView"> | |
<div class="infoArea"> | |
</div> | |
</div> | |
<div class="mainView"> | |
<header class="headerArea"> | |
<div class="profilePic"></div> | |
<div class="userId"> | |
<span>nigayo</span> | |
</div> | |
<div class="userMessage"><span>안녕하세요 nigayo입니다.</span> | |
</div> | |
<ul class="userSNSInfo"> | |
<li><span>review : </span><span class="count">10</span><span> | </span> | |
</li> | |
<li><span>follower : </span><span class="count"><span >12</span><span> </span></span><span > | </span> | |
</li> | |
<li><span>following : </span><span class="count"><span >30</span><span> </span></span><span > </span> | |
</li> | |
</ul> | |
</header> | |
<nav class="tabArea"></nav> | |
<main class="sectionArea"></main> | |
</div> | |
<footer class="footerView">original by nigayo, modified by sokcuri. </footer> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Template = { | |
NavigateTab: `<div class="badge">{{clickCount}}</div><div class="content" id="{{id}}"><span>{{text}}</span></div>`, | |
Section: `<div class="pageHeader"><h3>{{title}}</h3></div><p>{{content}}</p>` | |
} | |
class NavigateTab { | |
constructor(id, text, selected) { | |
this.id = id; | |
this.text = text; | |
this.clickCount = 0; | |
this.node = document.createElement('section'); | |
this.node.classList.add('tab'); | |
this.node.addEventListener('click', (e) => { | |
this.clickCount++; | |
this.makeNode(); | |
}); | |
if (selected) { | |
this.clickCount++; | |
this.node.classList.add('selectedTab'); | |
} | |
} | |
getObject() { | |
return { | |
id: this.id, | |
text: this.text, | |
clickCount: this.clickCount | |
} | |
} | |
makeNode() { | |
this.node.innerHTML = Handlebars.compile(Template.NavigateTab)(this.getObject()); | |
} | |
getNode() { | |
this.makeNode(); | |
return this.node; | |
} | |
} | |
Object.defineProperty(NavigateTab.prototype, 'conflict', { | |
get: function() { return 0xdeadbeef; } | |
}); | |
document.addEventListener('DOMContentLoaded', function () { | |
tabInitialize(); | |
registerEvents(); | |
loadContents(); | |
}); | |
function tabInitialize() { | |
window.tabList = []; | |
let tabArea = document.querySelector('.tabArea'); | |
tabArea.innerHTML = ''; | |
let tabs = [ | |
['position', 'About me'], | |
['news', 'News'], | |
['following', 'Following'], | |
['follower', 'Follower'], | |
['comment', 'Comment'] | |
]; | |
for (let tab of tabs) { | |
let firstElement = (tabs[0] === tab); | |
let tabObj = new NavigateTab(tab[0], tab[1], firstElement); | |
tabArea.appendChild(tabObj.getNode()); | |
window.tabList.push(tabObj); | |
} | |
} | |
function registerEvents() { | |
setHandlerTab(); | |
} | |
function loadContents() { | |
let el = document.querySelector('nav > section'); | |
tabClickDelegate({target: el}); | |
} | |
function setHandlerTab() { | |
let nav = document.querySelector('nav'); | |
nav.addEventListener('click', tabClickDelegate); | |
} | |
// 클릭한 탭에 색이 들어가고 탭에 해당하는 섹션이 보이도록 조정 | |
function tabClickDelegate(evt) { | |
let sectionArea = document.querySelector('.sectionArea'); | |
let tabElement = evt.target; | |
let id = evt.target.querySelector('.content').id; | |
let sectionElement = document.querySelector(`.sectionArea .${id}`); | |
// class 이름에 해당하는 섹션 id가 없는경우 만들어준다 | |
if (!sectionElement) { | |
sectionElement = document.createElement('div'); | |
sectionElement.classList.add(id); | |
sectionArea.appendChild(sectionElement) | |
} | |
const visibleControl = (el, name) => { | |
let prev_el = document.querySelector(`.${name}`); | |
if (prev_el) | |
prev_el.classList.remove(name); | |
el.classList.add(name); | |
} | |
visibleControl(tabElement, 'selectedTab'); | |
visibleControl(sectionElement, 'eleDisplayShow'); | |
// 이미 서버에서 받아온 정보가 있으면 리턴 | |
if (sectionElement.dataset.receivedData) | |
return; | |
sectionElement.dataset.receivedData = true; | |
const getJsonURL = n => { | |
let protocol = (document.location.href.indexOf('file:') === 0) ? 'http:' : ''; | |
return `${protocol}//jsonplaceholder.typicode.com/posts/${n}`; | |
}; | |
const getTabNumber = el => { | |
let navTabs = Array.from(document.querySelectorAll('nav > .tab')); | |
for (let i = 0; i < navTabs.length; i++) { | |
if (navTabs[i] == el) | |
return i + 1; | |
} | |
} | |
// 탭에 컨텐츠를 채워넣는다 | |
let number = getTabNumber(tabElement); | |
requestXHR(getJsonURL(number), function() { | |
return updateSection(sectionElement, JSON.parse(this.responseText)); | |
}); | |
} | |
// 컨텐츠를 요청. XHR | |
function requestXHR(url, callback) { | |
let oReq = new XMLHttpRequest(); | |
oReq.addEventListener('load', callback); | |
oReq.open('GET', url); | |
oReq.send(); | |
} | |
// 섹션을 업데이트 | |
function updateSection(sectionElement, obj) { | |
let result = Handlebars.compile(Template.Section)({title: obj.title, content: obj.body}); | |
sectionElement.innerHTML = result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preview Site