ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript Event] Drop 이벤트에 preventDefault, stopPropagation를 적용해도 동작하지 않는 현상 해결
    Javascript & TypeScript 2021. 4. 27. 23:12
    반응형

    React 프로젝트에서 파일을 Drag & Drop 하기 위한 기능을 구현하고 있었다. React를 대상으로한 Drag & Drop 컴포넌트가 오픈소스상에 많이 존재하긴 하지만, 간단한 기능 구현마저 라이브러리로 도배해야할까 싶어 직접 구현하고 있다.

    (결국 진행이 간단치 않게 되었다. 🤣)

     

    소스코드는 아래와 같다.

    import React, { useState, useRef, useEffect, useCallback } from "react";
    import {
      Container,
      createStyles,
      Grid,
      makeStyles,
      Paper,
      Theme,
    } from "@material-ui/core";
    
    const useGridStyles = makeStyles((theme: Theme) =>
      createStyles({
        root: {
          flexGrow: 1,
          marginTop: theme.spacing(4),
          width: "100%"
        },
        item: {
          width: "50%",
        },
        paper: {
            height: 100
        }
      })
    );
    
    function CoverMaker() {
      const cssGrid = useGridStyles();
    
      return (
        <Container>
          <Grid container className={cssGrid.root} spacing={2}>
            <Grid item className={cssGrid.item}>
              <Paper className={cssGrid.paper}>
                  <label 
                    onDragEnter={(e) => { e.preventDefault(); e.stopPropagation(); console.log('onDragEnter')}}
                    onDrop={(e) => { e.preventDefault(); e.stopPropagation(); console.log('drop') }} style={{ width: "100%", height: "100%", display:"block" }}>
                  </label>
                  <input type="file" id="mp3Upload" style={{ display: "none" }}></input>
              </Paper>
            </Grid>
            <Grid item className={cssGrid.item}>
              <Paper className={cssGrid.paper}>2</Paper>
            </Grid>
          </Grid>
        </Container>
      );
    }
    export default CoverMaker;
    

    metarial-ui 기반으로 개발하고 있으며, Paper 안에 label 영역에 파일을 넣으면, 숨겨진 input 태그에 파일을 등록하는 것으로 구현중 이다. 파일을 직접 브라우저에 Drag & Drop 하기 때문에, 기본동작(예를 들어, txt 파일을 drop하면 새 탭으로 txt파일이 표시되는 동작)을 막고자 preventDefault() 함수와 stopPropagation() 함수를 선 호출하도록 처리했다.

     

    그런데 문제가 생겼다. drop 이벤트가 동작하지 않는다. 더군다나 preventDefault() 와 stopPropagation()를 사용했음에도 여전히 기본동작이 실행된다.

     

    이에 대한 원인을 찾기위해 계속 구글링해봤지만 답을 찾을 수가 없었다. 그렇게 삽질을 거듭하다 잘 동작하는 예제소스들이 공통적으로 dragover, dragleave도 함께 사용한다는 것을 확인할 수 있었다.

     

    혹시나 하는 마음에 dragover를 적용해보았고, "drop 이벤트가 정상동작하려면, dragover 도 반드시 필요하다"는 걸 깨닫기까지 그리 오랜 시간이 걸리지 않았다.

    import React, { useState, useRef, useEffect, useCallback } from "react";
    import {
      Container,
      createStyles,
      Grid,
      makeStyles,
      Paper,
      Theme,
    } from "@material-ui/core";
    
    const useGridStyles = makeStyles((theme: Theme) =>
      createStyles({
        root: {
          flexGrow: 1,
          marginTop: theme.spacing(4),
          width: "100%"
        },
        item: {
          width: "50%",
        },
        paper: {
            height: 100
        }
      })
    );
    
    function CoverMaker() {
      const cssGrid = useGridStyles();
    
      return (
        <Container>
          <Grid container className={cssGrid.root} spacing={2}>
            <Grid item className={cssGrid.item}>
              <Paper className={cssGrid.paper}>
                  <label 
                    onDragEnter={(e) => { e.preventDefault(); e.stopPropagation(); console.log('onDragEnter')}}
                    onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); console.log('dragover') }} // 추가한 이벤트. onDrop을 위해선 반드시 필요함.
                    onDrop={(e) => { e.preventDefault(); e.stopPropagation(); console.log('drop') }} style={{ width: "100%", height: "100%", display:"block" }}>
                  </label>
                  <input type="file" id="mp3Upload" style={{ display: "none" }}></input>
              </Paper>
            </Grid>
            <Grid item className={cssGrid.item}>
              <Paper className={cssGrid.paper}>2</Paper>
            </Grid>
          </Grid>
        </Container>
      );
    }
    export default CoverMaker;
    

    여담으로, 어떤 동작을 수행하는 함수가 preventDefault() 와 stopPropagation() 보다 앞에 있으면 안된다고 한다.

     

    React 기준이지만, 이벤트는 javascript 기본 이벤트이므로 다른 프레임워크나, 기본 javascript에서도 공통적인 이슈가 될 거라 생각한다.

     

    누군가 검색했을 때, 이 삽질기록이 획기적인 시간단축을 해줄 수 있길 바란다.

     

     

    반응형

    댓글

Designed by Tistory.