Complete Guide to Manage Style Format Drop Down in WP Editor

how-to-wp-editor-format-drop-down

There’s tons of tutorial about how to add feature in WP Editor (tinymce) via “formats” drop downs, but none really explain how it works. Most just a recycle of other tutorials with different element and CSS and (sometimes) poorly coded.

This tutorial maybe incomplete and incorrect, but I’ll try to explain as much as I can.

Why I write this tutorial:

A lot of developer use shortcode to style content, such as dropcap, boxes, columns, etc and it’s sucks. I did some “site-fix” project where user broke their site because it’s uses massive amount of shortcodes used in content. I hope with this guide, a lot developer can utilize tinyMCE more for better user experience.

How To Add Formats Drop Down in WordPress Editor

It’s actually disabled/not-added by default, the ID/name of this “button”/option is styleselect and we can add it easily by filtering mce_buttons hook.

Here’s an example code:

/* Arrange WP Editor Toolbar Buttons */
add_filter( 'mce_buttons', 'my_wpeditor_buttons', 10, 2 );

/**
 * Add Buttons To WP Editor Toolbar.
 */
function my_wpeditor_buttons( $buttons, $editor_id ){
    /* Add it as first item in the row */
    array_unshift( $buttons, 'styleselect' );
    return $buttons;
}

The code above will add “Formats” Drop down as the first button in the first row of the editor.

The filter mce_buttons have two parameter, the first one is a simple array of $buttons and the second one (optional) is $editor_id. The $editor_id parameter is useful if we want to add our button only in specific editor. For example if we want to add the button only in content editor (post editor) we can use this code:

/**
 * Add Buttons To WP Editor Toolbar.
 */
function my_wpeditor_buttons( $buttons, $editor_id ){
    /* if not "content" editor, bail */
    if ( 'content' != $editor_id ){
        return $buttons;
    }
    /* Add it as first item in the row */
    array_unshift( $buttons, 'styleselect' );
    return $buttons;
}

To rearrange the buttons, because it’s using simple array (no key), we need use several php functions to order an array such as array_unshift(), array_splice(), or array_push(), etc.

TinyMCE Button Row

Even though as default WordPress only display two rows of toolbar button in the editor, actually we can add up to 4 rows of toolbar (cmiiw). To add it in other row toolbar, we need to use different filter hook:

/* Add button in 1st row */
add_filter( 'mce_buttons', 'my_wpeditor_buttons', 10, 2 );

/* Add button in 2nd row */
add_filter( 'mce_buttons_2', 'my_wpeditor_buttons', 10, 2 );

/* Add button in 3rd row */
add_filter( 'mce_buttons_3', 'my_wpeditor_buttons', 10, 2 );

/* Add button in 4th row */
add_filter( 'mce_buttons_4', 'my_wpeditor_buttons', 10, 2 );

Note: If we add buttons to the 4th toolbar, but there’s no button on the 3rd toolbar (empty), the buttons will be assigned (shifted) to the 3rd row.

The Default Formats Drop Down

There’s a lot of options in the default formats drop down (WP version 4.x).

  • Headings
    • Heading 1
    • Heading 2
    • Heading 3
    • Heading 4
    • Heading 5
    • Heading 6
  • Inline
    • Bold
    • Italic
    • Underline
    • Strikethrough
    • Superscript
    • Subscript
    • Code
  • Blocks
    • Paragraph
    • Blockquote
    • Div
    • Pre
  • Alignment
    • Left
    • Center
    • Right
    • Justify

I’ll explain more about this section later.

How to Add Our Own Option in Formats Drop Down

It’s actually very simple. We don’t even need to get dirty with javascript. We just need a simple filter to the tiny_mce_before_init. With this filter we can manage tinyMCE configuration such as list of buttons in the toolbar, language used in tinyMCE, tinyMCE active skin, and various other tinyMCE config.

To add our own custom options, we need to add style_formats in the array of config in json formated data. In this example we add a custom options to add <span> element to add dropcap.

/* Filter  */
add_filter( 'tiny_mce_before_init', 'my_wpeditor_formats_options' );

/**
 * Override Default Formats Options in the Drop Down with our own options.
 */
function my_wpeditor_formats_options( $settings ){

    /* List all options as multi dimension array */
    $style_formats = array(
        array(
            'title'   => 'DropCap', /* Title of the option drop down */
            'inline'  => 'span', /* use "inline" or "block" as key and the element wrapper ( "div", "span", "etc" ) as value. */
            'classes' => 'dropcap' /* additional classes for the element created (separated by space) */
        ),
    );

    /* Add it in tinymce config as json data */
    $settings['style_formats'] = json_encode( $style_formats );
    return $settings;
}

If we add the code above we will have this in our Formats options:

formats-dropcap

It’s pretty simple right?

if we select a letter or several words with it and click the “DropCap” button, our selection will be wrapped in span element with “dropcap” class:

<span class="dropcap">L</span>orem ipsum dolor sit amet, consectetur adipiscing elit.

The $style_formats array

In $style_formats array above we can use several configuration, such as:

