일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- html
- react-native
- custom printing
- 이미지 데이터 타입
- Failed to compiled
- silent printing
- device in use
- augmentedDevice
- adb connect
- Can't resolve
- electron-packager
- camera permission
- vercel git lfs
- dvh
- github pdf
- rolldown
- npm package
- ffi-napi
- Recoil
- Each child in a list should have a unique "key" prop.
- react-native-dotenv
- Git
- adb pair
- camera access
- nextjs
- github 100mb
- ELECTRON
- animation
- github lfs
- 티스토리 성능
- Today
- Total
Bleeding edge
contentEditable에서 한글입력 막기(단일 node) 본문
사용하게된 이유
사용자에게 원하는 타입의 값을 받아서 처리를 한다고 하면, 두 가지 방법이 있다.
1. validator를 이용해서 원하는 값이 나오지 않는다면 사용자에게 다시 값을 입력하라고 알린다.
2. 원하지 않는 값이 나온 경우에 input에 값의 입력을 받지 않는다.
이번에 적용해야할 기능은 2번이었다.(물론 1번도 부분 적용..) 이번에 적용해야하는 것은 숫자와 %였다.
input vs contentEditable
View적인 차이
input은 value의 하나의 textNode에 대한 스타일링을 적용할 수 있지만 contentEditable은 innerHTML을 이용하여 다양한 태그를 감쌀 수 있기에 다양한 스타일링이 가능하다
코드에서의 차이
input은 type을 입력하여 의도되지 않은 타입이 있다고하면 키를 입력하는 단계에서 차단이 가능하다. contentEditable에는 아쉽게도 이 기능이 없다.(굳이 몇가지를 추가하면, input은 onChange를 사용할 수 있고, contentEditable은 불가하다)
나의 선택은?
contentEditable!
이유는? input의 type을 이용하여 한글의 입력을 막고 싶지만 문제는 input 님께서는 %를 받을 수 없기 때문에 input을 사용할 수 없다.
한글입력 막기 시도 - 1
키의 입력을 막는 방법은 contentEditable이 적용된 element에 onKeyDown에서 event.preventDefault()를 적용하면된다. 그렇다. 영어는 가능하다. 하지만 위대한 한글은 불가능하다. 이유는 한글과 영어의 키에 차이가 있다.
영어 : 한 개의 키당 한개의 값
한글 : 자음 + 모음
한글을 contentEditable에 입력해보면 영어와 다르게 밑줄이 들어간다. 이 밑줄이 쳐진 글자의 경우 다음 key 입력과 조합하여 글자가 있는 경우 선택된 글자를 수정한다. (일단 모음을 누르면 아무것도 입력이 안되는데 굳이 밑줄이 될 이유가 있나요 !!)
결론은 onKeyDown의 preventDefault로 한글을 막을 수 없다.
한글입력 막기 시도 - 2
preventDefault로 막을 수 없다면 innerHTML은 어떨까?
innerHTML이 원하는 모양의 Text를 전달해줬지만, 문제는 커서의 위치였다. innerHTML을 set한 이후에 커서가 맨 앞으로 이동하였다.(이미 알고는 있었지만..)
커서의 위치를 입력한 위치로 바꾸기
contentEditable에서 커서의 위치를 바꾸는 방법은 getSelection과 range를 이용하여 바꿀 수 있다. 만일 이 방법을 적용하면 한글을 입력하였을 때 다음과 같은 플로우로 진행된다.
1. 한글을 입력한다
2. contentEditable(예시이름).innerHTML = format(contentEditable.innerHTML)
3. 커서가 맨 앞으로 이동
4. 커서 위치를 처음 위치로 돌리기
4번의 방법을 사용하기 위해서는 플로우에 몇가지 과정이 다시 필요하기 때문에 플로우를 다시 잡아야한다.(선행 작업이 필요하기 때문에)
1. 한글을 입력한다.
2. getSelection을 이용하여 위치를 구한다.
3. contentEditable(예시이름).innerHTML = format(contentEditable.innerHTML)
4. createRange를 이용하여 텍스트 레인지를 생성하고 레인지에 setStart를 사용한다.
굳이 미래의 나를 위해서 팁을 하나 남기자면 4번과정에서 setStart를 사용할 때는 element.childNodes[0]를 사용하자(제목처럼 단일 텍스트 노드인 경우)
코드로 작성하면 이런 코드가 될 것 같다.
const customInnerHTML = (element, value) => {
const selection = window.getSelection();
if (selection === null || element === null) return;
const { focusOffset } = selection;
if (element.innerHTML === value) return;
element.innerHTML = value;
const range = document.createRange();
console.log(element.childNodes[0], focusOffset);
range.setStart(element.childNodes[0], Math.min(focusOffset, element.innerHTML.length));
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
element.focus();
};
자 여기까지 왔으면 거의 마무리다. 문제는 이 함수는 어느 이벤트핸들러에서 사용해야할까? onKeyDown, onKeyUp?
이벤트 발생 순서를 보면 한글의 입력의 이벤트를 막기 위해서는 onKeyUp에서 처리를 해야한다. (onKeyDown으로 하는 경우 연타를 눌렀을 때 텍스트가 최대 두 글자까지만 막는 기현상을 볼 수 있다. 예를들면 1234ㄱㅁ와 같이)
마무리
이번에 contentEditable에서는 이해를 완전히 못하고 코드를 마무리 했었던 것 같은데 이전보다 range instance에 대한 것과 위치에 대한 이해도가 생겨서 이전보다 contentEditable에 대한 이해가 생긴 것 같아서 좋았다. 이전에 만들었던 코드의 경우 단일 텍스트 노드가 아니지만, 이 방법을 이용해서 리팩토링을 적용해봐야겠다.
'Javascript > React & Next' 카테고리의 다른 글
Nextjs 모바일 주소창 유무에 따른 높이(viewport height) 변화 막기 (0) | 2023.09.04 |
---|---|
상태관리 라이브러리의 장단점 정리(직접작성이 아닌 링크) (0) | 2023.06.16 |
onKeyUp, onKeyDown 언제 사용하는게 좋을까?(feat ContentEditable) (0) | 2023.05.03 |
state를 beforeunload의 함수 내부에서 localStorage로 저장하기 (0) | 2023.04.06 |
React 정리 - 수정중... (0) | 2022.06.14 |