[portfolio] Seoul City Hall 클론코딩

김연빈·2023년 2월 23일
0

portfolio

목록 보기
2/7
post-thumbnail

✔️서울시청 클론코딩

제작기간 : 23.02.18 ~ 23.02.20 (3일)
사용 : HTML, CSS
분류 : 클론코딩, 웹 접근성, PC

파일 구조

  • index.html
  • assets/css/common.css
  • assets/css/layout.css
  • assets/css/main.css
  • assets/css/reset.css
  • assets/images/이미지파일
  • assets/js/main.js

1. WAI - ARIA

  • WAI - ARIA (Web Accessibility Initiative - Accessible Rich Internet Applications)
    페이지를 새로고침 하지 않고도 페이지 영역에 역할, 속성, 상태 정보 추가
    컨텐츠 내용이나 구조가 바뀌는 상황에서 페이지 전환 상태를 WAI - ARIA로 알려줄 수 있음(새로고침 및 링크이동x)
  • 동적 컨텐츠에 원활하게 접근
  • 페이지 접근성 높임

주의사항

  • 올바르지 못한 ARIA를 사용할 바엔 ARIA를 사용하지 않는 편이 좋음
  • ARIA를 사용하기 전에 태그의 역할과 의미에 맞게 작성
  • 태그의 기본 의미를 중복해서 선언할 필요는 없음
  • 페이지에서 사용하는 태그의 역할이 잘못된 ARIA를 선언하면 안 됨

태그별 역할

🔷서울시청 프로젝트 내 WAI - ARIA

<nav class="gnb">
	<ul class="nav-list" role="tablist">
		<li class="nav-item"><a href="" role="tab" class="nav">서울소식</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">시민참여</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">분야별정보</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">서울소개</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">부서안내</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">정보공개</a></li>
		<li class="nav-item"><a href="" role="tab" class="nav">응답소</a></li>
	</ul>
</nav>

<ul class="related-list">
	<li class="related-item">
		<a href="" role="button" class="btn-related none"><span>중앙행정기관</span></a>
	</li>
</ul>

2. window.open / location.href

🔷window.open
자바스크립트 window 객체의 open() 함수로 웹브라우저에서 새창(팝업창)을 열 수 있음

window.open(url, name, windowFeatures);
window.open(팝업창 주소, 팝업창 이름, 팝업창 옵션);

  • url
    새로운 창에 보여질 주소. url 설정을 안 하면 (about:blank)가 뜸.

  • name
    새로 열릴 창의 속성 또는 창의 이름을 지정함. 기본값은 "_blank".
    name(임의의 이름) - 새 창이 열리고 창의 이름을 지정함.

  • windowFeatures
    창 크기, 리사이즈 가능 여부, 스크롤바 사용 여부 등을 설정함.

🔷location.href

  • location.href
    현재 페이지 주소 확인

  • location.href = "이동할 페이지 주소"
    현재 창에서 페이지 이동하기

📌html

<div class="lang-area">
	<select name="#" id="langList" class="select-lang">
		<option value="https://english.seoul.go.kr/?SSid=101_01">ENGLISH</option>
		<option value="https://japanese.seoul.go.kr/?SSid=101_02">日本語</option>
		<option value="https://chinese.seoul.go.kr/?SSid=101_04">简体中文</option>
		<option value="https://tchinese.seoul.go.kr/?SSid=101_03">繁体中文</option>
		<option value="https://world.seoul.go.kr/">WorldWide</option>
	</select>
	<button id="langBtn" class="btn-lang" aria-label="사이트로 새창 이동">GO</button>
</div>

📌js

// 둘 중에 하나 선택해서 사용할 것
$('#langBtn').click(function(){
	const url=$('#langList').val();
	window.open(url);
});

$('#langBtn').click(function(){
	url=$('#langList').val();
	location.href=url;
});

