FriconiX
Free collection of beautiful vector icons for your web pages.

How to insert MathJax formulas in Quill editor?

Introduction

Quill is proposed with a native module for inserting formulas. This module is based on KaTeX which is great, light and fast. But you may want some extra features like rendering SGV equations. This page proposes to create a new Quill module for inserting MathJax equations.

Here is a preview of the result:

MathJax

To use MathJax, you need to include the MathJax library with something like:

<script type="text/javascript" id="MathJax-script" async
    src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.0.1/es5/latest?tex-mml-svg.js">
</script>

HTML

Here is the HTML:

<button id="delta">Show delta in console</button>
<button id="mathjax">Add MathJax</button>
<div id="editor-container"></div>

The first button is for displaying the Delta content in the console to check if the formula is correctly added in the JSON. The second button is for adding a MathJax formula.

JavaScript: Quill

Let's start by configuring the Quill editor:

var quill = new Quill('#editor-container', {
  placeholder: 'Click the MathJax button to insert a formula.',
  theme: 'snow',
  modules: {
    toolbar: [
        ['bold', 'italic', 'underline', 'strike'],
        ['link'],
        ['blockquote'],
        [{ 'list': 'ordered'}, { 'list': 'bullet' }],
        [{ 'script': 'sub'}, { 'script': 'super' }], 
        ['align', { 'align': 'center' }, { 'align': 'right' }, { 'align': 'justify' }]
    ]
  },
});

Please see to the Quill documentation for more details.

JavaScript: buttons

Let's now catch the button's events. First the button for displaying the Delta content in the console:

// Display the current delta content of the editor in the console
document.getElementById('delta').onclick = () => {
    console.log ( quill.getContents() );
}

And now the MathJax button:

// When the MathJax button is clicked, add a mathjax equation at the current selection
document.getElementById('mathjax').onclick = () => {
    var latex = prompt("Enter a LaTeX formula:", "e=mc^2");
    var range = quill.getSelection(true);
    quill.deleteText(range.index, range.length);
    quill.insertEmbed(range.index, 'mathjax', latex);
    quill.insertText(range.index + range.length + 1 , ' ');
    quill.setSelection(range.index + range.length + 1);
}

The MathJax button displays an input dialog box for entering the LaTeX formula. Then, it get and remove the current selection before inserting the MathJax equation followed by a space. The space is optional, it's a trick to avoid weird behaviors in Mozilla Firefox with the cursor.

The MathJax module

The MathJax module extends the class Embed. The module is composed of 3 methods:

Note that the method tex2svg(latex) is a trick to overcome this MathJax bug. It should become unecessary in the next MathJax release (version 3.0.2).

// Import parchment and delta for creating custom module
const Parchment = Quill.imports.parchment;
const Delta = Quill.imports.delta;

// Extend the embed
class Mathjax extends Parchment.Embed {

    // Create node
    static create(value) 
    {
        const node = super.create(value);    
        if (typeof value === 'string') {
            node.innerHTML = "&#65279;" + this.tex2svg(value) + "&#65279;";
            node.contentEditable = 'false';
            node.setAttribute('data-value', value);         
        }
        return node;
    }

    // Return the attribute value (probably for Delta)
    static value(domNode) 
    {
        return domNode.getAttribute('data-value');
    }

    // Manually render a MathJax equation until version 3.0.2 is not released
    static tex2svg(latex)
    {
        // Create a hidden node and render the formula inside
        let MathJaxNode = document.createElement("DIV");
        MathJaxNode.style.visibility = "hidden";
        MathJaxNode.innerHTML = '\\(' + latex + '\\)';
        document.body.appendChild(MathJaxNode);
        MathJax.typeset();
        let svg = MathJaxNode.innerHTML;
        document.body.removeChild(MathJaxNode);
        return svg;
    }
    /*
    //  Never called ? See : https://stackoverflow.com/questions/60935100/html-method-in-quill-formula-js
    html() {
        const { mathjax } = this.value();
        return `<span>${mathjax}</span>`;
      }
     */
}

// Set module properties
Mathjax.blotName = 'mathjax';
Mathjax.className = 'ql-mathjax';
Mathjax.tagName = 'SPAN';

// Register the module
Quill.register(Mathjax);

Here is the result:

Any improvements are welcome...

See also


Last update : 03/31/2020