contentEditable
은 *전역 특성으로 요소 내의 텍스트나 컨텐츠를 편집할 수 있도록 만들어주는 HTML 속성입니다.
'true'
또는 빈 문자열
: 요소에 텍스트를 입력하거나 편집할 수 있습니다.'false'
: 요소를 편집할 수 없습니다.'inherit'
: 부모 요소의 contentEditable
속성을 상속받습니다.전역 특성(Global attributes)
- 모든 HTML에서 공통으로 사용할 수 있는 특성으로 모든 HTML 요소에 지정할 수 있습니다.
- 하지만, 일부 요소에는 아무런 효과도 없을 수 있습니다.
contentEditable
를 적용하면 input
이나 textarea
처럼 텍스트를 입력할 수 있습니다!
input
이나 textarea
를 사용하면 기본 스타일을 제거해야하고, 한 줄이 아니라 여러 줄을 입력할 경우 텍스트 높이에 따라 입력 영역의 높이도 높여줘야 하는 번거로움이 있는데, contentEditable
을 적용할 경우 이러한 불편함이 사라집니다.
적용하는 방법은 간단합니다. 아래와 같이 적용하면 h1
태그의 컨텐츠를 편집할 수 있게 됩니다.
<h1 contentEditable>게시글 제목</h1>
그리고 다음과 같은 경고 메시지도 함께 등장합니다. 쨔쟌~
React에서 contentEditable
속성을 가진 요소가 있을 때 나타나는 경고 메시지 입니다.
contentEditable
속성을 적용하면 해당 요소는 사용자가 직접 컨텐츠를 편집할 수 있기 때문에 React가 해당 요소를 제어할 수 없게 됩니다. 따라서, React에 의해 렌더링 될 경우 React의 가상 DOM과 실제 DOM 사이에 충돌이 일어날 수 있기 때문에 React는 경고 메시지를 나타냅니다.
해당 경고를 무시하려면 suppressContentEditableWarning
속성을 추가합니다.
<h1 contentEditable suppressContentEditableWarning={true}>게시글 제목</h1>
하지만 이 방법은 경고 메시지를 무시하는 속성일 뿐 예기치 못한 문제를 해결해주는 방법은 아니라는 것을 알고 사용하는 것이 좋습니다.
일반적으로 form 요소를 사용하게 되면 placeholder
속성을 적용하여 해당 입력 필드에 사용자가 입력해야할 값의 형식이나 예시를 나타냅니다.
contentEditable
속성을 적용하게 되면 placeholder
를 직접 구현해야 합니다.
먼저, 다음과 같이 placeholder
로 보여질 메시지를 input
에서 적용하는 방식과 동일하게 작성했습니다.
<Title
contentEditable
suppressContentEditableWarning={true}
placeholder="제목을 작성해주세요."
/>
그리고 다음과 같이 스타일을 적용합니다.
// emotion을 이용한 코드
const Title = styled.h1`
&:empty::before {
content: attr(placeholder);
color: #CCC;
}
`;
:empty
는 가상 클래스로 해당 요소의 자식 요소나 텍스트가 없다면 지정한 스타일이 나타납니다. 단, 공백, 개행, 주석 등이 포함되어 있다면 :empty
선택자와 일치하지 않습니다.
:empty
를 이용하여 해당 요소에 텍스트가 입력되어 있지 않을 경우, placeholder
에 입력한 메시지가 옅은 회색으로 나타나게 했습니다.
아래는 적용한 모습입니다.
제목은 h1
태그에 contentEditable
속성을 적용했고, 내용은 textarea
로 작성한 코드입니다. 감쪽같죠?
placeholder | 텍스트 입력 |
---|---|
이제 React-hook-form 을 적용해봅시다!
form
요소가 아닌데 적용할 수 있을까요? 네
하지만 contentEditable
요소는 onChange
를 사용할 수 없습니다. 대신 onInput
를 사용할 수 있습니다.
register의 메서드에는 onInput
가 없기 때문에 다음과 같이 적용할 수 있습니다.
const { register, setValue } = useForm<{ title: string }>({ mode: 'onChange' });
const handleOnInput: FormEventHandler<HTMLHeadingElement> = (e) => {
const { textContent } = e.currentTarget;
if (textContent !== null) {
// heading 요소에 입력된 textContent를 title의 값으로 넣어준다.
setValue('title', `${textContent}`, {
shouldValidate: true, // 유효성을 검사하도록 설정
});
}
};
const onSubmit: SubmitHandler<{ title: string }> = (data) => {
console.log(data);
};
<form onSubmit={handleSubmit(onSubmit)}>
<Title
contentEditable
suppressContentEditableWarning={true}
placeholder="제목을 작성해주세요."
{...register('title', {
required: true,
})}
onInput={handleOnInput} // 텍스트를 입력하면 handleOnInput 동작
/>
</form>
실은 여기에 문제점이 있습니다. 콘솔창에 찍힌 데이터를 확인해주세요.
textContent
는 개행 문자가 나타나지 않습니다.
내용을 입력한 곳은 textarea
의 값을 가져오는데 개행 문자 \n
가 나타납니다.
Elements 탭에서 확인해봅시다아
띄어쓰기를 하면 홀수 번째 띄어쓰기는
문자로 나타납니다. 짝수 번째 띄어쓰기는 일반 공백으로 나타납니다.
개행은 나타나지 않습니다.. 왜요..
"textContent
는 공백 문자(whitespace)를 포함하여 모든 텍스트를 가져옵니다. 따라서, 띄어쓰기, 탭, 개행 문자 등의 공백 문자도 포함됩니다."
라고 하셨잖아요..
innerText
를 이용하면 개행을 연속으로 두 번 했을 경우 개행 문자가 한 번 더 추가됩니다.
개행 세 번 했는데 개행 문자는 5개!
contentEditable
를 왜 사용했을까요? 사용해보고 싶어서요.
contentEditable
속성을 알게된 건 꽤 오래 전인데 직접 적용해본 적이 없어서 한 번 사용해보고 싶었습니다. 추후에 해당 페이지에 에디터 기능을 추가하면 좋지 않을까? 미리 조금 적용해볼 수 있는 것을 넣어보자 했지만 생각보다 쉽지 않네요.
에디터 구현하게 되면 그 때 다시 만나자!
form
요소가 아닌 요소에 React-hook-form을 적용할 수 있다는 것 자체로 흥미진진했습니다. 당연하게 form
요소에만 적용할 수 있을거라고 생각했는데, 아니었네요.
그럼 저는 코드 수정하러 가겠습니다. 헿
참고
안녕하세요. 리액트 프로젝트에서 contentEditable 속성 사용하실 때 한글 자음, 모음이 분리되어서 입력되는 현상은 없으셨는지 궁금합니다.