Quill: Button tooltips in the demo

Created on 4 May 2016  路  11Comments  路  Source: quilljs/quill

It'll be nice to have tooltips for the buttons in the new demos.

http://quilljs.com/
http://beta.quilljs.com/docs/formats/
http://beta.quilljs.com/docs/themes/snow/

Version: [version]
1.0-Beta.0

feature

Most helpful comment

It would be nice to have it included. Even basic accessibility is better than nothing.
All it needs is an optional attribute on toolbar buttons, the rest could be handled by user style sheets.
Something like:

let toolbarOptions = [
    [
      { 'bold': { tooltip: "Bold (Ctrl+B)" } }, 'italic', 'underline', 'strike'
    ]
  ];

Generated via options:

<button type="button" class="ql-bold" data-tooltip="Bold (Ctrl+B)"></button>

User style sheet:

[data-tooltip] {
  content: attr(data-tooltip);
  display: none;
  // however people wanna style it..
}
[data-tooltip]:hover {
  display: block; // or something..
}

There's already an issue #1084 that touches on this, though that proposition is more generic.

All 11 comments

Even having title tags to get a label on hover would help quite a bit

Actually, it would be pretty nice to have this at v1.0. Some of our users could use a hint as to what the different toolbar items actually do - see GH toolbar:

screen shot 2016-08-22 at 12 19 32 pm

Is it actually possible to get tooltips??

We've had the same request, it would be great to be able to attach tooltips to each of the tools in the toolbar.

There is now an example using bootstrap. I'm hesitant to add this Quill core since users are likely to be opinionated with UI and want to customize, which would add a lot of complexity and weight into Quill, and so is better handled by other libraries.

@jhchen You were right. This example tripped me up a bit when learning about quill. I just realized that the data-toggle is a bootstrap thing and you're passing in the html (with the tooltip data-toggle) rather than making quill generate it in the html.

To deal with not having to write custom html for the toolbar buttons, I did this:

    let showTooltip = (el) => {
      let tool = el.className.replace('ql-', '');
      if (tooltips[tool]) {
        console.log(tooltips[tool]);

        // TODO: add your tooltip code here.
      }
    };

    let tooltips = {
      'bold': 'Bold',
      'italic': 'Italic'
    };

    let toolbarElement = document.querySelector('.ql-toolbar');
    if (toolbarElement) {
      let matches = toolbarElement.querySelectorAll('button');
      for (let el of matches) {
        showTooltip(el);
      }
    }

It would be nice to have it included. Even basic accessibility is better than nothing.
All it needs is an optional attribute on toolbar buttons, the rest could be handled by user style sheets.
Something like:

let toolbarOptions = [
    [
      { 'bold': { tooltip: "Bold (Ctrl+B)" } }, 'italic', 'underline', 'strike'
    ]
  ];

Generated via options:

<button type="button" class="ql-bold" data-tooltip="Bold (Ctrl+B)"></button>

User style sheet:

[data-tooltip] {
  content: attr(data-tooltip);
  display: none;
  // however people wanna style it..
}
[data-tooltip]:hover {
  display: block; // or something..
}

There's already an issue #1084 that touches on this, though that proposition is more generic.

Any way to make Angular's transclusion and Angular Material (present in my project/component) work instead of Bootstrap?

<quill-editor [modules]="myQuillConfig" name="my-email-body" [(ngModel)]="myEmailBody">
  <ng-content select=".ql-clean" matTooltip="Remove Formatting from Selection" matTooltipPosition="above"></ng-content>
</quill-editor>

Try adding like this

  let showTooltip = (el) => {
      let tool = el.className.replace('ql-', '');
    if (tooltips[tool]) {
      console.log(tooltips[tool]);
      $(el).attr("title",tooltips[tool]);
    }
  };
 let tooltips = {
    'bold': 'Bold (ctrl+B)',
    'italic': 'Italic (ctrl+I)',
    'underline': 'Underline (ctrl+U)',
    'strike': 'Strike'
  };

  let toolbarElement = document.querySelector('.ql-toolbar');
  if (toolbarElement) {
    let matches = toolbarElement.querySelectorAll('button');
    for (let el of matches) {
      showTooltip(el);
    }
  }

Modified the previous suggestion to allow tooltips also for button with values and also pickers..

