React-quill: Problem with custom handlers

Created on 27 Jun 2017  Β·  8Comments  Β·  Source: zenoamaro/react-quill

Hi, everyone!
First of all, thank you Alex for your great work with updating the react quill!

I have a problem with custom handlers in my react app. When person click to 'link' in toolbar and he have not got a selected text he does not understand what the program expected him to do. So, I have found such lines in the file with theme:

SnowTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, {
  modules: {
    toolbar: {
      handlers: {
        link: function(value) {
          if (value) {
            let range = this.quill.getSelection();
            if (range == null || range.length == 0) return;
            let preview = this.quill.getText(range);
            if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {
              preview = 'mailto:' + preview;
            }
            let tooltip = this.quill.theme.tooltip;
            tooltip.edit('link', preview);
          } else {
            this.quill.format('link', false);
          }
        }
      }
    }
  }
});

so, after the line with 'if (range == null || range.length == 0) return;' I want to put my notice instead of the return.

In my app I want to redefine the 'link' handler like this

render() {
    let modules = {
         toolbar: {
             container: [
                 ['blockquote', 'image'],
                 ['link', 'video'],
                 ['bold', 'italic', 'underline', 'strike']
             ],
             handlers: {
                'link': function(value){
                  alert('notice');
                }
             }
         }
    };
    let formats = [
      'video', 'image', 'link', 'blockquote','bold', 'italic', 'underline', 'list', 'strike', 'header'
    ]             
    return (
        <form className='form' onSubmit={this.submitForm}>
            <ReactQuill onChange={this.handleChange} 
                        value={this.state.text} 
                        theme={'snow'} 
                        formats={formats} 
                        modules={modules} />
        </form>
    )
}

It works good, I got a notice!) But after any action I also have an error in chrome console 'quill.js?a911:2835 The given range isn't in document.' and the quill redactor lost focus.
Any ideas about what have I done wrong?

Most helpful comment

@valentinkostadinov Hi) hope, it will help you. Here are some code from my app which have only text editor.

import React, { Component } from 'react'
import ReactQuill, { Quill } from 'react-quill'

function youtube_parser(url){
    var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
    var match = url.match(regExp);
    return (match&&match[7].length==11)? true : false;
}

class MyVideo1 extends Quill.imports['formats/video'] {

    static create(value) {
    if (youtube_parser(value)) {
        let node = super.create(value);
        node.setAttribute('frameborder', '0');
        node.setAttribute('allowfullscreen', true);
        node.setAttribute('src', this.sanitize(value));
        return node;
    } else {
        let node = document.createElement('span');
        FormNewTalk.prototype.formAlert('Волько youtube видСо возмоТно')
        return node;
    }
  }
}

Quill.register({
  'formats/video': MyVideo1,
})
let modules = {
     toolbar: {
         container: [
             ['blockquote', 'image'],
             ['link', 'video'],
             ['bold', 'italic', 'underline', 'strike']
         ],
         handlers: {
            link: function(value) {
              if (value) {
                let range = this.quill.getSelection();
                if (range == null || range.length == 0) {

                    FormNewTalk.prototype.formAlert('Π’Ρ‹ Π½Π΅ Π²Ρ‹Π΄Π΅Π»ΠΈΠ»ΠΈ тСкст')
                    return;
                }
                let preview = this.quill.getText(range);
                if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {
                  preview = 'mailto:' + preview;
                }
                let tooltip = this.quill.theme.tooltip;
                tooltip.edit('link', 'http://');
              } else {
                this.quill.format('link', false);
              }
            }
         }
     }
};

let formats = [
  'video', 'image', 'link', 'blockquote',
  'bold', 'italic', 'underline', 'list', 
  'strike', 'header'
]

export class FormNewTalk extends Component {
    render() {
        return (
            <div className="form__right">
                <ReactQuill value={this.state.text} 
                            theme={'snow'} 
                            formats={formats} 
                            modules={modules} />
            </div>
        )
    }
}

All 8 comments

I think if you fully reimplement the handler, including resetting the selection, you won't have this error. Right now Quill doesn't know what to do when you just alert without doing anything. If you need help implementing something on top of the Quill API I suggest asking on StackOverflow or upstream in the Quill project.

Thank you for a quick answer!
But the problem is not with the link handler, It works correctly and have no such errors.

This error appears on any symbol which I want to type.

UPD: I understand the problem. When the format is defined inside the render - the component is rerendering on any keyup action. So, the solution is to reimplement the formats and handles outside the render of the component! Thanks!

