개발일지

Javascript 공부 #6 본문

Javascript

Javascript 공부 #6

kosssshhhh 2023. 1. 20. 17:56

브라우저 안의 여러 저장공간


크롬 개발자도구의 Application 탭에 보면 여러가지 저장공간이 존재한다

  • Local Storage / Session Storage ( key : value 형태로 문자 숫자 데이터 저장 가능 )
  • indexed DB ( 크고 많은 구조화된 데이터를 DB처럼 저장 가능, 문법 더러움 )
  • Cookies ( 유저 로그인 정보 저장 공간 )
  • Cache Storage ( html css js img 파일 저장해두는 공간 )

개발자가 선택해서 사용하면 되는데 범용적으로 사용 가능한 Local Storage 사용해보자

Local Storage 는 문자, 숫자만 key : value 형태로 저장 가능하고, 5MB 까지만 저장 가능하다. Local Storage는 브라우저 재접속 해도 영구적으로 남아있는데 Session Storage 는 브라우저를 끄면 날아간다.

 

유저가 브라우저를 청소하지 않는 이상 반영구적으로 데이터 저장이 가능하다.

 

 

Local Storage 사용법


localStorage.setItem('이름', 'kim') //자료저장하는법
localStorage.getItem('이름') //자료꺼내는법
localStorage.removeItem('이름') //자료삭제하는법

 

 

Local Storage 에 array / object 저장하기


array 또는 object 를 로컬스토리지에 저장하려고 하면 강제로 문자로 바꿔서 저장된다. 따라서 자료가 깨지고 할 수 있다.

 

약간 편법인데 array / object 를 JSON으로 바꾸면 문자 취급을 받기 때문에 안전하게 로컬스토리지에 저장할 수 있다.

 

JSON은 그냥 따옴표 친 array / object

 

var arr = [1,2,3];
var newArr = JSON.stringify(arr);

localStorage.setItem('num', newArr)
  1. JOSN.stringify() 안에 array / object 넣어주면 JSON으로 변환해준다.
  2. 변환한 array / object 로컬스토리지에 저장해준다.

이런 방식으로 하면 자료형이 깨지지 않고 로컬스토리지에 저장 가능하다.

 

 

 

구매버튼 눌렀을 때 장바구니에 담기 예제 ( local storage 사용 )


	$(".buy").click(function (e) {
        //var productName =
        //  e.target.parentElement.firstElementChild.nextElementSibling.innerHTML;

        var productName = $(e.target).siblings("h5").text();

        if (localStorage.getItem("cart") == null) {
          var cart = [productName];

          localStorage.setItem("cart", JSON.stringify(cart));
        } else {
          var cart = JSON.parse(localStorage.getItem("cart"));

          cart.push(productName);

          var newJsonCart = JSON.stringify(cart);
          localStorage.setItem("cart", newJsonCart);
        }
      });

로컬스토리지가 비어있을 경우, 배열 새로 생성해서 JSON으로 로컬스토리지에 저장

이미 로컬스토리지에 cart 가 존재할 경우, 로컬스토리지에서 데이터 받아와 .push() 함수 이용하여 배열에 추가해 준 후 다시 JSON으로 바꿔서 로컬스토리지에 저장

 

 

위의 예제에 장바구니에 동일 항목 개수 세기


$(".buy").click(function (e) {
        var productName = $(e.target).siblings("h5").text();

        if (localStorage.getItem("cart") == null) {
          var productsCart = [
            { title: "Blossom Dress", num: 0 },
            { title: "Springfield Shirt", num: 0 },
            { title: "Black Monastery", num: 0 },
          ];

          ++productsCart.find((v) => v.title == productName).num;
          localStorage.setItem("cart", JSON.stringify(productsCart));
        } else {
          var cart = JSON.parse(localStorage.getItem("cart"));
          ++cart.find((v) => v.title == productName).num;
          localStorage.setItem("cart", JSON.stringify(cart));
        }
      });

