스키마 정의
제목 노드 타입의 스키마 정의:
{
heading: {
content: 'inline*',
group: 'block',
attrs: {
level: { default: 1 }
}
}
}모델 표현
모델 표현 예제:
{
type: 'heading',
attrs: { level: 1 },
children: [
{ type: 'text', text: '메인 제목' }
]
}HTML 직렬화
모델을 HTML로 변환:
function serializeHeading(node) {
const level = node.attrs?.level || 1;
return '<h' + level + '>' +
serializeChildren(node.children) +
'</h' + level + '>';
}HTML 역직렬화
HTML을 모델로 파싱:
function parseHeading(domNode) {
const level = parseInt(domNode.tagName[1]) || 1;
return {
type: 'heading',
attrs: { level },
children: parseChildren(domNode.childNodes)
};
}뷰 연동
뷰 연동 노트: 이 노드 타입을 뷰 레이어에서 구현할 때 contenteditable 동작, 선택 처리, 이벤트 관리에 특히 주의하세요.
뷰 연동 코드:
// 렌더링
const level = node.attrs?.level || 1;
const h = document.createElement('h' + level);
h.contentEditable = 'true';
node.children.forEach(child => {
h.appendChild(renderNode(child));
});
// 레벨 변경 처리
function changeHeadingLevel(node, newLevel) {
if (newLevel < 1 || newLevel > 6) return;
return {
...node,
attrs: { ...node.attrs, level: newLevel }
};
}일반적인 문제
일반적인 함정: 이 노드 타입을 구현할 때 자주 발생하는 문제들입니다. 구현 전에 주의 깊게 검토하세요.
일반적인 문제 및 해결 방법:
// 문제: 제목 레벨 검증
// 해결: 항상 레벨 검증
if (node.attrs.level < 1 || node.attrs.level > 6) {
node.attrs.level = 1;
}
// 문제: 빈 제목
// 해결: 빈 제목 방지 또는 처리
if (node.children.length === 0) {
return '<h' + level + '><br></h' + level + '>';
}
// 문제: 제목 계층 구조 (h3 다음에 h1)
// 해결: 문서 구조 검증
function validateHeadingHierarchy(doc) {
let lastLevel = 0;
// 제목 순서 확인
}구현
완전한 구현 예제:
class HeadingNode {
constructor(attrs, children) {
this.type = 'heading';
this.attrs = { level: attrs?.level || 1 };
this.children = children || [];
}
toDOM() {
const level = this.attrs.level;
const h = document.createElement('h' + level);
this.children.forEach(child => {
h.appendChild(child.toDOM());
});
return h;
}
static fromDOM(domNode) {
const level = parseInt(domNode.tagName[1]) || 1;
const children = Array.from(domNode.childNodes)
.map(node => parseNode(node))
.filter(Boolean);
return new HeadingNode({ level }, children);
}
}