@glebPotapenko Thanks for the update. I'm having the exact same problem. Any chance you can share an example of how to reimplement the handlers "outside the render"? Thanks!

@valentinkostadinov Hi) hope, it will help you. Here are some code from my app which have only text editor.

import React, { Component } from 'react'
import ReactQuill, { Quill } from 'react-quill'

function youtube_parser(url){
    var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
    var match = url.match(regExp);
    return (match&&match[7].length==11)? true : false;
}

class MyVideo1 extends Quill.imports['formats/video'] {

    static create(value) {
    if (youtube_parser(value)) {
        let node = super.create(value);
        node.setAttribute('frameborder', '0');
        node.setAttribute('allowfullscreen', true);
        node.setAttribute('src', this.sanitize(value));
        return node;
    } else {
        let node = document.createElement('span');
        FormNewTalk.prototype.formAlert('Волько youtube видСо возмоТно')
        return node;
    }
  }
}

Quill.register({
  'formats/video': MyVideo1,
})
let modules = {
     toolbar: {
         container: [
             ['blockquote', 'image'],
             ['link', 'video'],
             ['bold', 'italic', 'underline', 'strike']
         ],
         handlers: {
            link: function(value) {
              if (value) {
                let range = this.quill.getSelection();
                if (range == null || range.length == 0) {

                    FormNewTalk.prototype.formAlert('Π’Ρ‹ Π½Π΅ Π²Ρ‹Π΄Π΅Π»ΠΈΠ»ΠΈ тСкст')
                    return;
                }
                let preview = this.quill.getText(range);
                if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {
                  preview = 'mailto:' + preview;
                }
                let tooltip = this.quill.theme.tooltip;
                tooltip.edit('link', 'http://');
              } else {
                this.quill.format('link', false);
              }
            }
         }
     }
};

let formats = [
  'video', 'image', 'link', 'blockquote',
  'bold', 'italic', 'underline', 'list', 
  'strike', 'header'
]

export class FormNewTalk extends Component {
    render() {
        return (
            <div className="form__right">
                <ReactQuill value={this.state.text} 
                            theme={'snow'} 
                            formats={formats} 
                            modules={modules} />
            </div>
        )
    }
}

@glebPotapenko Thanks a lot, this helps!

I had this issue too with React ES6 classes. It was caused by me using a function to generate and return my Quill modules object instead of just returning a plain object. I'd like to record this here just in case anyone else has this issue.

Code that didn't work

getQuillModulesWithImageHandler = () => {
  return Object.assign(
    {},
    {
      toolbar: {
        ...modules.toolbar,
        handlers: {
          image: () => {
            //  Clicking on the Quill image button launches the upload modal
            this.setState({ showUploadModal: true });
          }
        }
      }
    }
  );
};

render() {
  return (
    <WebsiteBlogArticleItem
      blogArticle={this.state.blogArticle}
      blogArticleViewMode={this.state.blogArticleViewMode}
      toggleBlogArticleViewMode={this.toggleBlogArticleViewMode}
      blogCategories={this.state.blogCategories}
      modules={this.getQuillModulesWithImageHandler()}
      setQuillRef={this.setQuillRef}
      handleChange={this.handleChange}
      handleSubmit={this.handleSubmit}
    />
  );
}

Code that did work

getQuillModulesWithImageHandler = {
  toolbar: {
    ...modules.toolbar,
    handlers: {
      image: () => {
        //  Clicking on the Quill image button launches the upload modal
        this.setState({ showUploadModal: true });
      }
    }
  }
};

render() {
  return (
    <WebsiteBlogArticleItem
      blogArticle={this.state.blogArticle}
      blogArticleViewMode={this.state.blogArticleViewMode}
      toggleBlogArticleViewMode={this.toggleBlogArticleViewMode}
      blogCategories={this.state.blogCategories}
      modules={this.getQuillModulesWithImageHandler}
      setQuillRef={this.setQuillRef}
      handleChange={this.handleChange}
      handleSubmit={this.handleSubmit}
    />
  );
}

In my case it wasn't happening because of the modules being generated on the fly.

In my case I had onChangeSelection defined:

image

When I remove it, The given range isn't in document error isn't raised on some particular editor updates.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Nazanin1369 picture Nazanin1369  Β·  4Comments

shaneshearer-andculture picture shaneshearer-andculture  Β·  4Comments

camliu89 picture camliu89  Β·  4Comments

mtando picture mtando  Β·  5Comments

levous picture levous  Β·  3Comments