텍스트 노드 조작 insertData

내 이 세상 도처에서 쉴 곳을 찾아보았으나, 마침내 찾아낸, 컴퓨터가 있는 구석방보다 나은 곳은 없더라.

텍스트 노드 조작 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과 빈줄까지 합하면 다섯 줄)로 줄였다.