3. Swiper

  • 가장 널리 사용되고 있는 슬라이더 플러그인
  • Swiper 사이트 접속 -> Demos 클릭 -> 다양한 예시 중 원하는 기능의 Core 클릭 / API에서 원하는 기능 찾기 -> 예시 코드 응용해서 사용
  • swiper의 css를 가장 위에다 가져와야 함 - 점수가 가장 낮아야 해서.
    내가 다시 swiper를 꾸밀 때는 가져온 것보다는 쎄야 하니까. 위에 있어야 점수가 낮아짐!
  • swiper의 js는 jQuery의 js 아래에 가져오기

Swiper 불러오기

<head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/7.4.1/swiper-bundle.css">
    <link rel="stylesheet" href="./assets/css/layout.css">
    <link rel="stylesheet" href="./assets/css/main.css">
    <link rel="icon" href="./assets/images/favicon.ico">
</head>

<body>
	<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/7.4.1/swiper-bundle.min.js"></script>
	<script src="./assets/js/main.js"></script>
</body>

Swiper 기본 구조

<div class="swiper mySwiper"> <!-- mySwiper 클래스 이름은 원하는 대로 바꿔도 됨 -->
    <div class="swiper-wrapper">
      <div class="swiper-slide">Slide 1</div>
      <div class="swiper-slide">Slide 2</div>
      <div class="swiper-slide">Slide 3</div>
	  ...
    </div>
</div>

// 순서에 의미가 있다면
<div class="swiper mySwiper">
    <ul class="swiper-wrapper">
      <li class="swiper-slide">Slide 1</li>
      <li class="swiper-slide">Slide 2</li>
      <li class="swiper-slide">Slide 3</li>
	  ...
    </ul>
</div>

4. 슬라이드 재생 / 정지

📌html

<div class="btn-box">
	<button class="btn prev"><span class="blind">이전</span></button>
	<button class="btn autoplay"><span class="blind">자동재생 정지</span></button>
	<button class="btn next"><span class="blind">다음</span></button>
</div>

📌js

const mainSlide = new Swiper('.main-slide',{
    loop:true,
    autoplay: {
        delay: 1700,
        disableOnInteraction: false // 컨트롤 후 작동 유무
    },
    navigation:{
        nextEl:'.next',
        prevEl:'.prev'
    },
    pagination:{
        el:'.fraction',
        type:'fraction'
    },
});
$('.main-slide .autoplay').click(function(){
    if($(this).hasClass('on')){ // 선택한 autoplay에 on 클래스가 있다면 autoplay를 시작
        mainSlide.autoplay.start(); // JavaScript가 아닌 스와이퍼 자체 기능.
    }else{
        mainSlide.autoplay.stop(); // 없다면 autoplay를 멈춤
    }
    $(this).toggleClass('on'); // autoplay는 toggle. on 클래스가 있을 때 누르면 on 클래스가 빠지고 on 클래스가 없을 때 누르면 on 클래스가 들어감.
});

5. 활성화된 버튼과 슬라이드의 연동

📌html

<div class="group-nav">
    <button class="btn-nav news active" data-index="0">주요뉴스</button>
    <button class="btn-nav citizen" data-index="5">시민참여</button>
</div>

📌js

const mainSlide = new Swiper('.main-slide',{
    loop:true,
    autoplay: {
        delay: 1700,
        disableOnInteraction: false
    },
    navigation:{
        nextEl:'.next',
        prevEl:'.prev'
    },
    pagination:{
        el:'.fraction',
        type:'fraction'
    },
    on:{ // 이벤트 핸들러. 특정 요소에서 발생하는 이벤트를 처리
        "slideChange":function(){
		// 현재 슬라이드가 몇 번째인지 알려줌
            if(this.realIndex >= 5){ // 시민참여 슬라이드라면,
            // loop에서 복제된 슬라이드를 고려한 현재 활성 슬라이드의 index
            // activeIndex : 현재 활성 슬라이드의 index
                $('.sc-visual .citizen').addClass('active').siblings().removeClass('active');
              	// .sc-visual .citizen에 active 클래스 추가하고 그 형제는 active 클래스 제거
            }else{ // 주요뉴스 슬라이드라면,
                $('.sc-visual .news').addClass('active').siblings().removeClass('active');
				// .sc-visual .news에 ...
            };
        }
    }
});
$('.sc-visual .group-nav .btn-nav').click(function(){    
    idx=$(this).data('index');
  	// 선택한 index값을 불러옴
    $(this).addClass('active').siblings().removeClass('active');
 	// 클릭한 대상에게 active 클래스 추가하고 그 형제는 active 클래스 제거
    mainSlide.slideToLoop(idx);
  	// 현재 슬라이드를 idx값을 가진 슬라이드로 이동
  	// 기본값은 slideTo이고 loop와 함께 사용하는 경우 slideToLoop를 써야함! (API에서 검색)					
});

