quill icon indicating copy to clipboard operation
quill copied to clipboard

Consider allowing multiple editors on the same page & to use the same toolbar.

Open u12206050 opened this issue 8 years ago • 57 comments

When having more than one editor on a page, each one currently seems to require their own toolbar. If I try to assign a previously assigned toolbar then the events don't work for the second editor.

Steps for Reproduction

  1. Create one toolbar
  2. Create two editors
  3. Assign both editors the same toolbar.

Expected behavior: Would be easy enough to simply keep track of which editor was last active and apply and changes to that editor eg. Changing font size etc.

Actual behavior: The tool bar seems broken as soon as the second editor is attached to it. Only the second editor is actually usable, because when trying to type in the first one, the cursor jumps to the second one.

u12206050 avatar Apr 20 '16 12:04 u12206050

What is the use case for this? And why is that use case not solved with just hiding the respective toolbar and require there be one and only one toolbar?

jhchen avatar Nov 03 '16 20:11 jhchen

This is something that I am looking for also.

I am trying to incorporate Quill into a webpage builder. I'm hoping to have Quill handle the content and have another way to control the background images of the sections. Ideally the toolbar would be attached to the top and not connected to the editable areas. Maybe there's another way to do this?

quill-edit

m1kelynn avatar Dec 28 '16 15:12 m1kelynn

@m1kelynn Did you managed to solve the issue with multiple editors with single toolbar in your case? Eager to know if something you could share.

Thileepan avatar May 18 '17 03:05 Thileepan

Hello, I got a similar use case and managed it by implementing my own toolbar.
When the editor content / cursor changes, it will get the current formattings.

this.quill.on("editor-change", (eventName, ...args) =>
{
	if(this.quill.getSelection())
	{
		this.currentFormats = this.quill.getFormat();
	}
});

The toolbar buttons will call a format function.

format(format, params)
{
	if(this.currentFormats[format] && (!params || this.currentFormats[format] == params))
	{
		this.quill.format(format, false, Quill.sources.USER);
	}
	else
	{
		this.quill.format(format, params, Quill.sources.USER)
	}
}

Then I bind the style of the toolbar button to the currentFormats variable.

dkirchhof avatar May 21 '17 17:05 dkirchhof

@mrgrimmig, I have a hard time following what you did to solve this problem. I need to do something similar, and would really appreciate some clarification or a workinf code example, if possible.

Thanks!

-JM

TheWizz avatar Jun 11 '17 21:06 TheWizz

Sorry for my weird response. Actually i'm using polymer and bind current formats to the toolbar to update the buttons autmatically, but i've made a small example without any dependencies to clarify my answer.

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.quilljs.com/1.2.6/quill.js"></script>
    <link href="https://cdn.quilljs.com/1.2.6/quill.snow.css" rel="stylesheet">

    <style>

        .active {
            background: green;
        }

    </style>
</head>
<body>
    
    <button id="bold" onclick="onBoldClick()">Bold</button>

    <div id="editor1">
        <p>Hello World!</p>
        <p>Some initial <strong>bold</strong> text</p>
        <p><br></p>
    </div>

    <div id="editor2">
        <p>Hello World!</p>
        <p>Some initial <strong>bold</strong> text</p>
        <p><br></p>
    </div>
    
    <script>

        var currentEditor; // selected / focused editor
        var currentFormats; // save the current formattings

        createEditor("#editor1");
        createEditor("#editor2");

        function createEditor(selector)
        {
            let quill = new Quill(selector, { });
            
            quill.on("editor-change", (eventName, ...args) =>
            {
                currentEditor = quill;
                updateButtons();
            });
        }

        // get current formattings to style the toolbar buttons
        function updateButtons()
        {
            if(currentEditor.getSelection())
            {
                currentFormats = currentEditor.getFormat();

                if(currentFormats.bold)
                {
                    bold.classList.add("active");
                }
                else
                {
                    bold.classList.remove("active");
                }
            }
        }

        // if selected text is bold => unbold it - if it isn't => bold it
        function onBoldClick()
        {
            if(!currentFormats || !currentEditor)
            {
                return;
            }

            if(currentFormats.bold)
            {
                currentEditor.format("bold", false);
            }
            else
            {
                currentEditor.format("bold", true);                
            }
        }
        
    </script>

