input[type="file"]의 스타일링하기 + DOM 조작으로 자동 제출
너무 못생겼습니다...
input[type="file']을 디자인하기 위해는 input을 아예 숨기고, 붙어 있는 label을 이용해야 합니다.
여기서, 숨긴다고 input에 display:none을 주게 되면 연결된 label까지 없어지게 됩니다.
visibility를 hidden으로 준 다음, label을 스타일링하면 되겠습니다.
위치로는 form에 position을 relative로 주고, label을 form 내부에서 position:absolute로 주는 것이 좋겠죠?
<form
encType="multipart/form-data"
onSubmit={photoChange}
style={{ position: "relative" }}
>
<Label htmlFor="file">이미지 변경</Label>
<input
type="file"
name="file"
id="file"
accept="image/*"
style={{ visibility: "hidden" }}
/>
<input type="submit" />
</form>
const Label = styled.label`
display: inline-block;
padding: 0.5em 0.75em;
color: #999;
font-size: inherit;
line-height: normal;
vertical-align: middle;
background-color: #fdfdfd;
cursor: pointer;
border: 1px solid #ebebeb;
border-bottom-color: #e2e2e2;
border-radius: 0.25em;
position: absolute;
left: 5.6em;
margin-top: 1.5em;
`;
이제 버튼을 눌러서 이미지 업로드를 할 수 있습니다.
자, 그런데 문제는 유저의 입장에서는 해당 버튼을 눌러 이미지를 선택하면 자동으로 업로드가 되어야 하는데, 이 당연한 듯한 동작이 위와 기본 적인 코드 상에서는 안된다는 것입니다.
따라서 JS를 통한 DOM 조작을 이용해 강제로 이미지를 제출하도록 만들어야 합니다.
시도 1) parentNode를 통한 제출
e.target.parentNode.submit();
preventDefault()가 안 된다는 사실을 알고선 우회
시도 2) 제출 버튼을 작성한 후 nextSibling으로 해당 버튼을 click
이 방법은 되더군요. 제출 버튼을 누른 것과 동일하므로 form을 제출했을 때의 로직을 그대로 작성해주시면 됩니다.
여기서 팁이 있다면, visibility: "hidden"이 아니라 display: "none"을 쓰라는 것입니다.
visibility를 쓰게 되면 버튼이 사라지긴 하지만 공간은 그대로 차지하고 있기 때문에 작성한 Label의 위치를 css로 재수정해야 합니다. 그러나 display: none을 설정하면 공간까지 깔끔하게 사라집니다.
<input type="submit" style={{ display: "none" }}></input>
const photoChange = (e) => {
e.target.nextSibling.click();
};
React에서는 useRef를 통해서 입력하고자 하는 input 태그를 ref로 지정해서 가상 클릭을 만들어내면 쉽습니다.
const imageInput = useRef();
const onClickImageUpload = () => {
imageInput.current.click();
}
<input type="file" multiple hidden ref={imageInput} onChange={onChangeImages} />
<Button onClick={onClickImageUpload}>이미지 업로드</Button>
시도 3) 아! 이런 현명한 방법이...
상당히 시간이 지난 후지만, 그냥 input과 label을 이용할 필요도 없이 예쁘게 button을 만들고, 해당 버튼을 누르면
input 자체를 가상으로만든 후 클릭하게 만들면 되겠다는 생각이 들었습니다.
뭘 숨기고 자시고 할 필요도 없이 말이죠. 이게 제일 현명한 방법 같습니다.
아래와 같은 방식 말이죠. 이 방법은 다른 프로젝트에서 사용해봐야겠습니다. ㅎ
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
참고한 글)