6. 키보드 이벤트(keydown)

  • 키보드 이벤트(Keyboard Event)는 사용자가 키를 누르거나 키를 놓을 때 발생함.

    • keydown : 키보드 키를 누를 때 발생
    • keyup : 키보드 키를 누른 후에 놓을 때 발생
  • 키보드 이벤트 객체에는 눌리거나 놓아진 키에 대한 다양한 메타정보가 담겨 있음.
    ex) key 속성에는 키 값이, code 속성에는 코드 값이, keyCode 속성에는 키 번호 값이, shiftKey 속성에는 쉬프트키가 함께 눌렸는지 여부가 저장됨

  • 웹 접근성을 고려하여 tab 키로 원하는 정보에 접근할 수 있게함.

    • 기본적으로 focus를 받는 태그 : a, button, input, select, textarea
  • keycode === 9 : tab 키를 의미(모든 키보드는 넘버링이 있음!)
  • e.shiftkey : shift 키
  • ! : 부정의 의미. !로 shift를 부정함
if(e.keyCode === 9 && !e.shiftKey)

📌js

$('.sc-related .btn-related').click(function(){
    if($(this).hasClass('on')){
      	// 또 눌렀을 때 닫겨야 함. 클릭한 대상에게 on 클래스가 있다면,
        $('.sc-related .btn-related').removeClass('on').siblings('.sub-area').slideUp();
      	// .sc-related .btn-related의 on 클래스를 제거하고 그 형제는 슬라이드를 닫아라
        return false;
      	// 여기에서 스크립트를 끝내라!
    };
    $('.sc-related .btn-related').removeClass('on').siblings('.sub-area').slideUp();
  	// .sc-related .btn-related의 on 클래스를 제거하고 그 형제는 슬라이드를 닫아라
    $(this).addClass('on').siblings('.sub-area').slideDown();
  	// 클릭한 대상에게 on 클래스를 추가하고 그 형제는 슬라이드를 열어라
});

$('.sc-related .sub-list li:first-child').keydown(function(e){
	keyCode = e.keyCode; // e로 이벤트 받기
	if(keyCode === 9 && e.shiftKey){ // keyCode가 9번이고(tab 키), shift 키를 눌렀는지?
		$('.sc-related .btn-related').removeClass('on').siblings('.sub-area').slideUp();
	};
});
$('.sc-related .sub-list li:last-child').keydown(function(e){
	keyCode = e.keyCode;
	if(keyCode === 9 && !e.shiftKey){ // keyCode가 9번이고(tab 키), shift 키를 안 눌렀는지?
		$('.sc-related .btn-related').removeClass('on').siblings('.sub-area').slideUp();
	};
});

📌html