</body>
</html>

dkirchhof avatar Jun 12 '17 07:06 dkirchhof

I'd love to talk about my use-case here as well...

I'm using Quill as the text-editing component of a word-processor, which may have multiple documents onscreen at the same time. But all the instances should share the same toolbar. For example, here's a screenshot containing three Quill instances, one for the main document, and two containing margin comments:

Shaxpir Screenshot

Whenever the user clicks from one editor to another, the toolbar should update itself to reflect the active/enabled/disabled status of each button. And any toolbar clicks should be directed to the currently-focused editor instance.

Although some of the toolbar buttons correspond with Quill formatting functions, there are other toolbar buttons for non-Quill functionality, and those should continue to be enabled even when there are no active Quill editors onscreen at all.

Right now, I'm implementing my own toolbar, completely separate from Quill, and managing the active/enabled/disabled state of all the toolbar buttons myself. But it's pretty ugly, and I'd love to help Quill address this use case a little better.

Thank you for an incredible open-source project! I appreciate all your hard work.

benjismith avatar Jul 20 '17 18:07 benjismith

Sounds like there is sufficient demand for this feature. Can someone propose an API that would fit solve their needs? Short code snippets using the API and high level descriptions of what they do and don't do would be helpful.

jhchen avatar Aug 28 '17 00:08 jhchen

Perhaps some inspiration could be taken from the TextAngular editor, which has this feature.

https://github.com/textAngular/textAngular

See specifically "a toolbar can be linked to multiple editors" in the accompanying wiki:

https://github.com/textAngular/textAngular/wiki/Customising-The-Toolbar

-JM

TheWizz avatar Aug 28 '17 06:08 TheWizz

I don't think we need to change the API very much. The only things I really need are:

  1. The ability to attach and detach Quill from additional DOM containers.
  2. The ability to retrieve an editor instance by DOM selector.
  3. New events for editor attachment and detachment, as well as focus and unfocus between editor instances.