“title”
The title of the drop down option.

“inline” or “block”
We can use “inline” or “block” as key and the the element wrapper as value. “inline” will only wrap element we select, while “block” will select the whole block of active content (caret), such as “paragraph” element. It’s best to use “inline” for inline elements such as “span”, “em” etc, and “block” as block elements such as “div”.

“classes”
Class attributes of the elements.

“styles”
You can add the style attributes as array for each css declaration.

“items”
You can add sub-menu by adding your buttons/options configuration here.

“icon”
The icon class for the options. you can only use the icon you available in tinymce or you can style the icon your self if you use custom icon.

“format”
Predefined formats (configuration for the button). all defaults buttons have this. I think you can add your own using tinyMCE buttons api (javascript).

I’m sure there’s also other configurations you can use that I don’t know.

To make it easier to understand, here’s an example:

/* Filter  */
add_filter( 'tiny_mce_before_init', 'my_wpeditor_formats_options' );

/**
 * Override Formats Options in the Drop Down
 */
function my_wpeditor_formats_options( $settings ){

    /* List all options as multi dimension array */
    $style_formats = array(
        array(
            'title'   => 'Red Span Test',
            'inline'  => 'span',
            'classes' => 'red-span',
            'styles'  => array(
                'color'             => '#ff0000', 
                'font-weight'       => '800', 
                'text-transform'    => 'uppercase', 
            ),
        ),
        array(
            'title'   => 'Boxes',
            'items' => array(
                array(
                    'title'   => 'Quote Box',
                    'block'   => 'div',
                    'classes' => 'quotebox',
                    'icon'    => 'blockquote',
                ),
                array(
                    'title'   => 'Question Box',
                    'block'   => 'div',
                    'classes' => 'questionbox',
                    'icon'    => 'wp_help',
                ),
            ),
        ),
    );

    /* Add it in tinymce confTitleig as json data */
    $settings['style_formats'] = json_encode( $style_formats );
    return $settings;
}

and the drop down options we’ll get:

test-drop-down

and the example (text) result when we use these buttons:

<span class="red-span" style="color: #ff0000; font-weight: 800; text-transform: uppercase;">Lorem ipsum</span> dolor sit amet, consectetur adipiscing elit. Maecenas a tortor quam. Vestibulum aliquet, diam eget dignissim vehicula, sapien sapien tempor velit, a ultrices tellus turpis nec nunc.
<div class="quotebox">Duis porta dapibus ligula vel semper. Quisque gravida, nunc nec consectetur tempus, massa augue facilisis quam, a ultrices felis orci lacinia justo.</div>
<div class="questionbox">Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse a ultrices felis. Sed mollis pharetra tortor condimentum congue.</div>

It will remove the defaults options

The code above is actually is not overriding the [‘style_formats’] default value in tinymce_before_init but adding it. As default there’s no “style_formats” in there. So if there’s a “style_formats” in the tinymce config, it will use that custom configuration we defined instead of using the defaults.

How to keep the defaults style formats

I can’t really find a way to leave the defaults, so i just simply recreate it.

/* Filter */
add_filter( 'tiny_mce_before_init', 'my_wpeditor_formats_options' );

/**
 * Add Dropcap option but keep the defaults.
 */
function my_wpeditor_formats_options( $settings ){

    /* Default Style Formats */
    $default_style_formats = array(
        array(
            'title'   => 'Headings',
            'items' => array(
                array(
                    'title'   => 'Heading 1',
                    'format'  => 'h1',
                ),
                array(
                    'title'   => 'Heading 2',
                    'format'  => 'h2',
                ),
                array(
                    'title'   => 'Heading 3',
                    'format'  => 'h3',
                ),
                array(
                    'title'   => 'Heading 4',
                    'format'  => 'h4',
                ),
                array(
                    'title'   => 'Heading 5',
                    'format'  => 'h5',
                ),
                array(
                    'title'   => 'Heading 6',
                    'format'  => 'h6',
                ),
            ),
        ),
        array(
            'title'   => 'Inline',
            'items' => array(
                array(
                    'title'   => 'Bold',
                    'format'  => 'bold',
                    'icon'    => 'bold',
                ),
                array(
                    'title'   => 'Italic',
                    'format'  => 'italic',
                    'icon'    => 'italic',
                ),
                array(
                    'title'   => 'Underline',
                    'format'  => 'underline',
                    'icon'    => 'underline',
                ),
                array(
                    'title'   => 'Strikethrough',
                    'format'  => 'strikethrough',
                    'icon'    => 'strikethrough',
                ),
                array(
                    'title'   => 'Superscript',
                    'format'  => 'superscript',
                    'icon'    => 'superscript',
                ),
                array(
                    'title'   => 'Subscript',
                    'format'  => 'subscript',
                    'icon'    => 'subscript',
                ),
                array(
                    'title'   => 'Code',
                    'format'  => 'code',
                    'icon'    => 'code',
                ),
            ),
        ),
        array(
            'title'   => 'Blocks',
            'items' => array(
                array(
                    'title'   => 'Paragraph',
                    'format'  => 'p',
                ),
                array(
                    'title'   => 'Blockquote',
                    'format'  => 'blockquote',
                ),
                array(
                    'title'   => 'Div',
                    'format'  => 'div',
                ),
                array(
                    'title'   => 'Pre',
                    'format'  => 'pre',
                ),
            ),
        ),
        array(
            'title'   => 'Alignment',
            'items' => array(
                array(
                    'title'   => 'Left',
                    'format'  => 'alignleft',
                    'icon'    => 'alignleft',
                ),
                array(
                    'title'   => 'Center',
                    'format'  => 'aligncenter',
                    'icon'    => 'aligncenter',
                ),
                array(
                    'title'   => 'Right',
                    'format'  => 'alignright',
                    'icon'    => 'alignright',
                ),
                array(
                    'title'   => 'Justify',
                    'format'  => 'alignjustify',
                    'icon'    => 'alignjustify',
                ),
            ),
        ),
    );

    /* Our Own Custom Options */
    $custom_style_formats = array(
        array(
            'title'   => 'DropCap',
            'inline'  => 'span',
            'classes' => 'dropcap',
        ),
    );

    /* Merge It */
    $new_style_formats = array_merge( $default_style_formats, $custom_style_formats );

    /* Add it in tinymce config as json data */
    $settings['style_formats'] = json_encode( $new_style_formats );
    return $settings;
}