<ul class="related-list">
	<li class="related-item">
		<button class="btn-related"><span>직속기관·사업소</span></button>
       	<!-- 자식 요소가 있는 경우, button태그로 슬라이드 열고닫는 버튼 -->
		<div class="sub-area">
        	<!-- slideUp(), slideDown()은 요소를 각각 display: none, block이 되게 함.
            	 ul의 부모가 없을 때 요소 배치를 위해 ul에 display: flex를 준다면
            	 slideDown()으로 ul이 block이 되어서버려서 flex가 풀림.  -->
          
            <!-- 따라서 ul을 감싸는 부모 .sub-area를 추가해서 
                 .sub-area에 기본값으로 display: none을 주고, ul에 display: flex를 줘야함! -->
			<ul class="sub-list" role="tablist">
				<li><a href="" role="tab">서울시립대학교</a></li>
                ...
        	</ul>
		</div>
	</li>
    <li class="related-item">
    	<a href="" role="button" class="btn-related none"><span>중앙행정기관</span></a>
        <!-- 자식 요소가 없는 경우, a태그로 바로 링크 이동 -->
	</li>
</ul>

📌css

.sc-related .related-item .btn-related:not(.none) span{ 
	/* 글씨 길이와 상관없이 일정한 간격으로 떨어진 화살표를 넣기 위해서 기준이 되는 span을 추가함 */
    position: relative;
    padding-right: 20px;
}
.sc-related .related-item .btn-related:not(.none) span::after{
	/* 화살표를 만들어서 위치시킴 */
    position: absolute;
    top: 8px;
    right: 0px;
    height: 7px;
    width: 7px;
    border-style: solid;
    border-color: #373737;
    border-width: 0px 2px 2px 0px;
    transform: rotate(-135deg);
    /* 화살표 회전 전 각도 */
    transition: all .3s;
    transform-origin: center center;
    content: '';
}
.sc-related .related-item .btn-related.on:not(.none){
    background: #e2e2e2;
}
.sc-related .related-item .btn-related.on:not(.none) span::after{
    top: 4px;
    transform: rotate(45deg);
    /* 화살표 회전 후 각도*/
    transform-origin: center center;
}
.sc-related .sub-area{
	/* slideDown()이 적용되면 display: block으로 바뀜. slideUp()은 다시 none으로 돌아옴 */
    display: none;
    position: absolute;
    bottom: calc(100% - 1px);
    left: 0;
    width: 100%;
    padding: 10px 20px 20px;
    background: #fff;
    border: 1px solid #d0d0d0;
}
.sc-related .sub-area .sub-list{
	/* 자식 요소를 flex로 배치 */
    display: flex;
    flex-wrap: wrap;
}

7. 상단으로 올라가는 버튼

📌html

<div class="fix-btn">
    <button class="btn-top"><span class="blind">상단으로</span></button>
</div>

📌css

.fix-btn{
	/* 버튼을 중심을 기준으로 위치시켰기 때문에, 화면 크기를 조절해도 항상 위치가 똑같음 */
    position: fixed;
    bottom: 98px;
    left: 50%;
    margin-left: 600px;
}
.fix-btn .btn-top{
    /* display: none과 block간의 변화는 transition 효과를 줄 수 없으므로, 
    transition 효과가 필요하면 opacity: 0과 visibility: hidden으로 숨긴 뒤 
    opacity: 1과 visibility: visible로 보이게 해야함! */
    display: block;
    width: 34px;
    height: 34px;
    opacity: 0; 
    visibility: hidden;
    transition: 1s;
    transform: translateY(300px);
    /* 버튼을 밑에 숨겨놓을 때 위치 */
}
.fix-btn .btn-top.show{
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    /* 버튼이 보일 때 위치 */
}

📌js

$(window).scroll(function(){
    curr=$(this).scrollTop(); // 현재 스크롤바의 수직 위치 값
    if(curr >= 20){
        $('.fix-btn .btn-top').addClass('show'); // curr가 20 이상이면 show 클래스 추가
    }else{
        $('.fix-btn .btn-top').removeClass('show'); // 20 미만이면 show 클래스 제거
    };
});
$('.fix-btn .btn-top').click(function(){
    window.scrollTo({top:0,behavior:"smooth"}); // 스크롤바가 부드럽게 최상단으로 이동
});

* 웹 표준 검사 통과

The W3C Markup Validation Service의 Nu Html Checker를 통해 index.html의 마크업이 웹 표준에 적합함을 확인했습니다.

profile
web publisher

0개의 댓글