Here's a quick example of creating the root Quill object, registering event handlers, attaching a few editors, selecting them for focus, and detaching from them again.

    let quill = new Quill({ toolbar : "#toolbar" });

    quill.on('editor-attach', function(e) { console.log("attached to:" + e.getEditorId());
    quill.on('editor-detach', function(e) { console.log("detached from:" + e.getEditorId());
    quill.on('editor-focus', function(e) { console.log("focused:" + e.getEditorId());
    quill.on('editor-unfocus', function(e) { console.log("unfocused:" + e.getEditorId());

    quill.attach('#editor-a');
    quill.attach('#editor-b');

    var editorA = quill.getEditorInstance('#editor-a");
    editorA.focus();

    var editorB = quill.getEditorInstance('#editor-b");
    editorB.focus();

    quill.detach('#editor-b');
    quill.detach('#editor-a');

The current Quill constructor, which accepts an editor ID...

    let quill = new Quill("#editor", { toolbar : "#toolbar" });

...could continue to be supported, as a shorthand for the more verbose version:

    let quill = new Quill({ toolbar : "#toolbar" });
    quill.attach("#editor");

It might also be nice to declare which toolbar buttons are enabled and disabled for each editor instance, possibly with something like this:

    let quill = new Quill({ toolbar : "#toolbar" });
    quill.attach("#editor", { enabled : [ "#button-bold", "#button-italic" ] });

However, I'd also be happy to add the enable/disable logic to my own event-handler code for the focus and unfocus events.

benjismith avatar Sep 04 '17 22:09 benjismith

To be clear you are proposing that let quill = new Quill({ toolbar : "#toolbar" }); would not create a Quill editor and instead create an editor-less toolbar?

Quill.find is already an API method (though currently experimental) by the way.

jhchen avatar Sep 06 '17 05:09 jhchen

Yep, that's what I'm suggesting!

Maybe most users will typically include an editor selector in the constructor let quill = new Quill("#editor", { toolbar : "#toolbar" }); but I think it would be very handy to startup Quill without an editor.

In my application, the toolbar is always present from startup onward, even before the user creates their first document. As soon as they create a new document, or navigate into their existing content, I create new Quill editors and manually update the toolbar based on the selection and formatting state within that editor.

In my ideal scenario, calling let quill = new Quill({ toolbar : "#toolbar" }); essentially just creates an editor factory, configured with a particular Parchment schema.

benjismith avatar Sep 06 '17 13:09 benjismith

Hmm the thing is Quill is the editor with optional modules, one of which is the toolbar. So new Quill not making an editor would be a dramatic departure.

jhchen avatar Sep 11 '17 23:09 jhchen

I totally understand, and I'm 100% open to other designs. The one I suggested just seems the most natural to me, as a user. But I'd love to see other proposals too!

benjismith avatar Sep 12 '17 00:09 benjismith

Would it be possible to make multiple editors work if created with the same toolbar?

`var quill1 = new Quill('#editor1', { modules: { toolbar: '#toolbar' }, theme: 'snow' });

var quill2 = new Quill('#editor2', { modules: { toolbar: '#toolbar' }, theme: 'snow' });`

fmpenz avatar Oct 15 '17 09:10 fmpenz

If you click a button in the toolbar what editor would it modify? Both?

jhchen avatar Oct 16 '17 05:10 jhchen

If you click a button in the toolbar only the editor which lastly had focus should be modified. The Shared Toolbar feature in CKE shows how it could be done.

fmpenz avatar Oct 16 '17 10:10 fmpenz

Has there been any progress on this feature? I would really like to switch to Quill from what I'm using today (TextAngular). Anything I can do to help out moving this forward?

-JM

TheWizz avatar Nov 15 '17 10:11 TheWizz

We would love to see this feature as well. For now I have made an Angular wrapper library (ngx-quill-wrapper) that allows this, but the way it handles it is bit hacky and definitely not optimal.

sconix avatar Nov 15 '17 10:11 sconix

I'm usinbn AngularJS, so unfortunately I can't use your wrapper. But I think the idea of separating the toolbar from the editor (at least as an option) is well thought out. Hopefully, we'll see this as part of Quill itself in a not-too-distant future, since this is really something that ought to be done independently of any specific framework wrapper.

TheWizz avatar Nov 15 '17 13:11 TheWizz

I don't believe anyone is working on it and I probably will not get to it as there are other higher priority items at the moment. If someone with this use case wants to take it on please do. I am happy to give feedback on specific proposals but do not have the bandwidth to give general guidance for those new to Quill.

jhchen avatar Nov 20 '17 06:11 jhchen

Hello guys, hope this message everyone fine.

Good topic/issue to cover here. I've implement and managed to add some buttons in toolbar by manual way which are functional as it should be, but I'm still facing some issues like Color, Font Size & Font Family etc.

Demo link: Codepen

Now what i need is to make Color, Font Size & Font Family button functional. Please review and suggest me. Thanks

umairrazzaq avatar Mar 15 '18 13:03 umairrazzaq

Would love to see this feature as well. At the moment I have to create a toolbar for every Quill editor as trying to use a single toolbar leaves the previous editor toolbar event handlers attached. After hours of trying to "hack it out", I finally created an editor + toolbar for every area I wanted to allow editing.

I was also wondering if I can use the addContainer API method, but the docs are not specific enough for me to quickly understand what it does.

Overall, Quill is amazing btw :)

PawelGlow avatar Aug 10 '18 06:08 PawelGlow

I see this topic has not moved, too bad. Any example on how to use the addContainer API method?

PawelGlow avatar Nov 19 '18 23:11 PawelGlow

Yes you're right, but I've managed to do so with TextAngular, since i need it for angularjs project.

umairrazzaq avatar Nov 20 '18 09:11 umairrazzaq

I have been working with Quill for the past 6 months while using it in a product I am building. I have found that the documentation is ok, but lacks better examples.

With regards to this topic, I have many boxes and only one toolbar. My goal was to have a Quill editor in the active box and allow the toolbar to control the content of the active box. Here are my findings:

1. Initialize a Quill editor inside each box with the same toolbar selector class for each

var editors = [];
for (var x = 0; x < len; x ++) {
   editors.push(new Quill(document.getElementById('box-' + x), {
       modules: {
           toolbar: {
               container: '.toolbar',
           },
       },
   }));
}

This failed because the toolbar now had an event attached for each Quill object, which resulted in having many events per action. This became difficult to manage and caused many technical issues when switching between boxes to edit the content.

2. Same as point 1, except now create a toolbar for each box and display only the active box toolbar. For each Quill editor use the unique toolbar.

var editors = [];
for (var x = 0; x < len; x ++) {
   editors.push(new Quill(document.getElementById('box-' + x), {
       modules: {
           toolbar: {
               container: '.toolbar-' + x,
           },
       },
   }));
}

This worked as expected but it resulted in a very DOM heavy structure, especially if you have lots of actions in your toolbar or lots of Quill instances.

You have 2 options:

  • generate all the toolbars server side and send them to the frontend, which in my case, resulted in heavy page load.
  • render the toolbars dynamically on the frontend and inject them in to the DOM. Lots of memory overhead when working with 20+ Quill instances.

During my testing I used Angular and Vue to test the performance of rendering the toolbars dynamically. I noted a rendering performance hit when I was working with 20+ Quill instances.

3. Initialize a Quill editor inside the active box only and destroy the previous editor + one toolbar

This is the solution I am using today and it results in the best performance and a some what clean solution. When the active box changes, I get the Quill instance content, remove the previous Quill events + instance, and I update the DOM as the HTML created by the editor is not removed automatically.

I am using this quill delta to html addon

if (window.editor) {
    //Get the content
    var content = '';
    if (window.editor.getLength() > 1) {
        var delta = window.editor.getContents(); //store globally
        var converter = new QuillDeltaToHtmlConverter(delta.ops, {});
        var html = converter.convert();
        if (typeof html !== "undefined" && html !== null) {
            content = html;
        }
    }

    //remove any [on events](https://quilljs.com/docs/api/#events) you attached to the Quill instance 

    //remove Quill instance
    window.editor = undefined;

    // clean DOM and set content
    document.getElementById('previousBoxId').classList.remove('ql-container');
    document.getElementById('previousBoxId').innerHTML = content;
}

//create new quill instance
window.editor = new Quill(...)

//set the editor contents ("//store globally" comment above)
if (editorContent) {
    window.editor.setContents(editorContent)
}

window.editor.focus();
//initialize any [on events](https://quilljs.com/docs/api/#events) if you want

The downside of Quill not managing multiple instances of the editor with a single toolbar is not a big problem, but it does require you to do the research/testing + add the logic yourself, which can be a hassle.

I hope someone finds this useful.

PawelGlow avatar Nov 21 '18 11:11 PawelGlow

I find Quill to be a very helpful control and have been working with Quill for a couple of months now and also have the need of multiple editors on a single view. I ask if this could be something like reassigning of Quill.options.modules.toolbar. If I have editor1 and editor2, I wonder if code as the following pseudo code not work:

if editor1 is selected then editor2.unassignToolbar editor1.options.modules.toolbar = theToolbar else if editor2 is selected then editor1.unassignToolbar editor2.options.modules.toolbar = theToolbar

I realize that this is a trivial example, yet I hope that it shows the intent and possible desire.

I have tried this utilizing an example, yet for some reason it did not work with the current version of Quill. So for now I am simply using an independent toolbar and using the formattext and other functions Quill provides, which unfortunately is, for some reason, causing other issues with deletion of text and typing, which I shall not bring up at this time.

GJMAC avatar Apr 03 '19 14:04 GJMAC

@GJMAC I have not tried to reassign the toolbar in "Quill.options.modules.toolbar". I doubt this would work because there are probably some event handlers being added and removed when you create the Quill editor.

Although in the Quill docs they mention event handlers are automatically garbage collected, this did not work for me, that is why I decided to destroy and recreate the Quill editor every time I wanted to change the toolbar.

PawelGlow avatar Apr 03 '19 15:04 PawelGlow

@PawelGlow Garbage collectors seems to not work for me too. How do you destroy the Quill editor? I am using Vuejs but I think it's the same way for different frameworks/vanilla.

Onouriis avatar Jun 29 '19 06:06 Onouriis

@Onouriis You need to keep track of the Quill object and then when you want to destroy it you set the reference to undefined. Check out my previous post, #3 with the line:

//remove Quill instance window.editor = undefined;

https://github.com/quilljs/quill/issues/633#issuecomment-440623398

If you need to keep track of multiple Quill instances at once, create a JavaScript object and store them under some key.

window.editors = {
   editor1: Quill_instance,
   editor2: Quill_instance,
   ...
}

PawelGlow avatar Jun 29 '19 06:06 PawelGlow