Create a Custom Toolbar Button

This example shows you how to add a custom button to the toolbar.

Contribute to this page

Basic button

First question to answer is probably - how do you create a button in TinyMCE? And the answer is - of course there’s a special method for that - addButton(). It should be called with button identifier and configuration object. Here’s an example:

editor.addButton('mybutton', {
  text: "My Button",
  onclick: function () {
     alert("My Button clicked!");
  }
});

In fact identifier is the only required argument, but if you want to be able to find your button on the toolbar you better provide either an icon option or a text option (or both). Identifier will let us reference this button later in the code. Another important option is onclick - we want our button to do something when we click it, right?

But let's do something more useful, than simply alerting a message. Let's insert something into the editor - a current date, for example.

Now, you might know that TinyMCE already ships with nice Insert date/time plugin (it is hard to find a text editing feature that is not implemented in TinyMCE), but it does whole lot of complementary things - adds some commands and shortcuts, inserts "Insert date/time" menu, adds support for fancy date formats, i18n and some more. We won’t go in all of that in this tutorial and make our button much simpler. However insertdatetime plugin is a good read if mentioned stuff is something that you would like to familiarise yourself with. Its source code can be found on github.

For our purpose we will use <time> tag. <time> tag should have an attribute datetime - valid date with an optional time string. And as MDN states:

This attribute indicates the time and date of the element. If the value cannot be parsed as a date with an optional time string, the element does not have an associated time stamp.

Here's a simple function, that will convert a Date object into such tag with populated timestamp in datetime attribute and readable date string inside.

function toTimeHtml(date) {
    return '<time datetime="' + date.toString() + '">' + date.toDateString() + '</time>';
}

So it will produce something like this:

<time datetime="Mon Sep 26 2016 08:42:22 GMT+0400 (GET)">Mon Sep 26 2016</time>

All we need now is to call this function when our custom button is clicked and insert whatever we get from it into the editor.

function toTimeHtml(date) {
    return '<time datetime="' + date.toString() + '">' + date.toDateString() + '</time>';
}

editor.addButton('currentdate', {
  icon: 'insertdatetime',
  //image: 'http://p.yusukekamiyamane.com/icons/search/fugue/icons/calendar-blue.png',
  tooltip: "Insert Current Date",
  onclick: function () {
     var html = toTimeHtml(new Date());
     editor.insertContent(html);
  }
});

We've borrowed an icon from the above mentioned Insert date/time plugin, but you can use any icon class that you have currently defined in your stylesheets. If you do not have any icon classes, you can supply direct image URL via image option.

image option has priority over icon, so if you uncomment corresponding line above, icon will get superseded, with the calendar image from the beautiful Fugue iconset.

We've also replaced text with tooltip option, which fits the toolbar concept much better.

Done. That's it. Seriously.

Here is the full code (you can experiment on it by clicking Edit on Codepen in the top right corner)..

See the Pen XjRWZj by TinyMCE (@tinymce) on CodePen.

As you see we've supplied identifier of our button in the toolbar property, along with undo and redo (code plugin is also included - so that you could see HTML code that gets generated). So whenever user clicks Insert Current Date, the date will be inserted into the editor at the current cursor position. Notice how undo button gets enabled after insertion, that's because we handle modification history for you.

Another point is setup callback - see how we put all of our code into it? It is the callback that TinyMCE will automatically invoke for every initialised editor instance. It will receive reference to the instance as the first argument. We can use setup callback to customise editor to our taste - here we are adding a button, but we could also add custom keyboard shortcuts, menus and everything that has to be added before editor is ready.

Button options

Button configuration properties:

Conditionally disable button

You probably do not want your button to be enabled at all times, since sometimes it might fell out of context. For example it would be awkward if we could insert <time> tag into another <time> tag, right? And you can see in the example above, that it is possible.

So basically we need a way to monitor the cursor position and disable our button when it's inappropriate. For this purpose we can hook onto a NodeChange event, that gets fired when cursor jumps from one node to another.

// ...

function monitorNodeChange() {
  var btn = this;
  editor.on('NodeChange', function(e) {
    btn.disabled(e.element.nodeName.toLowerCase() == 'time');
  });
}

editor.addButton('currentdate', {
  icon: 'insertdatetime',
  tooltip: "Insert Current Date",
  onclick: insertDate,
  onpostrender: monitorNodeChange
});

We made use of postrender option here and attached the callback that will be called after the button gets rendered. That's were we start our monitoring. See how we toggle buttons state, depending on whether the node under the cursor is time tag or not.

Here's updated demo. Try to click inside and outside the date string:

See the Pen qaoXLB by TinyMCE (@tinymce) on CodePen.

In reality it would have been more practical to simply set contenteditable attribute to false on the time tag. But I wanted to demonstarte how you can toggle the button state, depending on various logical conditions.

By the way, notice how the code for our example gets bigger and bigger. It has almost reached the boundaries of simplicity already. This is the moment when you should ask yourself whether it's better to bundle this feature as a plugin instead.

Toggle button

Sometimes we need a button to act as the on/off switcher, like in the case of basic emphasizing formatting (e.g. bold, italic). We've seen how we can conditionally disable button, now lets see how we can conditionally make button either active (depressed) or inactive (unpressed).

Lets add a basic button first, that will strike out a currently selected text.

editor.addButton('strikeout', {
  icon: 'strikethrough',
  onclick: function() {
    editor.execCommand('mceToggleFormat', false, 'strikethrough');
  }
});

mceToggleFormat is internal command, which when executed, toggles the specified format on and off. Obviously the format should also be registered and in this case strikethrough happens to be such, internally registered format. But notice that we named our button strikeout. We did it to differentiate from internal strikethrough button and make sure that we are indeed creating the functionality ourselves.

If you try the code at this stage, you will see that it actually works perfectly - striking out the plain text, and removing the striked formatting if it was already striked out. However the button visually doesn't reflect the operation that will be applied at that particular moment and place. Lets address this:

editor.addButton('strikeout', {
  icon: 'strikethrough',
  onclick: function() {
    editor.execCommand('mceToggleFormat', false, 'strikethrough');
  },

  onpostrender: function() {
    var btn = this;
    editor.on('init', function() {
      editor.formatter.formatChanged('strikethrough', function(state) {
        btn.active(state);
      });
    });
  }
});

Again we are using onpostrender to invoke our code after the button is rendered. But at that moment editor.formatter might not be initialised yet, so we hook onto init event first. Then there's that internal TinyMCE method editor.formatter.formatChanged() that will register a callback to be called when current selection is of the specified format. Callback will take in a state as the argument, and we will use it to visually depress or unpress our button.

Here's a full example:

See the Pen wzmAjY by TinyMCE (@tinymce) on CodePen.

Can't find what you're looking for? Let us know.

Except as otherwise noted, the content of this page is licensed under the Creative Commons BY-NC-SA 3.0 License, and code samples are licensed under the Apache 2.0 License.