array / object 자료형을 사용하는 데에 불펴함이 있어서 array / object 를 미리 생성해놓은 후 진행

추후 과제로 확장성 있는 코드로 바꿔주기

 

 

 

 

position: sticky 활용하기


참고 사이트: apple.com/apple-tv-4k

 

position sticky

(Edge 이상에서 사용가능)

스크롤 되었을 때 화면에 고정되는 요소를 만들고 싶을 때 사용할 수 있는 CSS 속성.

 

position: fixed 는 항상 화면에 고정되는 요소를 만들 때 사용했는데 차이가 무엇이냐면,

position: sticky 는 스크롤 되어 요소가 화면에 등장하게 되면 고정시킨다는 특성이 있다.

<body style="background : grey; height : 3000px">

<div class="grey">
  <div class="image">
    <img src="appletv.jpg" width="100%">
  </div>

  <div style="clear : both"></div>
  <div class="text">Meet the first Triple Camera System</div>
    
</div>

</body>
.grey {
  background: lightgrey;
  height: 2000px;
  margin-top: 500px;
}
.text {
  float: left;
  width : 300px;
}
.image {
  float: right;
  width : 400px;
  position: sticky;
  top: 100px;
}

검고 긴 화면에 텍스트와 이미지가 하나씩 보인다.

 

이미지에 position: sticky 를 주면

  1. 스크롤 되어서 이미지가 보이는 순간
  2. viewpoint 의 맨 위에서부터 100px 위치에서 고정이 된다.
  3. 부모 박스를 넘어서 스크롤이 되면 이미지도 같이 사라지게 된다.

( 주의 )

  1. 스크롤을 할 부모 박스가 있어야하고
  2. top 등 좌표 속성과 함께 사용해야 제대로 보인다.

 

 

캐러셀에 스와이프 기능 만들기


터치 가능한 캐러셀 조작 기능에는

  1. 드래그한 거리만큼 사진도 왼쪽으로 움직임
  2. 마우스 뗐을 때, 일정거리 이상 이동하면 두번 째 사진 보여줌
  3. 일정거리 미만 움직였을 때 다시 첫번 째 사진 보여줌

이 기능을 구현하기 위해 mouse 이벤트에 대해 알아야 한다.

 

 

 

mouse 이벤트


mousedown : 어떤 요소에 마우스 버튼을 눌렀을 때

mouseup : 어떤 요소에 마우스 버튼 뗐을 때

mousemove : 어떤 요소 위에서 마우스 이동할 때

<div>캐러셀있는곳</div>

<script>
  $('.slide-box').eq(0).on('mousemove', function(){
    console.log('안녕')
  })
</script>

위와 같이 이벤트리스너에 mousemove 이벤트 달아주면 해당 div 위에서 마우스 움직일 때마다 ‘안녕’ 출력해준다.

<div>캐러셀있는곳</div>

<script>
  $('.slide-box').eq(0).on('mousemove', function(e){
    console.log(e.clientX)
  })
</script>

mouse 이벤트리스너 안에서는 e.clientX, e.clientY 를 사용할 수 있는데, 현재 마우스 좌표를 알려준다.

유용하게 사용할 수 있다.

 

 

드래그한 만큼 사진 움직이기 구현


예를 들어 사진을 왼쪽으로 50px 드래그 했다면, 사진도 왼쪽으로 50px 같이 움직여주어야 한다.

이러한 기능을 구현하기 위해선 마우스의 이동거리를 측정해야되는데, 마우스 이벤트를 이용하여 알 수 있다.

 

(마우스 누를 때 X좌표) - (마우스 움직일 때의 X좌표)

를 하게되면 드래그한 만큼의 거리를 알 수 있다.

<script>
  var 시작좌표 = 0;

  $('.slide-box').eq(0).on('mousedown', function(e){
    시작좌표 = e.clientX;
  });

  $('.slide-box').eq(0).on('mousemove', function(e){
    console.log(e.clientX - 시작좌표)
  });
