Quill: Custom inline blot with <span> and className not working.

Created on 13 Dec 2017  ·  9Comments  ·  Source: quilljs/quill

I'm creating a custom inline blot. I want to use span and style it with css. I read in the: "Cloning Medium with Parchment" tutorial the following:

Note Inline blots use <span> and Block Blots use <p> by default, so if you would like to use these tags for your custom Blots, you will have to specify a className in addition to a tagName.

And I'm creating this:

class TestBlot extends Inline { }
TestBlot.blotName = 'thick';
TestBlot.tagName = 'span';
TestBlot.className = 'test-class';
Quill.register(TestBlot);

And it's not working. But if I change the tagName to spann it does work! What am I missing here?! What's the right way of doing this?

Thanks!

Most helpful comment

Adding to what @dkreft said (which completely fixed my same issue):

Looking at that codeblock (from https://github.com/quilljs/parchment/blob/master/src/blot/abstract/format.ts#L11-L18)

static formats(domNode: HTMLElement): any {
    if (typeof this.tagName === 'string') {
      return true;
    } else if (Array.isArray(this.tagName)) {
      return domNode.tagName.toLowerCase();
    }
    return undefined;
  }

Because the first check in formats is to determine if tagName is a string, and thus returns true, then since my tagName = 'span', then I just added my own formats function like this:

static formats(): boolean {
  return true;
}

Still seems silly for this to be required, but until there's a legit fix internally, this seems to be working properly. I can't think of any possible side effects this would have since in my case (with tagName = 'span') this function should've normally returned true. Only thing I don't quite understand why the InlineBlot would first do this if (domNode.tagName === InlineBlot.tagName) return undefined; in its formats function.

So, my stripped down blot class looks like this:

export class TagBlot extends Inline {
  static blotName = 'tag';
  static className = 'aur-tag';
  static tagName = 'span';

  static formats(): boolean {
    return true;
  }
}

And it's finally appending the span.aur-tag element to the selection in my editor

All 9 comments

Please follow the issue template and include all the requested info.
Also, fork the CodePen show what you tried to do and what doesn't work.

Thanks @benbro! Here is the template!

Steps for Reproduction

  1. https://codepen.io/andresyo990/pen/jYPmmo?editors=0110
  2. Write something on the editor, select what you wrote, and touch the "Test" button
  3. Nothing happens.

Expected behavior:

A new <span class="test-class">selected text</span> is created in the html editor.

Actual behavior:

Nothing happens.

Platforms:

Chrome, Safari, Firefox (and I think all the others but those are the ones I have)

Version:

Version: 1.3.4

Hi @benbro,

That link doesn't resolve anymore. Is it possible for someone to repeat the information? I'm interested in how to get around this too.

@benbro. I am experiencing the same issue, and finding that the link is now a dead end.

@dannyrb, @chrisronline : Take a look at https://github.com/quilljs/parchment/blob/master/src/blot/inline.ts#L23

There you will see this:

if (domNode.tagName === InlineBlot.tagName) return undefined;

It's not clear why this line is there or why it should return undefined, but if that line is commented out, you then invoke FormatBlot.formats(domNode) (https://github.com/quilljs/parchment/blob/master/src/blot/abstract/format.ts#L11-L18)

static formats(domNode: HTMLElement): any {
    if (typeof this.tagName === 'string') {
      return true;
    } else if (Array.isArray(this.tagName)) {
      return domNode.tagName.toLowerCase();
    }
    return undefined;
  }

On a whim, I simply did this:

export default MyCustomInline extends Inline {
  static tagName = 'span'

  formats() {
    return MyCustomInline.tagName
  }
}

...and that got things working for me.

I have no idea what side effects my "solution" will have, as I'm still learning my way about the API, and the documentation only says:

  // Returns format values represented by domNode if it is this Blot's type
  // No checking that domNode is this Blot's type is required.
  static formats(domNode: Node);

...which seems rather arcane and unhelpful.

When I set a breakpoint inside InlineBlot#parchment() (https://github.com/quilljs/parchment/blob/master/src/blot/inline.ts#L50-L61), I can see that other inline elements (e.g. <b> and <i>) return an object from .formats():

  {
    bold: true
  }

But it really doesn't seem to matter (as far as I've cared to dig) whether you return a mapping of tags to truthy values or if you simply return a string...though I am assuming that the mapping is more correct...again, for reasons that do not appear to be documented.

It probably goes without saying that It's very disappointing to have this issue unceremoniously closed by @benbro with nothing more than a link to SO (which has been noted already, is dead), and then pretty much ignored as others have expressed a desire for a real resolution. This does not reflect well on the project.

@dkreft completely forgot about this issue. I had a similar experience. I kept playing and landed on something that looked like this:

// https://github.com/quilljs/parchment/#blots
import Quill from 'quill'
let Inline = Quill.import('blots/inline')

class SpellingBlot extends Inline {
  static create(value) {
    let node = super.create(value)
    node.setAttribute('data-invalid-spelling', '')
    node.setAttribute('data-alternative-spellings', [])
    return node
  }

  static formats(domNode) {
    return {
      isInvalidSpelling: domNode.hasAttribute('data-invalid-spelling'),
      alternativeSpellings: domNode.getAttribute('data-alternative-spellings'),
    }
  }
}

SpellingBlot.blotName = 'spelling'
SpellingBlot.className = 'spelling'
SpellingBlot.tagName = 'SPAN'

export default SpellingBlot

Originally, I only wanted the spelling class applied to the formatted section. When I couldn't get that to work, I began looking at code that would allow me to set a custom data-* annotation. After adding the create and formats methods, the spelling class appeared.

This seems to imply that there is more required to creating an extended Inline Blot than suggested in the documentation. 🤷‍♂️

I'm having the same issue. Any updates here? I'm unable to figure out Inline with span and class.

Adding to what @dkreft said (which completely fixed my same issue):

Looking at that codeblock (from https://github.com/quilljs/parchment/blob/master/src/blot/abstract/format.ts#L11-L18)

static formats(domNode: HTMLElement): any {
    if (typeof this.tagName === 'string') {
      return true;
    } else if (Array.isArray(this.tagName)) {
      return domNode.tagName.toLowerCase();
    }
    return undefined;
  }

Because the first check in formats is to determine if tagName is a string, and thus returns true, then since my tagName = 'span', then I just added my own formats function like this:

static formats(): boolean {
  return true;
}

Still seems silly for this to be required, but until there's a legit fix internally, this seems to be working properly. I can't think of any possible side effects this would have since in my case (with tagName = 'span') this function should've normally returned true. Only thing I don't quite understand why the InlineBlot would first do this if (domNode.tagName === InlineBlot.tagName) return undefined; in its formats function.

So, my stripped down blot class looks like this:

export class TagBlot extends Inline {
  static blotName = 'tag';
  static className = 'aur-tag';
  static tagName = 'span';

  static formats(): boolean {
    return true;
  }
}

And it's finally appending the span.aur-tag element to the selection in my editor

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benbro picture benbro  ·  3Comments

GildedHonour picture GildedHonour  ·  3Comments

lastmjs picture lastmjs  ·  3Comments

DaniilVeriga picture DaniilVeriga  ·  3Comments

ouhman picture ouhman  ·  3Comments