Play nice with other plugin.

As you can see from the code above it’s rewriting all the “style_formats” configuration. If there’s two plugin adding style formats, it will override each other (the latest will win). Example:

/* Filter */
add_filter( 'tiny_mce_before_init', 'dropcap_plugin_that_add_style_formats_options' );

/**
 * Add Dropcap options to the style_formats drop down.
 */
function dropcap_plugin_that_add_style_formats_options( $settings ){

    /* Our Own Custom Options */
    $style_formats = array(
        array(
            'title'   => 'DropCap',
            'inline'  => 'span',
            'classes' => 'dropcap',
        ),
    );

    /* Add it in tinymce config as json data */
    $settings['style_formats'] = json_encode( $style_formats );
    return $settings;
}

if another plugin added at later priority

/* Filter (at later priority) */
add_filter( 'tiny_mce_before_init', 'boxes_plugin_that_add_style_formats_options', 20 );

/**
 * Add Dropcap options to the style_formats drop down.
 */
function boxes_plugin_that_add_style_formats_options( $settings ){

    /* Our Own Custom Options */
    $style_formats = array(
        array(
            'title'   => 'Boxes',
            'items' => array(
                array(
                    'title'   => 'Quote Box',
                    'block'   => 'div',
                    'classes' => 'quotebox',
                    'icon'    => 'blockquote',
                ),
                array(
                    'title'   => 'Question Box',
                    'block'   => 'div',
                    'classes' => 'questionbox',
                    'icon'    => 'wp_help',
                ),
            ),
        ),
    );

    /* Add it in tinymce config as json data */
    $settings['style_formats'] = json_encode( $style_formats );
    return $settings;
}

The drop cap option will be gone.

So the proper way to add style drop down (and play nice with other plugin) is to check if “style_formats” is enabled and merge it with our own. Here’s an example:

/* Filter */
add_filter( 'tiny_mce_before_init', 'dropcap_plugin_that_add_style_formats_options' );

/**
 * Add Dropcap options to the style_formats drop down.
 */
function dropcap_plugin_that_add_style_formats_options( $settings ){

    /* Our Own Custom Options */
    $new_formats = array(
        array(
            'title'   => 'DropCap',
            'inline'  => 'span',
            'classes' => 'dropcap',
        ),
    );

    /* Check if custom "style_formats" is enabled */
    if( isset( $settings['style_formats'] ) ){

        /* Get old style_format config */
        $old_formats = json_decode( $settings['style_formats'] );

        /* Merge it with our own */
        $new_formats = array_merge( $new_formats, $old_formats );
    }

    /* Add it in tinymce config as json data */
    $settings['style_formats'] = json_encode( $new_formats );
    return $settings;
}

If all plugin do this, we can use multiple plugin adding formats options.

How to add CSS style for the elements

You can learn more here: How to add Editor Style from Plugin.

Add in Theme vs Plugin

I highly recommend to add this feature in plugin and not in theme. Because I think content creation should not be theme-dependent. Theme can disable the custom style or override it.

“Big” Theme did similar thing.

Yes. And I don’t agree with the opinion that it’s okay to add custom elements in content (as long as it’s not shortcode) because the content still available when user switch theme.

5 Comments

  1. Tim

    Thanks! I couldn’t find any documentation on how to group styles into a dropdown.

    By the way, you can enable merging with the defaults with $settings[‘style_formats_merge’] = true;

    Reply
  2. Mike

    Hey,

    Great article! Got a question though – I want to have in item that has sub/child items but not allow multiple selection of them. Each of the sub items would have a common class though.

    I’ve tried using the “exact’ key setting it to true but it made no difference.

    Thanks,
    Mike

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *