Schema Definition
Schema definition for the Image node type:
{
image: {
inline: true,
attrs: {
src: { default: '' },
alt: { default: '' },
title: { default: null },
width: { default: null },
height: { default: null }
}
}
}Model Representation
Example model representation:
{
type: 'image',
attrs: {
src: 'https://example.com/image.jpg',
alt: 'Description',
width: 800,
height: 600
}
}HTML Serialization
Converting model to HTML:
function serializeImage(node) {
const attrs = {};
if (node.attrs.src) attrs.src = escapeHtml(node.attrs.src);
if (node.attrs.alt) attrs.alt = escapeHtml(node.attrs.alt);
if (node.attrs.title) attrs.title = escapeHtml(node.attrs.title);
if (node.attrs.width) attrs.width = node.attrs.width;
if (node.attrs.height) attrs.height = node.attrs.height;
return '<img' + serializeAttrs(attrs) + '>';
}HTML Deserialization
Parsing HTML to model:
function parseImage(domNode) {
return {
type: 'image',
attrs: {
src: domNode.getAttribute('src') || '',
alt: domNode.getAttribute('alt') || '',
title: domNode.getAttribute('title') || null,
width: parseInt(domNode.getAttribute('width')) || null,
height: parseInt(domNode.getAttribute('height')) || null
}
};
}View Integration
View Integration Notes: Pay special attention to contenteditable behavior, selection handling, and event management when implementing this node type in your view layer.
View integration code:
// Rendering image
const img = document.createElement('img');
img.src = node.attrs.src;
img.alt = node.attrs.alt || '';
if (node.attrs.title) img.title = node.attrs.title;
if (node.attrs.width) img.width = node.attrs.width;
if (node.attrs.height) img.height = node.attrs.height;
img.contentEditable = 'false'; // Images are typically not editable
// Image upload handler
function handleImageUpload(file) {
const reader = new FileReader();
reader.onload = (e) => {
insertNode({
type: 'image',
attrs: {
src: e.target.result,
alt: file.name
}
});
};
reader.readAsDataURL(file);
}Common Issues
Common Pitfalls: These are issues frequently encountered when implementing this node type. Review carefully before implementation.
Common issues and solutions:
// Issue: Image loading errors
// Solution: Handle broken images
img.onerror = function() {
this.src = 'placeholder.jpg';
this.alt = 'Image failed to load';
};
// Issue: Image resizing
// Solution: Maintain aspect ratio
function resizeImage(img, maxWidth, maxHeight) {
const ratio = Math.min(maxWidth / img.width, maxHeight / img.height);
img.width = img.width * ratio;
img.height = img.height * ratio;
}
// Issue: Image selection
// Solution: Handle image selection in contenteditable
function selectImage(img) {
const range = document.createRange();
range.selectNode(img);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}Implementation
Complete implementation example:
class ImageNode {
constructor(attrs) {
this.type = 'image';
this.attrs = {
src: attrs?.src || '',
alt: attrs?.alt || '',
title: attrs?.title || null,
width: attrs?.width || null,
height: attrs?.height || null
};
}
toDOM() {
const img = document.createElement('img');
Object.assign(img, this.attrs);
return img;
}
static fromDOM(domNode) {
return new ImageNode({
src: domNode.getAttribute('src') || '',
alt: domNode.getAttribute('alt') || '',
title: domNode.getAttribute('title') || null,
width: parseInt(domNode.getAttribute('width')) || null,
height: parseInt(domNode.getAttribute('height')) || null
});
}
}