Quill link handler not working

congregating all the information

The snow theme itself uses the toolbar's addHandler to show a tooltip and so it is impossible to use the addHandler method to achieve what we wish to.

So, instead we can do the following:

var Link = Quill.import('formats/link');
var builtInFunc = Link.sanitize;
Link.sanitize = function customSanitizeLinkInput(linkValueInput) {
    var val = linkValueInput;

    // do nothing, since this implies user's already using a custom protocol
    if (/^\w+:/.test(val));
    else if (!/^https?:/.test(val))
        val = "http:" + val;

    return builtInFunc.call(this, val); // retain the built-in logic
};

this method doesn't hook onto handlers but instead modifies the built-in sanitisation logic itself. We have also retained the original behavior of the sanitisation so that doesn't modify the editor's original behavior.

Alternatively, we could actually hook onto the save button of the tooltip, using this code. But it is too long a method compared to the one above.


As far as I can tell, the handling of creating and updating links is a bit distributed in Quill's sources. The default Snow theme handles editing links to some extent: it tracks the user selection and last selected link internally. Because of this I do not think that it is possible to achieve what you want currently in Quill using only a custom handler.

You may want to open an issue to report this, the authors might be willing to add such a handler.

In the meantime I came up with a way to update the link by simply listening for events causing the edit tooltip to close. There are some complications, because a link can be edited and the theme then relies on its internal tracking to update it. However, all in all I think that this solution is not too bad. You might want to add some error checking here and there, but overall it seems to work nicely and do what you want it do to. I have created a Fiddle demonstrating this. For completeness, I have included it here as a code snippet too.

var quill = new Quill('#editor', {
    modules: {
      toolbar: true
    },
    theme: 'snow'
  }),
  editor = document.getElementById('editor'),
  lastLinkRange = null;

/**
 * Add protocol to link if it is missing. Considers the current selection in Quill.
 */
function updateLink() {
  var selection = quill.getSelection(),
    selectionChanged = false;
  if (selection === null) {
    var tooltip = quill.theme.tooltip;
    if (tooltip.hasOwnProperty('linkRange')) {
      // user started to edit a link
      lastLinkRange = tooltip.linkRange;
      return;
    } else {
      // user finished editing a link
      var format = quill.getFormat(lastLinkRange),
        link = format.link;
      quill.setSelection(lastLinkRange.index, lastLinkRange.length, 'silent');
      selectionChanged = true;
    }
  } else {
    var format = quill.getFormat();
    if (!format.hasOwnProperty('link')) {
      return; // not a link after all
    }
    var link = format.link;
  }
  // add protocol if not there yet
  if (!/^https?:/.test(link)) {
    link = 'http:' + link;
    quill.format('link', link);
    // reset selection if we changed it
    if (selectionChanged) {
      if (selection === null) {
        quill.setSelection(selection, 0, 'silent');
      } else {
        quill.setSelection(selection.index, selection.length, 'silent');
      }
    }
  }
}

// listen for clicking 'save' button
editor.addEventListener('click', function(event) {
  // only respond to clicks on link save action
  if (event.target === editor.querySelector('.ql-tooltip[data-mode="link"] .ql-action')) {
    updateLink();
  }
});

// listen for 'enter' button to save URL
editor.addEventListener('keydown', function(event) {
  // only respond to clicks on link save action
  var key = (event.which || event.keyCode);
  if (key === 13 && event.target === editor.querySelector('.ql-tooltip[data-mode="link"] input')) {
    updateLink();
  }
});
<link href="https://cdn.quilljs.com/1.1.10/quill.snow.css" rel="stylesheet" />
<script src="https://cdn.quilljs.com/1.1.10/quill.min.js"></script>
<div id="editor"></div>

Let me know if you have any questions.