제작기간 : 23.03.20 ~ 23.03.23 (4일)
사용 : HTML, CSS, JSON
분류 : 클론코딩, 데이터 바인딩, tablet / mobile
⭐카카오페이지 json 데이터 파일을 생성하고, JavaScript를 사용해 html에 데이터 바인딩을 했습니다.⭐
- index.html : 마크업
- asset
- css
- common : 공통 소스
- fonts : 폰트
- layout : 레이아웃
- main : 메인 페이지
- reset : 리셋
- data : JSON 데이터
- bookData
- eventData
- mainBanner
- productData
- rankData
- fonts : 폰트파일
- images : 이미지파일
- js
- main : 메인 스크립트
Data Binding : 뷰와 모델을 묶어서 서로 간의 데이터를 동기화하는(일치시키는) 것
- 뷰(View) : 사용자에게 보이는 부분(화면상에 보여지는 데이터)
- 모델(Model) : 데이터를 관리하는 영역(브라우저 메모리에 있는 데이터)
JSON (JavaScript Object Notation)은 구조화된 데이터를 Javascript 객체 문법으로 표현하기 위한 문자 기반의 표준 포맷
JavaScript의 object(객체)와 같이 key(속성)-value(값)가 묶여진(property) 표기법을 사용
{
"items":[
{
"name":"lala",
"char":["fat","cute"],
"age":3
},
{
"name":"lulu",
"char":"lazy",
"age":5
}
]
}
*API (Application Programming Interface) : 응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스
📌html
<body>
<ul class="list1">
<!-- content -->
</ul>
<ul class="list2">
<!-- content -->
</ul>
<ul class="list3">
<!-- content -->
</ul>
<ul class="list4">
<!-- content -->
</ul>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</body>
📌json
{
"items":[
{
"name":"철수",
"age":20,
"sort":"돈이많음"
},
{
"name":"영수",
"age":10,
"sort":"가난"
},
{
"name":"만수",
"age":40,
"sort":"돈이많음,게으름"
},
{
"name":"정수",
"age":120,
"sort":"천재,게으름,돈이많음"
}
]
}
📌js
<script>
// 값을 일일이 넣지 않고 함수를 쓰면 편하다!
function humanList(sortData,frame){
fetch('./human.json') // 통신할 데이터파일의 경로 넣기
.then(res=>res.json()) // response로 json을 받아오기
.then(json=>{ // 받아온 json을 실행 -> 데이터에 접근 가능!
allData = json.items; // json 뒤에 .items를 붙이면 바로 array에 접근 가능
// resultData = allData.filter(function(data){
// return data.age >= 20;
// })
resultData = allData.filter(function(data){ // 필터링 하기
return data.sort.indexOf(sortData) >= 0
// indexOf() : 원하는 문자열이 있으면 0 이상을, 없으면 -1을 반환
// 즉 반환값이 0 이상이면 원하는 문자열을 포함한 문자열인 것!
// 이 조건에 해당하는 값만 반환
});
let html = '';
resultData.forEach(element => {
// array를 반복 돌리기. element로 배열 하나하나에 접근 가능
age = element.age < 20 ? '미성년자':'성인';
// 조건 연산자 `?`를 이용해 간단히 썼음.
// 의미는 아래 조건문과 동일
// if(element.age < 20){
// age = "미성년자";
// }else{
// age = "성인";
// }
html+=`<li>
${element.name}
나이:${age}
특징:${element.sort}
</li>`;
});
// 만들어 둔 빈 html에 `+=` 으로 더하기 할당.
// element 뒤에 .sort를 붙여서 sort 속성의 값을 불러옴
$(frame).html(html);
// .html() : 선택한 요소 안의 내용을 가져오거나, 다른 내용으로 바꿈.
// 불러온 json 데이터를 화면에 뿌려줌
});
};
/*
* @param : sortData
* @param : frame
*/
humanList('게으름','.list1'); // list1에 게으름 데이터를 나오게 해라
humanList('천재','.list2');
humanList('돈이많음','.list3');
humanList('가난','.list4');
</script>
📌json
{
"items":[
{
"id":1,
"thumb":"./assets/images/wait3-webtoon-1.png",
"title":{
"name":"호랑낭자뎐",
"img":"./assets/images/wait3-webtoon-1-2.png"
},
"snippet":{
"wait3":true,
"wait":false,
"event":false,
"new":true,
"free":false,
"up":false,
"age":0,
"info":"10.2만"
},
"sort":"1,5" // 두 영역에 동시에 존재함
},
...
]
}
📌js
/**
* @작품리스트
*/
function productList(sortNum,frame,type){
fetch('./assets/data/productData.json')
.then(res=>res.json())
.then(json=>{
allData=json.items;
result = allData.filter(function(data){
return data.sort.indexOf(sortNum) >= 0;
});
let html='';
result.forEach(element => {
titleEl = (type === 1)?`<img src="${element.title.img}" alt="${element.title.name}">`:element.title.name
wait3El=(element.snippet.wait3)?`<img src="./assets/images/badge-wait3.svg" alt="3시간마다 무료">`:'';
// wait3가 존재하면 파일이 담긴 img 태그를, 존재하지 않으면 빈값을 변수에 담아라
waitEl=(element.snippet.wait)?`<img src="./assets/images/badge-wait.svg" alt="기다리면 무료">`:'';
eventEl=(element.snippet.event)?`<img src="./assets/images/badge-event.svg" alt="이벤트">`:'';
newEl=(element.snippet.new)?`<img src="./assets/images/badge-new.svg" alt="신작">`:'';
freeEl=(element.snippet.free)?`<img src="./assets/images/badge-free.svg" alt="연재무료">`:'';
upEl=(element.snippet.up)?`<img src="./assets/images/badge-up.svg" alt="새회차 업로드">`:'';
switch (element.snippet.age) {
// switch 문 : 변수의 값과 동일한 값을 갖는 case로 가서 실행문을 실행
case 19:
ageEl=`<img src="./assets/images/badge-19.svg" alt="19세 이용가">`;
break;
case 15:
ageEl=`<img src="./assets/images/badge-15.svg" alt="15세 이용가">`;
break;
default:
ageEl='';
break;
}
switch (element.snippet.theme) {
case 1:
themeEl='웹툰';
break;
case 2:
themeEl='웹소설';
break;
case 3:
themeEl='채팅소설';
break;
default:
themeEl='책';
break;
}
descEl = (type === 1)?element.snippet.info:themeEl
html+=`<li class="swiper-slide">
<a href="">
<div class="img-area">
<div class="badge-box">
<div class="left">
${wait3El} ${waitEl} ${eventEl} ${newEl} ${freeEl} ${upEl}
</div>
<div class="right">
${ageEl}
</div>
</div>
<img src="${element.thumb}" alt>
</div>
<div class="text-area">
<h3 class="title">${titleEl}</h3>
<div class="desc">${descEl}</div>
</div>
</a>
</li>`;
});
$(frame).html(html); // 지정한 frame 아이디값에 가서 html 변수 텍스트를 추가
});
};
/**
* @param sortnum - 틀 영역의 고유한 번호
* @param frame - 틀 영역
* @param designtype - 디자인 모양의 차이
*
* @sort1 = 3다무-3시간마다무료 웹툰
* @sort2 = 3다무-3시간마다무료 웹소설
* @sort3 = 밀리언 페이지
* @sort4 = #영상화까지! 3다무 웹툰
* @sort5 = 신작 웹툰 베스트
*/
productList(1,'#list1',1);
productList(2,'#list2',1);
productList(3,'#list3',2);
productList(4,'#list4',1);
productList(5,'#list5',1);
const prdSlide = new Swiper('.prd-slide',{
slidesPerView: 'auto', // css로 이미지에 일정한 크기를 줬으므로,
// 슬라이드 뷰 개수를 자동으로 했음
spaceBetween:3,
freeMode: true,
});
📌html
<section class="sc-ranking pb16">
<div class="flex-area">
<h2 class="headline">실시간 랭킹</h2>
<a href="" class="btn-all" aria-label="실시간 랭킹 전체보기">
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'> <path fill-rule='evenodd' clip-rule='evenodd' d='M8.5 18.0694L14.6166 12L8.5 5.93058L9.9417 4.5L17.5 12L9.9417 19.5L8.5 18.0694Z' fill='#222222'/> </svg>
</a>
</div>
<div class="flex-area2">
<nav class="group-nav">
<div class="nav">
<!-- data-prd 속성값 #prd1, #prd2로 동일한 아이디값을 가진 영역에 연결 -->
<a href="" data-prd="#prd1" class="active">웹툰</a>
<a href="" data-prd="#prd2">웹소설</a>
</div>
</nav>
<span class="time">2023.00.00 00:00</span>
</div>
<div id="prd1" class="prd active">
<ul class="prd-list" id="rank1">
<!-- content -->
</ul>
</div>
<div id="prd2" class="prd">
<ul class="prd-list" id="rank2">
<!-- content -->
</ul>
</div>
</section>
📌css
.sc-ranking .group-nav a{ /* 처음엔 텍스트만 있음 */
padding: 5px 13px;
font-size: 13px;
color: #222;
}
.sc-ranking .group-nav a.active{ /* active 클래스 추가되면 보더 테두리 넣기 */
border: 1px solid #222;
border-radius: 100px;
}
.sc-ranking .prd{ /* prd 영역은 숨겨져 있음 */
display: none;
}
.sc-ranking .prd.active{ /* active 클래스 추가되면 prd 영역이 나타남 */
display: block;
}
📌js
$('.sc-ranking .group-nav a').click(function(e){
e.preventDefault();
// 이벤트의 기본 동작을 중단.
// a태그의 기본 동작인 링크 이동을 중단하고, 해당 코드 블록 내의 추가적인 동작을 실행
target=$(this).data('prd');
// 클릭한 요소의 data-prd 속성값을 가져와서 target 변수에 담음
$(this).addClass('active').siblings().removeClass('active');
// 클릭한 요소에 active 클래스 추가, 그 형제는 active 클래스 제거
$(target).addClass('active').siblings().removeClass('active');
// target 변수의 값을 가진 요소에 active 클래스 추가, 그 형제는 active 클래스 제거
});
먼저 초기값을 모바일로 둔 뒤, 사이즈를 늘려가면서 조절해야 함.
const mainSlide = new Swiper('.main-slide',{
slidesPerView: 1, // 브라우저 너비가 767px 이하 / 슬라이드 뷰 개수 1개
loop:true,
autoplay: {
delay: 1700,
disableOnInteraction: false
},
pagination:{
el:'.fraction',
type:'fraction'
},
breakpoints:{
767:{ // 브라우저 너비가 767px 초과 / 슬라이드 뷰 개수 1.5개
slidesPerView: 1.5,
spaceBetween:3,
centeredSlides:true, // true일 때 활성 슬라이드가 항상 중앙에 배치 됨
}
}
});
const bookSlide = new Swiper('.book-slide',{
loop:true,
slidesPerView: 1, // 브라우저 너비 767px 이하 / 슬라이드 뷰 개수 1개
autoplay: {
delay: 1700,
disableOnInteraction: false
},
pagination:{
el:'.fraction',
type:'fraction'
},
breakpoints:{
767:{ // 브라우저 너비가 767px 초과 / 슬라이드 뷰 개수 2개
slidesPerView: 2,
spaceBetween:10,
},
}
});
$(function(){ // 1) 윈도우 창을 켰을 때 초기 상태
if(window.innerWidth >= 768) { // 윈도우 창 크기가 768px 이상이면,
bookSlide.disable(); // bookSlide 비활성화
bookSlide.autoplay.disable(); // bookSlide 자동재생 비활성화
}else{ // 768px 미만이면,
bookSlide.enable(); // bookSlide 활성화
bookSlide.autoplay.start(); // bookSlide 자동재생 활성화
}
});
$(window).resize(function(){ // 2) 윈도우 창 크기가 변화할 때
if(window.innerWidth >= 768){
bookSlide.disable();
bookSlide.autoplay.disable();
}else{
bookSlide.enable();
bookSlide.autoplay.start();
}
});
The W3C Markup Validation Service의 Nu Html Checker를 통해 index.html의 마크업이 웹 표준에 적합함을 확인했습니다.