// desktop /* 최소 1024를잡고 작업한다.*/
@media (max-width: 1200px) {
.header .inner {width: 90%;}
.footer .inner {width: 90%;}
}
// tablet /* 최소 768을잡고 작업한다. */
@media (max-width: 1023px) {
.header .inner {flex-wrap: wrap;}
.header .group-logo {padding: 15px 0;}
.header .group-gnb { order: 3; width: 100%;}
.footer {position: relative;}
.footer .footer-top {flex-direction: column;}
.footer .footer-top .logo {width: 145px; height: 52px;}
.footer .footer-top .corp-area {margin: 16px auto 0;}
.footer .footer-bottom {flex-direction: column; border: none;}
.footer .footer-bottom .addr {order: 2; text-align: center; width: 100%; word-break: keep-all; margin-top: 25px;}
.footer .footer-bottom .select-area {order: 1; width: 100%;}
.footer .footer-top .sns-area {position: absolute; left: 50%; bottom: 30px; transform: translateX(-50%);}
}
// mobile /* 최소 320을잡고 작업한다. */
@media (max-width: 767px) {
.header .gnb-item:not(.mo) {display: none;}
.header .gnb-item:nth-child(1) {display: block;}
.header .group-util a:not(.mo) { display: none; }
.header .link-join {display: block;}
.footer .footer-top .corp-list {gap: 9px;}
.footer .footer-top .corp-item {font-size: 14px;}
}
반응형은 항상 최소를 잡고 작업을 해야 깨지지않는다.
그리고 완성한 상태에서 반응형 작업이 들어가야 수월하다.
미디어 쿼리 구문은 '미디어 유형/ 논리 연산자/ 특성'으로 이루어지며 대소문자를 구분하지 않는다.
// Break Point
$mobile: 767px;
$tablet: 1023px;
$desktop: 1200px;
// Mixins
@mixin mobile {
@media (min-width: #{$mobile}) {
@content;
}
}
@mixin tablet {
@media (min-width: #{$tablet}) {
@content;
}
}
@mixin desktop {
@media (min-width: #{$desktop}) {
@content;
}
}
분기점은 변수로 만들고, @content로 내용이 비워져있는 믹스인을 작성한다.
// SCSS
.logo {
width: 20px;
@include desktop {
width: 40px;
}
}
@include로 불러와서 사용하면된다.
fetch('./assets/data/menu.json')
.then((response) => response.json())
.then((json) => {
data = json.items;
let html = '';
data.forEach(el => {
let html2 = '';
if(el.children != null) {
el.children.forEach(sub => {
html2+=`<li class="sub-item"><a href="/sub/${sub.id}">${sub.name}</a></li>`
});
child =`<ul class="sub-list">${html2}</ul>`;
} else {
child = '';
}
html+=`<li class="lnb-item"><a href="/sub/${el.id}">${el.name}</a>${child}</li>`
});
$('.lnb-list').html(html);
});
fetch()를 이용해 데이터를 불러온 뒤 data변수에 데이터를 담아 저장한다.
그리고 html변수에 완성된 코드를 넣을 수 있는 빈문자를 담아둔다.
데이터의 객체수만큼 반복문으로 돌리고 만약 data에 children이 null이면 빈문자열에서 종료되고,
아니라면 children이 가지고 있는 객체만큼 또 반복문을 돌린다.
html2는 sub-item을 담을 빈문자 변수이며 여기에 sub-item코드를 넣고 데이터를 불러올 자리에 id, name값을 넣어준다.
반복이 끝나면 child변수 sub-list코드안에 데이터가 들어간 html2을 넣어주고
빈문자열이었던 html변수 lnb-item코드안에 데이터를 불러올 자리에 id, name값을 넣고,
데이터가 다 들어간 child도 넣어주면 된다.
마지막으로 lnb-list클래스에 html()메서드를 통해 html변수를 넣어주면 코드가 완성된다.
개발자도구로 확인하면 코드가 들어간걸 확인 할 수있다.
fetch(url, [option])
// 함수선언식으로 바꾸면
fetch('./assets/data/menu.json')
.then(function(response) {
// 데이터 성공적으로 불러왔을때, 응답을 json 형식으로 반환한다.
return response.json();
})
.then(function(json) {
// 데이터를 응답받은 후의 로직
console.log(json)
});
// Output
{
"items" : [
{
"id":0,
"name" : "전체"
},
{
"id":1,
"name" : "개발",
"children" : [
{"id":101, "name" : "개발 전체"},
{"id":102, "name" : "웹 개발자"},
{"id":103, "name" : "서버 개발자"},
{"id":104, "name" : "소프트웨어 엔지니어"},
{"id":105, "name" : "프론트엔드 개발자"},
{"id":106, "name" : "자바 개발자"},
{"id":107, "name" : "안드로이드 개발자"},
{"id":108, "name" : "C,C++ 개발자"},
{"id":109, "name" : "iOS 개발자"},
{"id":110, "name" : "파이썬 개발자"},
{"id":111, "name" : "데이터 엔지니어"},
{"id":112, "name" : "Node.js 개발자"},
{"id":113, "name" : "DevOps / 시스템 관리자"},
{"id":114, "name" : "시스템,네트워크 관리자"},
{"id":115, "name" : "머신러닝 엔지니어"},
{"id":116, "name" : "개발 매니저"},
{"id":117, "name" : "데이터 사이언티스트"},
{"id":118, "name" : "기술지원"},
{"id":119, "name" : "빅데이터 엔지니어"},
{"id":120, "name" : "QA,테스트 엔지니어"},
{"id":121, "name" : "보안 엔지니어"},
{"id":122, "name" : "프로덕트 매니저"},
{"id":123, "name" : "임베디드 개발자"},
{"id":124, "name" : "블록체인 플랫폼 엔지니어"},
{"id":125, "name" : "PHP 개발자"},
{"id":126, "name" : "하드웨어 엔지니어"},
{"id":127, "name" : ".NET 개발자"},
{"id":128, "name" : "DBA"},
{"id":129, "name" : "웹 퍼블리셔"},
{"id":130, "name" : "영상,음성 엔지니어"},
{"id":131, "name" : "크로스플랫폼 앱 개발자"},
{"id":132, "name" : "그래픽스 엔지니어"},
{"id":133, "name" : "CTO,Chief Technology Officer"},
{"id":134, "name" : "ERP전문가"},
{"id":135, "name" : "BI 엔지니어"},
{"id":136, "name" : "VR 엔지니어"},
{"id":137, "name" : "루비온레일즈 개발자"},
{"id":138, "name" : "CIO,Chief Information Officer"}
]
},
...
}
필요할 때 서버에 네트워크 요청을 보내고 새로운 정보를 받아오는 일을 할 수 있다.
$('.btn-lnb').mouseover(function(e){
e.preventDefault();
$('.group-lnb').addClass('active');
$('.lnb-item').find('.sub-list').hide();
$('body').css({overflow: 'hidden'})
})
$('.lnb-list').mouseleave(function(e){
e.preventDefault();
$('.group-lnb').removeClass('active');
$('body').css({overflow: 'auto'})
});
$('.lnb-item').mouseover(function(e){
e.preventDefault();
$('.lnb-item').find('.sub-list').hide();
$(this).find('.sub-list').show();
$('body').css({overflow: 'hidden'})
});
서브메뉴가 많아 스크롤을 적용했는데 스크롤이 끝나면 뒤에 body까지 같이 스크롤이 되서
메뉴를 빠져나가면 다시 컨텐츠를 보러 올리거나하는 불편함이 있었다.
body를 막는건 쉽게 처리할 수 있었다.
body에 overflow: hidden을 주면 스크롤되지않는 걸 확인할 수 있다.
마우스 오버됐을 때 body에 overflow:hidden을 줘서 스크롤이 되지않게 막고
마우스가 떠났을 때는 overflow: auto로 다시 스크롤이 되게 구현하였다.