Skip to content

Instantly share code, notes, and snippets.

@sokcuri
Created July 17, 2017 01:02
Show Gist options
  • Save sokcuri/7ef7202af89589d197fee5ae85cf9eec to your computer and use it in GitHub Desktop.
Save sokcuri/7ef7202af89589d197fee5ae85cf9eec to your computer and use it in GitHub Desktop.
우아한테크캠프 Tab UI Refactoring 과제
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;
}
<!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.&nbsp;</footer>
</div>
</body>
</html>
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;
}
@sokcuri
Copy link
Author

sokcuri commented Jul 17, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment