All about quill blots, and how to make a custom quill blot

Clock
24.10.2024
An eye
119
Hearts
0
Connected dots
0
Connected dots
0
Connected dots
0

List of blots and how to get them

Blot is a building block of document used by parchment. Their various types, examples of useage and implementation can be viewed on my online quill editor.
As you will see below, there are quite a few of these building blocks. And each of them exists to perform a specific role in the document. I will talk about the role of each blot separately, but before this, let's see what kinds of blots we have in QuillJs::
  1. block
  2. block/embed
  3. break
  4. container
  5. cursor
  6. embed
  7. inline
  8. scroll
  9. text
To import any blot from this list into a new JS variable, enter the blot name after blots, like this:
let blot = Quill.imports["blots/block"]
To see a full list of all available blots, type in the browser console:
Quill.imports

About each blot

Inline blot. Examples of using this block are bold, italic, or anything underlined. This is the simplest block; it simply wraps the selected block in a prespecified tag with prespecified classes. And it can be wrapped around another inline block.
Block blot. For example, a block of quotes, a block of code, or headings. It is important to understand how this block works. It does not simply wrap a selected block in a certain tag - yes, a block, not text - it just replaces it. That is, block blots cannot be inside each other, as is the case with inline blocks.
Container blot. They are created in order to overcome the inability of previous blocks to be embedded in one another. In other articles, you will see that we will have to specify in advance the elements that can be inside each other ;) An example of the implementation of such blocks are lists and tables.
Block/embed and embed. They work the same way as regular blocks, but with the only difference that the content they embed cannot be edited. It can be reloaded or deleted, but not changed. These are videos, formulas, and images.
Scroll blot. It is the main container for all the others. What is it? By default, it is a DIV element with the ql-editor class. Which can contain the following types of blots:
  1. block
  2. embed
  3. container
Another important feature of this blot is that the constructor accepts a special object as the first parameter - a register. It is too early to talk about Quill registers, but let's say that each register is a unique set of buttons and formatters for editing text. That is, on one page you can have several text editors with their own unique functionality.
Break blot. This is a special zero element. In the Parchment library, each container that can have children must have at least one. And this blot was created to play the role of the initial element.
Text blot. It is a regular wrapper around the text
Cursor blot. It is a vertical line when editing. And it knows where and on which block it is located.

All blot methods and members to override

In this chapter, I have collected all possible methods for overriding and added some comments to them. I did this because the official site does not have such information, although it can still be found through the browser console.

Block blot

Methods for overriding and a source code -> https://github.com/slab/parchment/blob/main/src/blot/block.ts
static create(value?: unknown);
static formats(domNode: HTMLElement, scroll: Root): any;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formats(): { [index: string]: any };
formatAt( index: number, length: number, name: string, value: any ): void;
insertAt(index: number, value: string, def?: any): void;
replaceWith(name: string | Blot, value?: any): Blot;
deleteAt(index: number, length: number): void;
delta(): void;
insertBefore(blot: Blot, ref?: Blot | null): void;
length(): number;
moveChildren(target: Parent, ref?: Blot | null): void;
optimize(context: { [key: string]: any }): void;
path(index: number) [Blot, number][];
removeChild(child: Blot): void;
split(index: number, force: boolean | undefined = false): Blot | null;
A default members and their values:
static blotName = 'block';
static scope = Scope.BLOCK_BLOT;
static tagName: string | string[] = 'P';
static allowedChildren: BlotConstructor[] = [InlineBlot, BlockBlot, LeafBlot ];
length: number;
name: string;

Inline blot

Methods for overriding and a source code -> https://github.com/slab/parchment/blob/main/src/blot/inline.ts
static create(value?: unknown): Node;
static formats(domNode: HTMLElement, scroll: Root): any;
static compare(self: string, other: string): number;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formats(): { [index: string]: any };
formatAt( index: number, length: number, name: string, value: any ): void;
optimize(context: { [key: string]: any }): void;
replaceWith(name: string | Blot, value?: any): Blot;
replaceWith(name: string | Blot, value?: any): Blot;
update( mutations: MutationRecord[], context: { [key: string]: any } ): void;
public wrap(name: string | Parent, value?: any): Parent;
A default members and their values:
static blotName = 'inline';
static order = [
'cursor',
'inline',
'link',
'underline',
'strike',
'italic',
'bold',
'script',
'code',
];
static scope = Scope.INLINE_BLOT;
static tagName: string | string[] = 'SPAN';
static allowedChildren: BlotConstructor[] = [InlineBlot, LeafBlot ];
length: number;
name: string;

Embed blot

Methods for overriding and a source code -> https://github.com/slab/parchment/blob/main/src/blot/embed.ts
static create(value?: unknown): Node;
static formats(_domNode: HTMLElement, _scroll: Root): any;
constructor(scroll: Root, domNode: Node);
format(name: string, value: any): void;
formatAt( index: number, length: number, name: string, value: any ): void;
formats(): { [index: string]: any };
index(node: Node, offset: number): number;
restore(node: Text): EmbedContextRange | null;
update(mutations: MutationRecord[], context: Record<string, unknown>): void;
A default members and their values:
static scope = Scope.INLINE_BLOT;
length: number;
name: string;
contentNode: HTMLSpanElement;
leftGuard: Text;
rightGuard: Text;

Cursor blot

Methods for overriding.
static create(value?: unknown): Node;
static formats(_domNode: HTMLElement, _scroll: Root): any;
constructor(scroll: ScrollBlot, domNode: HTMLElement, selection: Selection): void;
detach(): void;
format(name: string, value: unknown): void;
formatAt( index: number, length: number, name: string, value: any ): void;
formats(): { [index: string]: any };
index(node: Node, offset: number): number;
length(): number;
position(): [Text, number];
remove(): void;
restore(): EmbedContextRange | null;
update(mutations: MutationRecord[], context: Record<string, unknown>): void;
optimize(context?: unknown): void;
value() string; // Will return ''
A default members and their values:
static blotName = 'cursor';
static className = 'ql-cursor';
static tagName = 'span';
static CONTENTS = '\uFEFF'; // Zero width no break space
length: number;
name: string;

Text blot

Methods for overriding and a source code -> https://github.com/slab/parchment/blob/main/src/blot/text.ts
static create(value: string): Node;
static value(domNode: Text): string;
constructor(scroll: Root, node: Node);
deleteAt(index: number, length: number): void;
index(node: Node, offset: number): number;
insertAt(index: number, value: string, def?: any): void;
length(): number
optimize(context: { [key: string]: any }): void;
position(index: number, _inclusive = false): [Node, number];
split(index: number, force = false): Blot | null;
update( mutations: MutationRecord[], _context: { [key: string]: any } ): void;
value(): string;
A default members and their values:
static readonly blotName = 'text';
domNode: Text;
static scope = Scope.INLINE_BLOT;

Container blot

static create(value: string): Node;
constructor(scroll: Root, domNode: Node);
checkMerge(): boolean;
deleteAt(index: number, length: number): void ;
formatAt( index: number, length: number, name: string, value: any ): void;
optimize(context: { [key: string]: any }): void;
insertAt(index: number, value: string, def?: any): void;
A default members and their values:
static blotName = 'container';
static scope = Scope.BLOCK_BLOT;
static tagName: string | string[];
prev: BlockBlot | ContainerBlot | null;
next: BlockBlot | ContainerBlot | null;

Scroll blot

Methods for overriding and a source code -> https://github.com/slab/parchment/blob/main/src/blot/scroll.ts
static create(value: string): Node;
constructor( public registry: Registry, node: HTMLDivElement, { emitter }: { emitter: Emitter } );
batchStart(): void;
batchEnd(): void;
emitMount(blot: Blot): void;
emitUnmount(blot: Blot): void;
emitEmbedUpdate(blot: Blot, change: unknown): void;
deleteAt(index: number, length: number): void;
enable(enabled = true): void;
formatAt(index: number, length: number, format: string, value: unknown): void;
insertAt(index: number, value: string, def?: unknown): void;
insertBefore(blot: Blot, ref?: Blot | null): void;
insertContents(index: number, delta: Delta): void;
isEnabled(): boolean;
leaf(index: number): [LeafBlot | null, number];
line(index: number): [Block | BlockEmbed | null, number];
lines(index = 0, length = Number.MAX_VALUE): (Block | BlockEmbed)[]
optimize(context?: { [key: string]: any }): void;
optimize( mutations?: MutationRecord[], context?: { [key: string]: any }): void;
optimize(mutations = [], context = {}): void;
path(index: number): number;
remove(): void; // Do nothing
update(source?: EmitterSource): void;
update(mutations?: MutationRecord[]): void;
update(mutations?: MutationRecord[] | EmitterSource): void;
updateEmbedAt(index: number, key: string, change: unknown): void;
A default members and their values:
static blotName = 'scroll';
static className = 'ql-editor';
static tagName = 'DIV';
static defaultChild = Block;
static allowedChildren = [Block, BlockEmbed, Container];
emitter: Emitter;
batch: false | MutationRecord[];

How to make a new one blot and override a default one

For the example of overriding an existing blot, I'll use block blot. Let's start with import.
let Block = Quill.import('blots/block');
Next, you need to define new blot functionality. In my case, I want to use <div> instead of <p> tag and add a class named "txt". This is how I will do it:
Block.tagName = 'div'
class TextBlock extends Block{

static create(value){
let node = super.create(value);
node.setAttribute('class','txt');
return node;
}
}
// Register (updating block blot)
Quill.register(TextBlock, true)
This is about modifying blots. But how to make a new one? Any quill block has 3 variables that are used to distinguish one block from another. The first is tagName, the second is blotName and the third is className.
To create a new blot, you need to set a different value for blotName. I will demonstrate this using links as an example. We will create 2 types of links:
  1. Internal links
  2. External links
// Import inline blot
let Inline = Quill.import('blots/inline')

// Setting up a new inline blot
class InternalLink extends Inline{
constructor(scroll, domNode){
super(scroll, domNode);
domNode.addEventListener('click', (ev) => {
domNode.setAttribute('ref', 'me')
})
}
}
InternalLink.tagName = 'a'
InternalLink.className = 'ref-int'
// New blots name
InternalLink.blotName = 'internal-link'
// Register a new formater
Quill.register({'formats/internal-link': InternalLink})

// Setting up a new inline blot
class ExternalLink extends Inline{
constructor(scroll, domNode){
super(scroll, domNode);
domNode.setAttribute('target', '_blank')
domNode.setAttribute('ref', 'noreferrer nofollow external')
}
}
ExternalLink.tagName = 'a'
ExternalLink.className = 'ref-ext'
// New blots name
ExternalLink.blotName = 'external-link'
// Register a new formater
Quill.register({'formats/external-link': ExternalLink})
You're probably wondering what this "formater" is. But basically, I just made a button to add and delete this type of blot in the editor.

Comments

(0)
captcha
Send
It's empty now. Be the first (o゚v゚)ノ

Similar articles

Quill formats, how to overwrite them and types

Clock
24.10.2024
In this article, I will analyze the types of quilljs formatters and explain some of the nuances of working with tables, fonts, images, and video. I will also show how …

Quill formats, how to overwrite them and types

Clock
24.10.2024
In this article, I will analyze the types of quilljs formatters and explain some of the nuances of working with tables, fonts, images, and video. I will also show how …