</script>
  1. 시작좌표 변수를 함수 밖에 선언해준다.
  2. 마우스 클릭 시 현재 좌표를 시작좌표에 저장해준다.
  3. mousemove 이벤트 발생 시 시작좌표랑 현재좌표인 e.clientX 를 빼준다.

 

 

이동거리만큼 박스도 이동해주기

<script>
  var 시작좌표 = 0;

  $('.slide-box').eq(0).on('mousedown', function(e){
    시작좌표 = e.clientX;
  });

  $('.slide-box').eq(0).on('mousemove', function(e){
    console.log(e.clientX - 시작좌표)
    $('.slide-container').css('transform', `translateX( ${e.clientX - 시작좌표}px )`)
  });
</script>

이런식으로 코드를 짜주면 원하는대로 잘 작동하지 않는 것을 볼 수 있다.

따라서 사진을 클릭 했을 때만 박스 이동해달라고 코드를 더 짜주어야한다.

 

let 시작좌표 = 0;
    let check = false;
    $(".slide-box")
      .eq(0)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(0)
      .on("mousemove", function (e) {
        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(${e.clientX - 시작좌표}px)`
          );
          
        }
      });

    $(".slide-box")
      .eq(0)
      .on("mouseup", function (e) {
        check = false;
        if (e.clientX - 시작좌표 <= -100) {
          console.log($(".slide-box img").eq(0).width());
          var width = $(".slide-box img").eq(0).width();
          $('.slide-container').css('transform', `translateX(-${width }px)`)
          console.log("hi");
        }
      });

mousemove, mousedown, mouseup 이벤트를 모두 사용하여 해당 사진을 클릭 했을 때만 움직이도록 조건문을 사용하여 조작하였고,

사용자가 drag를 100px 이상 했을 땐, 다음 사진으로 넘어가도록 mouseup 이벤트 리스너에 달아주었다.

모든 사진에다가 스와이프 동작을 넣어주면

	let 시작좌표 = 0;
    let 이동거리 = 0;
    let check = false;

//첫번째 사진 mouse 이벤트리스너
    $(".slide-box")
      .eq(0)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(0)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css("transform", `translateX(${이동거리}px)`);
        }
      });

    $(".slide-box")
      .eq(0)
      .on("mouseup", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

//두번째 사진 mouse 이벤트리스너
    $(".slide-box")
      .eq(1)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(1)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(1)
      .on("mouseup", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        } else if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

//세번째 사진 mouse 이벤트리스너
    $(".slide-box")
      .eq(2)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(2)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;

        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() * 2 - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(2)
      .on("mouseup", function (e) {
        check = false;

        if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

마우스를 떼면 사진이 부드럽게 움직이지 않고 순간이동 하는데, 애니메이션 주고 싶으면 이동할 박스에 css transition 속성 추가해주면 된다.

 

하지만 사진 스와이프 기능 구현할 때 .slide-container 박스에 있던 transition을 제거해주었다.

 

⇒ 누르고 사진을 스와이프할 때 transition 이 있으면 부자연스럽게 움직이기 때문에

 

→ 평소엔 transition 이 필요 없지만, 사진에서 마우스를 떼면 transition 이 필요하다

 

“마우스 떼면 0.5초정도 transition 속성 주었다가 떼주기”

따라서 타이머 기능 사용하여 transition 붙였다가 0.5초 후에 떼어 달라고 코드 짬

 

  1. 마우스 놓게되면 transition : all 0.5s 부착한다
  2. setTimeout 사용하여 0.5초 후에 transition : none 줌

 

 

모바일 터치 이벤트리스너


만든 웹페이지를 모바일 기기로 테스트하고 싶으면 크롬 개발자도구 좌상단 toggle device toolbar 버튼을 누르면된다.

그런데 모바일 기기로 테스트하면 스와이프가 되지 않는다.

→ mouse 이벤트리스너를 달아놨기 때문

 

모바일 환경은 터치 이벤트리스너를 달아주어야 터치에 반응한다.

 

touchstart : 터치 시작시 감지

touchmove : 터치중일 때 계속 감지

touchend : 터치 종료 시 감지

 

touch 이벤트리스너를 사용할 땐 e.clientX 대신 e.touches[0].clientX 와 같이 사용하여야 한다.

터치이벤트는 여러 손가락으로 할 수 있기 때문에 그 중 몇번 째 손가락인지 지정해주어야한다.

 

touchend 이벤트리스너에선 e.clientX 대신 e.changedTouches[0].clientX 사용한다.

 

터치 이벤트리스너나 마우스 이벤트리스너를 둘 다 달아주게 되면 코드가 불필요하게 길어지게된다.

 

그래서 자바스크립트는 외부 라이브러리 의존도가 언제나 높은데

Hammer.js 같은 라이브러리 사용하면 쉽게 기능 개발이 가능하다.

  • 브라우저 호환성 알아서 잡아주고
  • 이벤트리스너 6개 대신 1개만 써도 되고
  • 스와이프, pinch, rotate 등 여러 제스쳐를 감지하는 이벤트리스너 제공해서 편리

최종 코드

 

	let 시작좌표 = 0;
    let 이동거리 = 0;
    let check = false;

    $(".slide-box")
      .eq(0)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(0)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css("transform", `translateX(${이동거리}px)`);
        }
      });

    $(".slide-box")
      .eq(0)
      .on("mouseup", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

    $(".slide-box")
      .eq(1)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(1)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(1)
      .on("mouseup", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        } else if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

    $(".slide-box")
      .eq(2)
      .on("mousedown", function (e) {
        시작좌표 = e.clientX;
        check = true;
      });

    $(".slide-box")
      .eq(2)
      .on("mousemove", function (e) {
        이동거리 = e.clientX - 시작좌표;

        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() * 2 - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(2)
      .on("mouseup", function (e) {
        check = false;

        if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

    //터치 이벤트 리스너
    $(".slide-box")
      .eq(0)
      .on("touchstart", function (e) {
        시작좌표 = e.touches[0].clientX;
        check = true;
      });

    $(".slide-box")
      .eq(0)
      .on("touchmove", function (e) {
        이동거리 = e.touches[0].clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css("transform", `translateX(${이동거리}px)`);
        }
      });

    $(".slide-box")
      .eq(0)
      .on("touchend", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

    $(".slide-box")
      .eq(1)
      .on("touchstart", function (e) {
        시작좌표 = e.touches[0].clientX;
        check = true;
      });

    $(".slide-box")
      .eq(1)
      .on("touchmove", function (e) {
        이동거리 = e.touches[0].clientX - 시작좌표;
        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(1)
      .on("touchend", function (e) {
        check = false;
        if (이동거리 < -100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        } else if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(0vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

    $(".slide-box")
      .eq(2)
      .on("touchstart", function (e) {
        시작좌표 = e.touches[0].clientX;
        check = true;
      });

    $(".slide-box")
      .eq(2)
      .on("touchmove", function (e) {
        이동거리 = e.touches[0].clientX - 시작좌표;

        if (check === true) {
          $(".slide-container").css(
            "transform",
            `translateX(-${$(".slide-box").eq(0).width() * 2 - 이동거리}px)`
          );
        }
      });
    $(".slide-box")
      .eq(2)
      .on("touchend", function (e) {
        check = false;

        if (이동거리 > 100) {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-100vw)`);
        } else {
          $(".slide-container")
            .css("transition", "all 0.5s")
            .css("transform", `translateX(-200vw)`);
        }
        setTimeout(() => {
          $(".slide-container").css("transition", "none");
        }, 500);
      });

'Javascript' 카테고리의 다른 글

this 키워드  (0) 2023.07.06
Javascript 공부 #5  (1) 2023.01.16
Javascript 공부 #4  (0) 2023.01.15
Javascript 공부 #2  (0) 2023.01.12
Javascript 공부 #1  (0) 2023.01.12