Click-Away Listener

Click-Away Listener (바깥 클릭 감지)

자식 요소 바깥에서 클릭 이벤트가 발생했을 때 이를 감지해 주는 Click-Away Listener 컴포넌트에 대해 알아봅니다. 사용자가 문서의 다른 곳을 클릭하면 닫혀야 하는 Popper 같은 컴포넌트에서 특히 유용해요.

출처: 문서

본문

Click-Away Listener는 자식 요소 바깥의 클릭 이벤트를 감지하는 유틸리티 컴포넌트입니다. (자식 요소는 _하나_만 받는다는 점을 기억해 두세요.) 사용자가 문서의 아무 곳이나 클릭하면 닫혀야 하는 Popper 같은 컴포넌트에서 유용해요. Click-Away Listener는 Portal 컴포넌트도 지원합니다.

아래 데모는 사용자가 페이지의 다른 곳을 클릭하면 메뉴 드롭다운이 숨겨지는 모습을 보여줍니다.

import * as React from 'react';
import Box from '@mui/material/Box';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import { SxProps } from '@mui/system';

export default function ClickAway() {
  const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen((prev) => !prev);
  };

  const handleClickAway = () => {
    setOpen(false);
  };

  const styles: SxProps = {
    position: 'absolute',
    top: 28,
    right: 0,
    left: 0,
    zIndex: 1,
    border: '1px solid',
    p: 1,
    bgcolor: 'background.paper',
  };

  return (
    <ClickAwayListener onClickAway={handleClickAway}>
      <Box sx={{ position: 'relative' }}>
        <button type="button" onClick={handleClick}>
          Open menu dropdown
        </button>
        {open ? (
          <Box sx={styles}>
            Click me, I will stay visible until you click outside.
          </Box>
        ) : null}
      </Box>
    </ClickAwayListener>
  );
}

기본 사용 (Basics)

Import

import ClickAwayListener from '@mui/material/ClickAwayListener';

커스터마이즈 (Customization)

Portal과 함께 사용하기

아래 데모는 Portal 컴포넌트를 사용해 드롭다운을 현재 DOM 계층 밖의 새로운 하위 트리로 렌더링합니다.

import * as React from 'react';
import Box from '@mui/material/Box';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import Portal from '@mui/material/Portal';
import { SxProps } from '@mui/system';

export default function PortalClickAway() {
  const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen((prev) => !prev);
  };

  const handleClickAway = () => {
    setOpen(false);
  };

  const styles: SxProps = {
    position: 'fixed',
    width: 200,
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    border: '1px solid',
    p: 1,
    bgcolor: 'background.paper',
  };

  return (
    <ClickAwayListener onClickAway={handleClickAway}>
      <div>
        <button type="button" onClick={handleClick}>
          Open menu dropdown
        </button>
        {open ? (
          <Portal>
            <Box sx={styles}>
              Click me, I will stay visible until you click outside.
            </Box>
          </Portal>
        ) : null}
      </div>
    </ClickAwayListener>
  );
}

leading 이벤트 듣기

기본적으로 Click-Away Listener 컴포넌트는 trailing 이벤트—클릭이나 터치의 끝—에 반응합니다.

mouseEvent와 touchEvent props를 사용하면 leading 이벤트(클릭이나 터치의 시작)를 듣도록 설정할 수 있어요. 아래 데모에서 확인할 수 있습니다.

:::warning 컴포넌트가 leading 이벤트를 듣도록 설정하면 스크롤바와의 상호작용은 무시됩니다. :::

import * as React from 'react';
import Box from '@mui/material/Box';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import { SxProps } from '@mui/system';

export default function LeadingClickAway() {
  const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen((prev) => !prev);
  };

  const handleClickAway = () => {
    setOpen(false);
  };

  const styles: SxProps = {
    position: 'absolute',
    top: 28,
    right: 0,
    left: 0,
    zIndex: 1,
    border: '1px solid',
    p: 1,
    bgcolor: 'background.paper',
  };

  return (
    <ClickAwayListener
      mouseEvent="onMouseDown"
      touchEvent="onTouchStart"
      onClickAway={handleClickAway}
    >
      <Box sx={{ position: 'relative' }}>
        <button type="button" onClick={handleClick}>
          Open menu dropdown
        </button>
        {open ? (
          <Box sx={styles}>
            Click me, I will stay visible until you click outside.
          </Box>
        ) : null}
      </Box>
    </ClickAwayListener>
  );
}

접근성 (Accessibility)

기본적으로 Click-Away Listener는 자식 요소에 onClick 핸들러를 추가합니다. 이 때문에 스크린 리더가 자식을 "클릭 가능한 요소"로 알려줄 수 있는데, 이 onClick 핸들러가 실제로 자식 요소에는 아무 효과가 없음에도 그렇죠.

이런 동작을 막으려면 자식 요소에 role="presentation"을 추가하면 됩니다.

<ClickAwayListener>
  <div role="presentation">
    <h1>non-interactive heading</h1>
  </div>
</ClickAwayListener>

이것은 Firefox에서 NVDA를 쓸 때 경고 메시지가 발표되지 않는 알려진 문제를 고치기 위해서도 필요합니다—자세한 내용은 이 GitHub 이슈를 참고하세요.

ClickAwayListener API

Demos

이 React 컴포넌트의 사용 예시와 자세한 내용은 컴포넌트 데모 페이지에서 확인할 수 있어요.

Import

import ClickAwayListener from '@mui/material/ClickAwayListener';
// or
import { ClickAwayListener } from '@mui/material';

Props

Name Type Default Required Description
children element - Yes
onClickAway func - Yes
disableReactTree bool false No
mouseEvent 'onClick' | 'onMouseDown' | 'onMouseUp' | 'onPointerDown' | 'onPointerUp' | false 'onClick' No
touchEvent 'onTouchEnd' | 'onTouchStart' | false 'onTouchEnd' No

참고: ref는 루트 요소.

Source code

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용은 컴포넌트 구현을 살펴보는 것도 좋아요.

더 알아보기 (Learn more)