모달창 스크롤 막기 - modalchang seukeulol maggi

티스토리 뷰

eungwang1 2022. 2. 20. 18:04

모달창을 띄울 때, 스크롤을 비활성화 하는 방법과

모달창이 닫힐 때, 스크롤을 다시 활성화 하는 방법입니다.

// 스크롤 비활성화
const openModal = (e) => {
    document.body.style.overflow = "hidden";
  };
  
// 스크롤 활성화
 const closeModal = (e) => {
    document.body.style.overflow = "unset";
 };

body 태그의 css를 position을 fixed로 변경하고, top을 현재 스크롤 위치로 하고 overflow-y: scroll; width: 100%;을 추가하면 스크롤 방지를 할 수 있다.

해당 css를 변경할 때는 useEffect를 사용할 것이다. 모달이 사라질 때에는 useEffect의 return을 사용해 react-dom0시킨 다음 react-dom1를 이용해 현재 스크롤 위치로 이동시키면 된다.

3-2. 구현 코드

useEffect(() => {
  document.body.style.cssText = `
    position: fixed; 
    top: -${window.scrollY}px;
    overflow-y: scroll;
    width: 100%;`;
  return () => {
    const scrollY = document.body.style.top;
    document.body.style.cssText = '';
    window.scrollTo(0, parseInt(scrollY || '0', 10) * -1);
  };
}, []);

4. 마무리

프로그래밍을 잘 모르는 사람이 볼 때는 모달을 컴포넌트 트리에서 빼든 안 빼든 css적으로 동일하다면 상관없을 것이다. 하지만 이런 사소한 부분 하나 하나 모여 코드의 완성도와 깔끔함이 결정된다고 믿기에 이 방법이 좋은 프로그래밍이라고 생각한다.

fixed로 화면을 덮는 모달을 열었을 때 모달창이 덮고 있는데 외부 화면이 스크롤 되는게 어색하다는 QA가 있었다.

모달 open state에 따라 외부 화면도 fixed <-> static으로 변경하면 외부화면도 고정이 되었지만 외부화면의 스크롤이 최상단으로 초기화되는 문제가 있었다.

처음에 생각했던대로 event.preventDefault(); 로 막아야겠다는 생각을 했지만
이벤트 리스너에 scroll이 아닌 wheel 이벤트인 것을 알게되어서 조치할 수 있었다.

  • wheel 같은 passive 이벤트는 preventDefault 사용하려면 passive false가 필요하다

참고

https://alvarotrigo.com/blog/prevent-scroll-on-scrollable-element-js/

TIL/2022 TIL

리액트 모달창 세로 스크롤 막기

ryurim 2022. 7. 30. 22:42

반응형

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ모달창을 띄웠는데 !!!!

이럴수가 ?

모달창 스크롤 막기 - modalchang seukeulol maggi

고수찾기 페이지의 

height 값이 3000px이어서 ..위처럼 스크롤이 되는 끔찍한 ...현상이 발생되었다..

홀..리 ~~~~~~~~~~~~~~

 

웨..웨 !!웨그러는고야 !!!

import React, { useEffect, useState } from "react";
import styled from "styled-components";

interface SearchProModalProps {
  filterIsOpen: boolean;
  setFiterIsOpen: any;
}
const SearchProModal = (props: SearchProModalProps) => {
  const { filterIsOpen, setFiterIsOpen } = props;

  const tabTitle: string[] = ["서비스", "지역"];

  const [openTab, setOpenTab] = useState<number>(0);

  const onClickTab = (idx: number) => {
    setOpenTab(idx);
  };

  const ModalClose = () => {
    setFiterIsOpen(!filterIsOpen);
  };

  return (
    <>
      <BackDrop onClick={ModalClose} />
      <ModalOutter>
        <ModalSearchBox>
          <FilterTitle>
            {tabTitle.map((title, idx) => {
              return (
                <FilterLi
                  key={idx}
                  idx={idx}
                  openTab={openTab}
                  onClick={() => {
                    onClickTab(idx);
                  }}
                >
                  {title}
                </FilterLi>
              );
            })}
          </FilterTitle>
          <button onClick={ModalClose} />
        </ModalSearchBox>
      </ModalOutter>
    </>
  );
};

export default SearchProModal;

const BackDrop = styled.div`
  width: 100%;
  height: 100vh;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 999;
  background: rgba(0, 0, 0, 0.5);
`;

const ModalOutter = styled.div`
  width: 420px;
  height: 42.5rem;
  overflow-y: scroll;
  background: #fff;
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
`;
const ModalSearchBox = styled.div`
  width: 100%;
  height: 60px;
  padding: 10px 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;

  & > button {
    width: 14px;
    height: 14px;
    background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTgiIGhlaWdodD0iMTgiIHZpZXdCb3g9IjAgMCAxOCAxOCI+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBpZD0iYSIgZD0iTTkgNy44NjlMMTYuNDM0LjQzNGwxLjEzMiAxLjEzMkwxMC4xMyA5bDcuNDM1IDcuNDM0LTEuMTMyIDEuMTMyTDkgMTAuMTNsLTcuNDM0IDcuNDM1LTEuMTMyLTEuMTMyTDcuODcgOSAuNDM0IDEuNTY2IDEuNTY2LjQzNCA5IDcuODd6Ii8+CiAgICA8L2RlZnM+CiAgICA8dXNlIGZpbGw9IiMzMjMyMzIiIGZpbGwtcnVsZT0ibm9uemVybyIgeGxpbms6aHJlZj0iI2EiLz4KPC9zdmc+Cg==")
      center;
  }
`;

const FilterTitle = styled.ul`
  width: 96px;
  height: 40px;
  font-size: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 500;
`;
interface LiType {
  idx: number;
  openTab: number;
}
const FilterLi = styled.li<LiType>`
  height: 40px;
  padding: 0 2px;
  line-height: 40px;
  cursor: pointer;
  color: ${({ openTab, idx }) => (idx === openTab ? "#00c7ae" : "#323232")};
  border-bottom: ${({ openTab, idx }) =>
    idx === openTab ? `2px solid #00c7ae` : "none"};
`;

문제의 이전 코드 ..

BackDrop에 .. overflow:hidden을 주어도 아무런 변화가 일어나지 않았다고 한다. 🧐

 

하지만 구글링하다가..ㅋㅋ발견해버렸다 마법의 코드를

 

 useEffect(() => {
    document.body.style.cssText = `
      position: fixed; 
      top: -${window.scrollY}px;
      overflow-y: scroll;
      width: 100%;`;
    return () => {
      const scrollY = document.body.style.top;
      document.body.style.cssText = "";
      window.scrollTo(0, parseInt(scrollY || "0", 10) * -1);
    };
  }, []);

위의 코드만 넣어주면 해결된다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

해결된 모달창의 모습

모달창 스크롤 막기 - modalchang seukeulol maggi

나이스 !!!

 

원래 오늘의 집 클론코딩에서 숨고 클론코딩으로 바뀌었고,,

내가 맡은 기능은 .. 무시무시한 숨고의 고수찾기 카테고리 기능이다 .. ^^

일단 뷰먼저 해두고..내일..카테고리 시도 해봐야징...핫..

320x100

반응형