let toolbarTooltips = {
      'font': 'Select a font',
      'size': 'Select a font size',
      'header': 'Select the text style',
      'bold': 'Bold',
      'italic': 'Italic',
      'underline': 'Underline',
      'strike': 'Strikethrough',
      'color' : 'Select a text color',
      'background': 'Select a background color',
      'script': {
        'sub' : 'Subscript',
        'super': 'Superscript'
      },
      'list': {
        'ordered':'Numbered list',
        'bullet': 'Bulleted list'
      },
      'indent': {
        '-1': 'Decrease indent',
        '+1':  'Increase indent'
      },
      'direction': {
        'rtl': 'Text direction (right to left | left to right)',
        'ltr': 'Text direction (left ro right | right to left)'
      },
      'align': 'Text alignment',
      'link': 'Insert a link',
      'image': 'Insert an image',
      'formula': 'Insert a formula',
      'clean': 'Clear format',
      'add-table': 'Add a new table',
      'table-row': 'Add a row to the selected table',
      'table-column': 'Add a column to the selected table',
      'remove-table': 'Remove selected table',
      'help': 'Show help'
    };


    function showTooltips(){
      let showTooltip = (which,el) => {
        if (which=='button'){
          var tool = el.className.replace('ql-', '');
        }
        else if (which=='span'){
          var tool = el.className.replace('ql-','');
          tool=tool.substr(0,tool.indexOf(' '));
        }
        if (tool){
          //if element has value attribute.. handling is different
          //buttons without value
          if (el.value ==''){
            if (toolbarTooltips[tool])
              el.setAttribute('title',toolbarTooltips[tool]);
          }
          //buttons with value
          else if (typeof el.value !=='undefined'){
            if (toolbarTooltips[tool][el.value])
              el.setAttribute('title',toolbarTooltips[tool][el.value]);
          }
          //default
          else
            el.setAttribute('title',toolbarTooltips[tool]);
        }
      };

      let toolbarElement = document.querySelector('.ql-toolbar');
      if (toolbarElement) {
        let matchesButtons = toolbarElement.querySelectorAll('button');
        for (let el of matchesButtons) {
          showTooltip('button',el);
        }
        //for submenus inside 
        let matchesSpans = toolbarElement.querySelectorAll('.ql-toolbar > span > span');
        for (let el of matchesSpans) {
          showTooltip('span',el);
        }
      }
    }
var toolbar = quill.container.previousSibling;
toolbar.querySelector('button.ql-bold').setAttribute('title', 'Bold');
toolbar.querySelector('button.ql-italic').setAttribute('title', 'Italic');
toolbar.querySelector('button.ql-list').setAttribute('title', 'List');
toolbar.querySelector('button.ql-link').setAttribute('title', 'Link');
toolbar.querySelector('button.ql-script').setAttribute('title', 'Superscript');
toolbar.querySelector('button.ql-clean').setAttribute('title', 'Clear Formatting');

Modified previous to work with angular 9 and multi language

import { QUILL_TOOL_TIPS_SR } from '../../../../../locale/quill-tooltips_sr';
import { QUILL_TOOL_TIPS_EN } from '../../../../../locale/quill-tooltip_en';

export class myQuillComponent {

  private toolbarTooltips: any;

  constructor(public elementRef: ElementRef,
  @Inject(LOCALE_ID) protected localeId: string) { }

  public ngAfterViewChecked(): void {

   if (this.localeId === 'sr-Latn') {
        this.toolbarTooltips = QUILL_TOOL_TIPS_SR;
   } else if (this.localeId === 'en-US') {
       this.toolbarTooltips = QUILL_TOOL_TIPS_EN;
   } else {
     // default to EN
     this.toolbarTooltips = QUILL_TOOL_TIPS_EN;
   }

   if (this.toolbarTooltips) {
     const toolbarElement = this.elementRef.nativeElement.querySelector('.ql-toolbar');
     // if i want to set manuallz
     // toolbar.querySelector('button.ql-bold').setAttribute('title', 'Bold');
     // toolbar.querySelector('button.ql-italic').setAttribute('title', 'Italic');
     // toolbar.querySelector('button.ql-list').setAttribute('title', 'List');
     // toolbar.querySelector('button.ql-link').setAttribute('title', 'Link');

   if (toolbarElement) {
      let matchesButtons = toolbarElement.querySelectorAll('button');
   for (let el of matchesButtons) {
      this.showTooltips('button', el);
   }
   //for submenus inside 
   let matchesSpans = toolbarElement.querySelectorAll('.ql-toolbar > span > span');
   for (let el of matchesSpans) {
      this.showTooltips('span', el);
   }
  }
   } else {
  console.warn('No tooltips');
  }
 }
}


quill-tooltips_en.ts file
export const QUILL_TOOL_TIPS_EN: any  = {
'font': 'Select a font',
'size': 'Select a font size',
'header': 'Select the text style',
'bold': 'Bold',
'italic': 'Italic',
'underline': 'Underline',
'strike': 'Strikethrough',
'color': 'Select a text color',
'background': 'Select a background color',
'script': {
  'sub': 'Subscript',
  'super': 'Superscript'
},
'list': {
  'ordered': 'Numbered list',
  'bullet': 'Bulleted list'
},
'indent': {
  '-1': 'Decrease indent',
  '+1': 'Increase indent'
},
'direction': {
  'rtl': 'Text direction (right to left | left to right)',
  'ltr': 'Text direction (left ro right | right to left)'
},
'align': 'Text alignment',
'link': 'Insert a link',
'image': 'Insert an image',
'video': 'Insert a video',
'formula': 'Insert a formula',
'clean': 'Clear format',
'add-table': 'Add a new table',
'table-row': 'Add a row to the selected table',
'table-column': 'Add a column to the selected table',
'remove-table': 'Remove selected table',
'help': 'Show help'
};
Was this page helpful?
0 / 5 - 0 ratings

Related issues

benbro picture benbro  路  3Comments

CHR15- picture CHR15-  路  3Comments

ouhman picture ouhman  路  3Comments

lastmjs picture lastmjs  路  3Comments

Yves-K picture Yves-K  路  3Comments