Programming/Html n CSS

[TailwindCSS x Styled-Components] focus/active 효과 적용하는데 다른 작업 하면 사라지는 마법을 해결해보자.

감귤밭호지차 2023. 9. 26. 19:42

보통 [ :focus / :active ] 설정을 했는데도 적용이 안되거나 다른 작업을 하면(스크롤을 내리거나 다른 빈 페이지를 누르면) 효과가 사라지는 경우를 경험해 보신 분이 있을 거다. ... 저 처럼... ㅎ

 

 

일단 원인은 크게 두 가지 

 

 

1. HTML 폼 요소가 <input>, <button>, <textarea> 가 아니다. 

2. 그냥 기본 웹 브라우저의 동작으로 다른 작업을 수행했을 때 사라지는 것이 정상이다. 

 

 

1번의 경우는 강제로 상태가 적용 될 수 있도록 [ tabindex = 0 ] 할당하면 처리가 된다. 

 

 

하지만 2번의 경우 그냥 정상적으로 웹 브라우저가 동작하는 거라 어떻게 처리해야할지 고민이라면.. onClick 이벤트를 이용해서 class를 활성화/비활성화 하는 방법이 있다. 

 

 

>  onClick 이벤트를 이용해서 클래스 할당하기 

기본 구조는

[1]  useState와 handleClick 메소드를 이용해서 on/Off 트리거를 만든다. 

[2]  CSS 속성에서 특정 class 일때 적용하고 싶은 스타일을 만들어둔다. 

[3]  삼항 연산자와  onClick 이벤트를 이용해서 클릭이 되었을 때 특정 class가 적용되거나 다시 적용되지 않은 상태로 만든다. 

 

 

 

 

* 원하는 결과물

 

 

 

 

 

[1] 기본 useState와 handleClick 메소드로 on/Off 트리거 만들기

 

우선 내 작업에서는 Division 부분과 필터링 해주는 부분이 필요하기 때문에 useState 의 상태를 객체{} 형태로 두 개로 관리했다. 

물론 이렇게 하지 않고, useState 를 두 개로 만들어서 관리할 수도 있다. 나는 어차피 한 번에 관리하기 편하라고 하나의 상태 객체 방법을 사용했다. 

 

1. 내가 쓴 방법 : 하나의 상태 객체 

   : Good - 관련된 상태를 한 곳에서 볼 수 있어서 편리

   : BAD - 상태를 업데이트 할 때 객체의 복사본을 만들어야 해서 성능의 오버헤드가 발생할 수 있고.. 여러 상태를 한 번에 업데이트 할 때 살짝 복잡해질 수 있다. 

 

2. 별도의 상태와 함수 사용하는 방법 

    : Good - 각각의 상태가 독립적으로 관리되기 때문에 이해하고 관리하기가 간단하고 쉽다. 

    : BAD - 여러 개의 상태를 관리해야 하기 때문에 코드의 양이 늘어날 수 있다. 

 

//내가 짠 예시 코드
// useState 상태 관리
  const [ isClicked, setIsClicked ]  = useState({
    division: false,
    filter: false,
  });


// handleClick 메소드
  const handleDivisionClick = () => {
    setIsClicked((prevState) => ({
      ...prevState,
      division: !prevState.division,
    }));
  };

  const handleFilterClick = () => {
    setIsClicked((prevState) => ({
      ...prevState,
      filter: !prevState.filter,
    }));
  };

 

위에서 얘기했듯이 handle 함수에서 바로 상태 업데이트를 하지 않는 이유는 하나의 상태 객체이기 때문에 사용하지 않는 상태들을 그냥 복사해버리면 같이 업데이트 되어서 의도하지 않은 효과들이 함께 나타나버리는 수가 있다. 그렇기 때문에 사용하지 않는 녀석들은 그대로 이전 상태를 복사해서 변화를 주고 싶은 부분만 업데이트를 하는 것이다. 

 

 

 

 

[2] CSS 속성에서 특정 class 일때 적용하고 싶은 스타일을 만들기

 

요로콤 .clicked 클래스가 적용될 때 나타나고 싶은 CSS 속성들을 작성해주었다. 역시 하나의 상태를 사용하면 Class가 햇깔리지 않고 관리하기 편하다는 나만의 생각..?

 

export const DivisionTitle = styled.button`
  font-family: 'Inconsolata', sans-serif;
  ${tw`px-3 text-xl `}

  &:hover {
    font-weight: bolder;
    color: #8580E1;
    border-bottom:3px solid #8580E1;
  }

  &.clicked {
    font-weight: bolder;
    color: #8580E1;
    border-bottom:3px solid #8580E1;
  }
`;

export const DivisionFilter = styled.button`
  font-family: 'Inconsolata', sans-serif;
  ${tw`px-2 text-sm font-normal text-BASIC_GRAY`}

  &:hover {
    color: #8580e1;
  }

  &.clicked {
    color: #8580e1;
  }
`;

 

 

 

[2] 삼항 연산자와  onClick 이벤트를 이용해서 클릭이 되었을 때 특정 class가 적용되거나 다시 적용되지 않은 상태로  만들기 

 

짜잔 이렇게 class( tailwindCSS라 className 을 사용 ) 속성을 삼항 연산자를 통해서 각각 상태의 division이나 filter 가 true or flase 로 업데이트 되느냐에 따라 속성의 적용 여부가 주어질 수 있도록 구현했다. 

 

<DivisionWrapper>
    <DivisionBox>
      <DivisionTitle onClick={handleDivisionClick} className={isClicked.division ? 'clicked' : ''}>Recruitment</DivisionTitle>
      <DivisionTitle  onClick={handleDivisionClick} className={isClicked.division ? '' : 'clicked'}>Cooperation</DivisionTitle>
    </DivisionBox>

    <DivisionBox>
      <DivisionFilter  onClick={handleFilterClick} className={isClicked.filter ? 'clicked' : ''}>최신순</DivisionFilter>
      <DivisionFilter  onClick={handleFilterClick} className={isClicked.filter ? '' : 'clicked'}>인기순</DivisionFilter>
    </DivisionBox>
</DivisionWrapper>

 

* 만약에 typescript 를 써서 onClick 부분에 경고 줄이 뜬다면 onClick={ () => handleFilterClick() } 이렇게 바꿔주면 된다. 사실 이거는 보통 익명함수를 사용해서 함수를 호출하는 방법인데, "인수(props)"를 전달해야 하는 경우나 특정 조건에 따라 동적으로 함수를 호출해야 할 때 주로 사용한다.

 

지금 내가 사용한 방법은 함수를 직접 참조해서 이벤트 핸들러로 사용한 방법으로 인수를 전달할 수 없다. 

 

 

 

쫘짠 (날짜는 무시해주시길.. 최신순은 맞은ㅁ..ㅋㅋㅋ)