WYSIWYG의 Markdown 에디터를 목표로 ContentEditable 및 execCommand와 정면 승부 해 본다 (part 2)
part1 을 아직 보지 않은 분은 그쪽을 먼저 읽어 주시면 이야기의 흐름을 알기 쉬울까 생각합니다.
마지막으로 남겨진 수정점
이전의 구현으로 남겨진 수정점은 다음과 같이 되어 있습니다.
blockquote
는 Enter
이번에는 이것들을 수정하겠습니다.
① 삽입한 요소에 캐럿을 맞출 수 없는 문제에 대해
이전 구현에서는 execCommand
에서 HTML 요소를 변경한 후 innerText
를 비웠습니다.
이 때, caret가 옮겨놓은 요소의 직전의 요소에 (왜인지) 이동해 버리는 것이 원인으로 이러한 문제가 일어나고 있습니다.
그리고 대체한 요소에는 캐럿을 맞출 수 없습니다.
이 문제를 해결하기 위해 요소를 대체 한 후 innerText
를 비우는 대신 공백을 넣음으로써 해결했습니다.
즉,
// 前回のコード
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = ''
// 修正
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = '\xA0'
어떻게 든 캐럿을 맞출 수있었습니다.
하지만 이것에 의해 새로운 문제가 나왔습니다.
위의 Gif 이미지의 끝에 조금 비치고 있습니다만, 개행한 후에 다시 H1
가 삽입되고 있습니다.
이 문제는 마지막 두 번째 문제인 무한 blockquote
와 비슷한 것으로 파악하고 다음 장에서 해결합니다.
또, 브라우저의 표준 기능으로서, 리스트를 빠져 나올 때는 하늘의 행으로 Enter
를 눌러야 합니다.
1. aaa
2. | ←キャレット位置
↓ Enter押下
1. aaa
|
하지만, 방금전의 구현에서는 행의 말미에 공백이 들어가기 때문에, 행이 하늘이라고는 간주되지 않고 개행해도 연장 목록의 그대로입니다.
즉, 리스트나 순서 첨부 리스트를 삽입할 때에는 스페이스를 삽입할 필요는 없을 것 같습니다.
전회의 구현으로 마크다운 기법의 경우 나누기를 했습니다만, 리스트나 순서 첨부 리스트의 때에는 전회와 같이 innerText
를 비우도록(듯이) 합니다.
② 무한 blockquote 에 대해
예에 의해 자세한 원인은 불명합니다만, 전회의 구현에서는 blockquote
속에서 Enter
를 눌러도 빠져나갈 수 없었습니다.
원인을 모르기 때문에, 개행으로 새롭게 삽입된 blockquote
를 execCommand
로 무리하게 div
로 변환하는 구현을 했습니다.
마크다운 기법을 대체한 것과 반대의 방법입니다.
document.execCommand('formatblock', false, 'div')
또한, Shift+Enter
는 같은 요소내에서의 개행이므로, Shift
없음의 Enter
만을 포착해 상기의 커멘드를 실행할 필요가 있습니다.
또, 리스트나 순서 첨부 리스트는 브라우저의 표준 기능으로 바람직한 움직임을 하므로, div
로 옮겨놓는 처리를 적응하지 않아야 합니다.
Shift
의 캡처는 키 이벤트의 .shiftKey
속성에서 사용할 수 있습니다.
리스트의 판정은, caret가 있는 요소명을 취득하는 것으로 했습니다.
상기를 정리하면 이하의 처리가 됩니다.
if (!event.shiftKey && event.keyCode === 13) {
if (!focusingOnOrderElement()) {
document.execCommand('formatblock', false, 'div')
}
}
const focusingOnOrderElement = () => {
const element_name = window.getSelection().getRangeAt(0).endContainer.parentNode.nodeName
return (element_name === 'LI' || element_name === 'UL' || element_name === 'OL')
}
완성형
이번 개량점을 정리하면 다음과 같습니다.
const element = document.getElementById('markdown')
element.focus()
element.addEventListener('keyup', (event) => {
const currentLine = window.getSelection().getRangeAt(0).endContainer.data || ''
if (!event.shiftKey && event.keyCode === 13) {
if (!focusingOnOrderElement()) {
document.execCommand('formatblock', false, 'div')
}
}else{
if (currentLine.match(/^#{1}\xA0$/)) { // 見出し
document.execCommand('formatblock', false, 'h1')
clearCurrentLine()
} else if (currentLine.match(/^#{2}\xA0$/)){
document.execCommand('formatblock', false, 'h2')
clearCurrentLine()
} else if (currentLine.match(/^#{3}\xA0$/)) {
document.execCommand('formatblock', false, 'h3')
clearCurrentLine()
} else if (currentLine.match(/^#{4}\xA0$/)) {
document.execCommand('formatblock', false, 'h4')
clearCurrentLine()
} else if (currentLine.match(/^#{5}\xA0$/)) {
document.execCommand('formatblock', false, 'h5')
clearCurrentLine()
} else if (currentLine.match(/^#{6}\xA0$/)) {
document.execCommand('formatblock', false, 'h6')
clearCurrentLine()
} else if (currentLine.match(/^>\xA0$/)) { // 引用
document.execCommand('formatblock', false, 'blockquote')
clearCurrentLine()
} else if (currentLine.match(/^\d+\.\xA0$/)) { // 順序付きリスト
document.execCommand('insertOrderedList')
clearCurrentLine('')
} else if (currentLine.match(/^[\-+*]\xA0+$/)) { // リスト
document.execCommand('insertUnorderedList')
clearCurrentLine('')
}
}
})
const clearCurrentLine = (clearCharacter = '\xA0') => {
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = clearCharacter
}
const focusingOnOrderElement = () => {
const element_name = window.getSelection().getRangeAt(0).endContainer.parentNode.nodeName
return (element_name === 'LI' || element_name === 'UL' || element_name === 'OL')
}
데모는 여기입니다.
다음 번에
이번 구현으로 Chrome에서도 어떻게든 움직이게되었습니다 (Safari와 Chrome에서만 시도하고 있습니다)
다음 번 이후에는 강조나 이탤릭 등의 문두가 아닌 마크다운 기법의 판정을 할 수 있으면 좋겠습니다.
난문이었지만 끝까지 읽어 주셔서 감사합니다 m (_ _) m
개선점 등 있으면, 교수 바랍니다.
// 前回のコード
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = ''
// 修正
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = '\xA0'
1. aaa
2. | ←キャレット位置
↓ Enter押下
1. aaa
|
예에 의해 자세한 원인은 불명합니다만, 전회의 구현에서는
blockquote
속에서 Enter
를 눌러도 빠져나갈 수 없었습니다.원인을 모르기 때문에, 개행으로 새롭게 삽입된
blockquote
를 execCommand
로 무리하게 div
로 변환하는 구현을 했습니다.마크다운 기법을 대체한 것과 반대의 방법입니다.
document.execCommand('formatblock', false, 'div')
또한,
Shift+Enter
는 같은 요소내에서의 개행이므로, Shift
없음의 Enter
만을 포착해 상기의 커멘드를 실행할 필요가 있습니다.또, 리스트나 순서 첨부 리스트는 브라우저의 표준 기능으로 바람직한 움직임을 하므로,
div
로 옮겨놓는 처리를 적응하지 않아야 합니다.Shift
의 캡처는 키 이벤트의 .shiftKey
속성에서 사용할 수 있습니다.리스트의 판정은, caret가 있는 요소명을 취득하는 것으로 했습니다.
상기를 정리하면 이하의 처리가 됩니다.
if (!event.shiftKey && event.keyCode === 13) {
if (!focusingOnOrderElement()) {
document.execCommand('formatblock', false, 'div')
}
}
const focusingOnOrderElement = () => {
const element_name = window.getSelection().getRangeAt(0).endContainer.parentNode.nodeName
return (element_name === 'LI' || element_name === 'UL' || element_name === 'OL')
}
완성형
이번 개량점을 정리하면 다음과 같습니다.
const element = document.getElementById('markdown')
element.focus()
element.addEventListener('keyup', (event) => {
const currentLine = window.getSelection().getRangeAt(0).endContainer.data || ''
if (!event.shiftKey && event.keyCode === 13) {
if (!focusingOnOrderElement()) {
document.execCommand('formatblock', false, 'div')
}
}else{
if (currentLine.match(/^#{1}\xA0$/)) { // 見出し
document.execCommand('formatblock', false, 'h1')
clearCurrentLine()
} else if (currentLine.match(/^#{2}\xA0$/)){
document.execCommand('formatblock', false, 'h2')
clearCurrentLine()
} else if (currentLine.match(/^#{3}\xA0$/)) {
document.execCommand('formatblock', false, 'h3')
clearCurrentLine()
} else if (currentLine.match(/^#{4}\xA0$/)) {
document.execCommand('formatblock', false, 'h4')
clearCurrentLine()
} else if (currentLine.match(/^#{5}\xA0$/)) {
document.execCommand('formatblock', false, 'h5')
clearCurrentLine()
} else if (currentLine.match(/^#{6}\xA0$/)) {
document.execCommand('formatblock', false, 'h6')
clearCurrentLine()
} else if (currentLine.match(/^>\xA0$/)) { // 引用
document.execCommand('formatblock', false, 'blockquote')
clearCurrentLine()
} else if (currentLine.match(/^\d+\.\xA0$/)) { // 順序付きリスト
document.execCommand('insertOrderedList')
clearCurrentLine('')
} else if (currentLine.match(/^[\-+*]\xA0+$/)) { // リスト
document.execCommand('insertUnorderedList')
clearCurrentLine('')
}
}
})
const clearCurrentLine = (clearCharacter = '\xA0') => {
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = clearCharacter
}
const focusingOnOrderElement = () => {
const element_name = window.getSelection().getRangeAt(0).endContainer.parentNode.nodeName
return (element_name === 'LI' || element_name === 'UL' || element_name === 'OL')
}
데모는 여기입니다.
다음 번에
이번 구현으로 Chrome에서도 어떻게든 움직이게되었습니다 (Safari와 Chrome에서만 시도하고 있습니다)
다음 번 이후에는 강조나 이탤릭 등의 문두가 아닌 마크다운 기법의 판정을 할 수 있으면 좋겠습니다.
난문이었지만 끝까지 읽어 주셔서 감사합니다 m (_ _) m
개선점 등 있으면, 교수 바랍니다.
const element = document.getElementById('markdown')
element.focus()
element.addEventListener('keyup', (event) => {
const currentLine = window.getSelection().getRangeAt(0).endContainer.data || ''
if (!event.shiftKey && event.keyCode === 13) {
if (!focusingOnOrderElement()) {
document.execCommand('formatblock', false, 'div')
}
}else{
if (currentLine.match(/^#{1}\xA0$/)) { // 見出し
document.execCommand('formatblock', false, 'h1')
clearCurrentLine()
} else if (currentLine.match(/^#{2}\xA0$/)){
document.execCommand('formatblock', false, 'h2')
clearCurrentLine()
} else if (currentLine.match(/^#{3}\xA0$/)) {
document.execCommand('formatblock', false, 'h3')
clearCurrentLine()
} else if (currentLine.match(/^#{4}\xA0$/)) {
document.execCommand('formatblock', false, 'h4')
clearCurrentLine()
} else if (currentLine.match(/^#{5}\xA0$/)) {
document.execCommand('formatblock', false, 'h5')
clearCurrentLine()
} else if (currentLine.match(/^#{6}\xA0$/)) {
document.execCommand('formatblock', false, 'h6')
clearCurrentLine()
} else if (currentLine.match(/^>\xA0$/)) { // 引用
document.execCommand('formatblock', false, 'blockquote')
clearCurrentLine()
} else if (currentLine.match(/^\d+\.\xA0$/)) { // 順序付きリスト
document.execCommand('insertOrderedList')
clearCurrentLine('')
} else if (currentLine.match(/^[\-+*]\xA0+$/)) { // リスト
document.execCommand('insertUnorderedList')
clearCurrentLine('')
}
}
})
const clearCurrentLine = (clearCharacter = '\xA0') => {
window.getSelection().getRangeAt(0).endContainer.parentNode.innerText = clearCharacter
}
const focusingOnOrderElement = () => {
const element_name = window.getSelection().getRangeAt(0).endContainer.parentNode.nodeName
return (element_name === 'LI' || element_name === 'UL' || element_name === 'OL')
}
이번 구현으로 Chrome에서도 어떻게든 움직이게되었습니다 (Safari와 Chrome에서만 시도하고 있습니다)
다음 번 이후에는 강조나 이탤릭 등의 문두가 아닌 마크다운 기법의 판정을 할 수 있으면 좋겠습니다.
난문이었지만 끝까지 읽어 주셔서 감사합니다 m (_ _) m
개선점 등 있으면, 교수 바랍니다.
Reference
이 문제에 관하여(WYSIWYG의 Markdown 에디터를 목표로 ContentEditable 및 execCommand와 정면 승부 해 본다 (part 2)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/TomOse/items/cc79eff90485a907ee15텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)