맨앞으로 커서가 튄다.
해결책: 맨뒤로 커서를 조정하자!
그냥 맨 마지막에 커서를 위치 시키면 중간에 글을 수정할 수 없다. 그냥 무조건 마지막에 추가만 되는 에디터가 된다.
해결책: 커서의 위치를 기억해서 하면 어떨까?
그냥 텍스트를 계속 하거나 하면 괜찮은데, 마크업?그 뭐시냐 암튼 그런걸 사용하려면 innerHTML이 변경되고 positon의 위치가 달라지기에 뚱딴지 같은 곳에 커서가 위치되게 된다. 혹은 커서가 그냥 풀린다.
커서가 풀리는 이유:
range가 document로 잡힌다.
내용입력중갹ㄱ
innerHTML: 내용
이렇게 일단 대충 positon이 해당 태그 안에 있고, 해당 태그의 마지막을 range로 하는 것. 하지만 positon이 정확한 위치에 있지 않아서 요상하게 튄다. positon을 제대로 구하는 것이 첫번째 과제인 것 같다.
내용 <div>내용입력중갹ㄱ</div><div>제대"로"</div>
position: 13
slicedText: 갹ㄱ<
innerHTML:
그리고 내가 긁어온 position찾는 함수는 나처럼 태그안의 내용이 태그가 아닌 그냥 text일때만 인것 같아서 position도 따로 구했다.
아 아무리 생각해도 시맨틱 태그로 변경되고 그 변경된걸 적용하고 어쩌구 저꺼구하는 게 너무 복잡해서 정확한 position을 찾는 것이 버그가 많이 생길 것 같다.
해결책: 태그를 변경되는 위치에 삽입했다가 그 태그의 위치로 커서를 둘까?
const getPosition = (parentElement, offset) => {
let currentNode = parentElement;
const indexStack = [];
let count = 0;
while (currentNode === $content) {
let text = currentNode.outerHTML;
count += 1;
indexStack.push(getIndex(currentNode));
currentNode = currentNode.parentElement;
}
for (let i = 0; i < indexStack.length-1; i++) {
currentNode = currentNode.children[indexStack[i]];
}
return currentNode;
$content.addEventListener("compositionend", (e) => {
const selection = window.getSelection();
const node = selection.focusNode;
const offset = selection.focusOffset;
const elementPosition = getPosition(node.parentElement, offset);
const $empty = document.createElement('span');
$empty.setAttribute('class', 'empty');
if(!elementPosition.querySelector('.empty')) {
elementPosition.appendChild($empty)
}
...
}
addRange안되는 문제
commonAncestorContainer의 text을 클릭하면 이렇게 현재 페이지에서 찾을 수 없다고 뜬다.
추가되고 난다음에 새롭게 document.querySelecor로 요소를 선택해서 해주면 된다.
const transformText = (text) => {
return text
.replace("#### ", "")
.replace("### ", "")
.replace("## ", "")
.replace("# ", "")
.replace(/\*\*(.*?)\*\*/g, "")
.replace(/_(.*?)_/g, "")
.replace(/~~(.*?)~~/g, "");
};
$content.addEventListener("compositionend", (e) => {
let selection = window.getSelection();
const node = selection.focusNode;
let offset = selection.focusOffset;
// 첫 입력시 div가 없어서 div를 씌어주기 위한 판별
const isDiv = e.target.innerHTML.includes("<div>");
// span을 삽입할 node를 찾는다.
const elementPosition = getPosition(node.parentElement, offset);
const $empty = document.createElement("span");
$empty.setAttribute("class", "empty");
// 변환이 되면서 줄어든 문자열을 반영
const text = transformText(node.data);
// 문자열의 마지막이 아니라 중간일 경우 offset으로 할당
offset = text === node.data ? offset : text.length;
if (!elementPosition.querySelector(".empty")) {
elementPosition.appendChild($empty);
}
this.setState({
...this.state,
content: isDiv ? e.target.innerHTML : `<div>${e.target.innerHTML}</div>`,
});
// 원래 있던 range를 모두 제거
selection.removeAllRanges();
const range = document.createRange();
const temp = document.querySelector(".empty");
range.setStart(temp.previousSibling, 0);
range.setEnd(temp.previousSibling, offset);
range.collapse(false);
selection.addRange(range);
// 위치를 찾기 위해 임시로 삽입한 span태그 제거
temp.remove();
// onEditing(this.state);
});
const getPosition = (parentElement) => {
let currentNode = parentElement;
const indexStack = [];
while (currentNode === $content) {
indexStack.push(getIndex(currentNode));
currentNode = currentNode.parentElement;
}
for (let i = 0; i < indexStack.length - 1; i++) {
currentNode = currentNode.children[indexStack[i]];
}
return currentNode;
};
const getIndex = (element) => {
let count = 0;
while ((element = element.previousSibling) != null) {
count += 1;
}
return count;
};
transformText가 좀 말썽이다.
정규식 변경
const transformTag = (text) => {
let h1Pattern = /<div>#\s+(.*?)<\/div>/g;
let h2Pattern = /<div>##\s+(.*?)<\/div>/g;
let h3Pattern = /<div>###\s+(.*?)<\/div>/g;
let h4Pattern = /<div>####\s+(.*?)<\/div>/g;
let boldPattern = />(.*?)\*\*(.*?)\*\*(.*?)</g
let italicPattern = />(.*?)_(.*?)_(.*?)</g;
let strikePattern = />(.*?)~~(.*?)~~(.*?)</g;
return text
.replace(h1Pattern, "<div><h1>$1</h1></div>")
.replace(h2Pattern, "<div><h2>$1</h2></div>")
.replace(h3Pattern, "<div><h3>$1</h3></div>")
.replace(h4Pattern, "<div><h4>$1</h4></div>")
.replace(boldPattern, ">$1<b>$2</b>$3<")
.replace(italicPattern, ">$1<i>$2</i>$3<")
.replace(strikePattern, ">$1<s>$2</s>$3<")
.replace(/ /g, " ")
.replace(/\n/g, "<br>")
.replace(/<h1><br><\/h1>/g, "<br>")
.replace(/<h2><br><\/h2>/g, "<br>")
.replace(/<h3><br><\/h3>/g, "<br>")
.replace(/<h4><br><\/h4>/g, "<br>")
.replace(/<i><br><\/i>/g, "<br>")
.replace(/<b><br><\/b>/g, "<br>")
.replace(/<s><br><\/s>/g, "<br>");
};
const transformText = (text) => {
return text
.replace("#### ", "")
.replace("### ", "")
.replace("## ", "")
.replace("# ", "")
.replace(/\*\*(.*?)\*\*/g, "$1")
.replace(/_(.*?)_/g, "$1")
.replace(/~~(.*?)~~/g, "$1");
};
$content.addEventListener("compositionend", (e) => {
let selection = window.getSelection();
const node = selection.focusNode;
let offset = selection.focusOffset;
// 첫 입력시 div가 없어서 div를 씌어주기 위한 판별
const isDiv = e.target.innerHTML.includes("<div>");
// span을 삽입할 node를 찾는다.
const elementPosition = getPosition(node.parentElement, offset);
const $empty = document.createElement("span");
$empty.setAttribute("class", "empty");
// 변환이 되면서 줄어든 문자열을 반영
const text = transformText(node.data);
// 문자열의 마지막이 아니라 중간일 경우 offset으로 할당
// 변환이 됐다면? -> 1
// 변환이 안됐고, 중간
// 변환됐고 끝
if(text !== node.data) {
offset = 1;
if(offset === text.length) {
offset = text.length;
}
} else {
if(offset === node.data.length) {
offset = node.data.length;
}
}
...
selection.removeAllRanges();
const range = document.createRange();
const temp = document.querySelector(".empty");
range.setStart(temp.previousSibling, 0);
range.setEnd(temp.previousSibling, offset);
range.collapse(false);
selection.addRange(range);
잘된다. 하지만
ㅇㅇ띄안 / ㅇㅇ띄안
const deleteText = (text) => {
if (text.indexOf("#") === 0) {
text = text
.replace("#### ", "")
.replace("### ", "")
.replace("## ", "")
.replace("# ", "");
}
return text
.replace(/(.*?)\*\*(.*?)\*\*/g, "")
.replace(/(.*?)_(.*?)_/g, "")
.replace(/(.*?)~~(.*?)~~/g, "");
};
$content.addEventListener("compositionend", (e) => {
let selection = window.getSelection();
const node = selection.focusNode;
let offset = selection.focusOffset;
// 첫 입력시 div가 없어서 div를 씌어주기 위한 판별
const isDiv = e.target.innerHTML.includes("<div>");
// span을 삽입할 node를 찾는다.
const elementPosition = getPosition(node.parentElement, offset);
const $empty = document.createElement("span");
$empty.setAttribute("class", "empty");
// 변환이 되면서 줄어든 문자열을 반영
const text = transformText(node.data);
// 변환이 됐다면
if (text !== node.data) {
// 변환된 글자 뒤에 입력된 값만큼 offset
offset = deleteText(node.data).length;
// 맨마지막에 됐다면 text.length를 offset(아마?)
if (offset === text.length) {
offset = text.length;
}
} else {
// 변환안됐고 마지막? 걍 다 이거인듯
if (offset === node.data.length) {
offset = node.data.length;
}
}
...
}
해결됐다.