텍스트 노드 조작 insertData
텍스트 노드에 텍스트를 삽입할 때 처음에는 텍스트 노드의 textContent
를 직접 조작했다. 즉 Range
객체에서 텍스트를 삽입할 위치를 구한 다음 다음과 같이 substr
함수로 텍스트를 직접 조작했다.
var rng = caret.getRange(),
node = rng.startContainer,
offset = rng.startOffset,
tc = node.textContent;
node.textContent = tc.substr(0, offset) + t + tc.substr(offset);
...
코드가 마음에 들지 않았지만, 다른 방법을 몰라 어쩔 수 없이 이렇게 썼다. 그러다 Range.insertNode()
를 알게 되었다. 이 녀석을 사용하면 좀더 쉽게 텍스트를 조작할 수 있다.
var rng = caret.getRange();
rng.insertNode(document.createTextNode(t));
rng.startContainer.normalize();
텍스트 노드를 삽입하는 것이므로 컨테이너 요소(element)에서 텍스트 노드가 여러 개로 분리되는데(삽입한 텍스트 노드와 삽입한 위치 앞뒤로 노드가 나뉜다), normalize()
함수를 사용하면 분리된 텍스트 노드를 쉽게 병합할 수 있다.
그러나 특정 브라우저에서는 rng.insertNode()
를 실행하고 나면 rng.startContainer
가 부모 노드로 올라가버리는 경우가 발생한다. 이것은 의도하지 않은 부수효과로 브라우저에 따라 동작이 다르기 때문에 처리가 귀찮아진다. 프로그램이 일관성있게 동작하도록 하기 위해서는 텍스트 노드를 조작한 후에 range
를 원래의 기대 위치로 보정해주는 코드를 넣어야 했는데, 영 마음에 들지 않았다.
그런데 조금 더 찾아보니 좋은 방법을 알게 되었다. 텍스트 노드의 insertData()
메서드를 사용하는 것이다.
textnode.insertData(offset, text);
insertData()
를 사용하면 텍스트를 삽입하는 코드도 단순해지고, 작업 후에도 텍스트 노드가 분할되지 않기 때문에 normalize()
를 하지 않아도 될뿐 아니라, range
가 깨지지도 않는다.
이렇게 insertData()
(실제 코드에서는 replaceData()
)를 이용해 30줄에 달하던 코드를 두 줄(assertion과 빈줄까지 합하면 다섯 줄)로 줄였다.