Add TinyMCE and Quicktags visual editors to WordPress comments form

TinyMCE to comments respond

At WordPress powered site backend you have these great Javascript visual text editors TinyMCE (Visual) and Quicktags (HTML/Text). Wouldn't it be great to bring those two to your WordPress blog or site frontend to be used inside our comments form? Sounds great so lets proceed. In this article I'll present two ways how to integrate TinyMCE and Quicktags visual editors into your WordPress comment system.

Since WordPress 3.3 we have wp_editor() function whose idea is to simplify adding visual editor instance for developers not familiar with TinyMCE and Quicktags Javascript code. This function does this job perfectly but it has its own flaws. Most importantly WordPress editor API is currently under major rewrite and because of that underlying PHP class _WP_Editors is currently singleton and marked as final to prevent being extended by our own editor class. This means that with default code you can't have multiple visual editor instances on the same page, and that the only way to modify some more detailed editor parameters is to fork current class and maintain your own fork your self.

If you try to add visual editor to comments form you will discover another problem. TinyMCE isn't designed to be moved around in DOM once initialized. WordPress comment-reply.js code does just that when you click on reply/cancel reply link on threaded comments powered WordPress site. In this article, besides showing you how to add visual editor to your comments textarea, I'll present two ways to modify comment-reply.js source code to remove TinyMCE before moving comments form in DOM. I will also present ways to add TinyMCE back once your comments textarea has settled on new place inside DOM after you click on reply/cancel reply link on your comments board.

Replacing comments form textarea with visual editors

This part largely depends on your WordPress theme. Your theme author might have used WordPress comment_form() function to create comments textarea and surrounding elements or he maybe opted for doing this comments form code him self. You should browse trough your theme comments.php and functions.php files and search for code in charge of this. If your theme author has used comment_form() function you can use comment_form_defaults filter to replace textarea with visual editor (code goes into your theme functions.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function your_theme_wp_editor(){
    ob_start();
    wp_editor( '', 'comment', array(
        'media_buttons' => false,
        'textarea_rows' => '3',
        'tinymce' => array(
            'plugins' => 'inlinepopups, wordpress, wplink, wpdialogs',
            'theme_advanced_buttons1' => 'bold, italic, underline, strikethrough, forecolor, separator, bullist, numlist, separator, link, unlink, image',
            'theme_advanced_buttons2' => ''
            ),
        'quicktags' => array('buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close')
        )
    );
 
    return ob_get_clean();
}
 
public function your_theme_comment_form_defaults($args) {
    $args['comment_field'] = your_theme_wp_editor();
 
    return $args;
}
add_filter('comment_form_defaults', 'your_theme_comment_form_defaults');

If your theme author has created comment form him self you just need to look for <textarea id="comment"></textarea> inside your theme files (most probably comments.php or functions.php) and replace it with something like this:

1
2
3
4
5
6
7
8
9
10
11
wp_editor( '', 'comment', array(
    'media_buttons' => false,
    'textarea_rows' => '3',
    'tinymce' => array(
        'plugins' => 'inlinepopups, wordpress, wplink, wpdialogs',
        'theme_advanced_buttons1' => 'bold, italic, underline, strikethrough, forecolor, separator, bullist, numlist, separator, link, unlink, image',
        'theme_advanced_buttons2' => ''
        ),
    'quicktags' => array('buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close')
    )
);

For wp_editor() customization please take a look at reference documentation or its source code inside wp-includes/class-wp-editor.php. Also keep in mind that visual text editor lives inside div tag and that theme authors usually place textarea inline element inside paragraph tag or similar. Since we have replaced this inline element with block level element like visual text editor, your theme CSS might require some adjustments.

WordPress filters comment text for non admin users so the next thing to do is to exclude filtering elements our current visual editor setup is using. Here's how to do it for visual editors button setup from my previous calls to wp_editor() (this goes into your theme's functions.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function your_theme_allowed_tags() {
    global $allowedtags;
    $allowedtags['ul'] = array();
    $allowedtags['ol'] = array();
    $allowedtags['li'] = array();
    $allowedtags['strong'] = array();
    $allowedtags['ins'] = array(
        'datetime' => true
    );
    $allowedtags['del'] = array(
        'datetime' => true
    );
    $allowedtags['pre'] = array(
        'lang' => true,
        'line' => true
    );
    $allowedtags['span'] = array(
        'style' => true
    );
    $allowedtags['img'] = array(
        'width' => true,
        'height' => true,
        'src' => true,
        'alt' => true
    );
    $allowedtags['a'] = array(
        'target' => true,
        'href' => true,
        'title' => true,
    );
}
add_action('init', 'your_theme_allowed_tags');

First solution for fixing comment-reply.js to support visual editors

In this first solution we will replace built in comment-reply.js with our own comment-reply.js copy, modified to support removing TinyMCE before moving comment form and re-adding it back after move is complete. If we assume that our new comment-reply.js and its non minified copy comment-reply.dev.js is placed inside directory named scripts inside our theme root, here's code to replace WordPress comment reply code with our own (should go to functions.php):

1
2
3
4
5
6
7
8
9
10
11
function your_theme_scripts(){
    // Load minified or not
    $script_suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '.dev' : '';
 
    // Deregister WordPress comment-reply script
    wp_deregister_script('comment-reply');
 
    // Register our own comment-reply script
    wp_register_script('comment-reply', get_bloginfo('template_url').'/scripts/comment-reply'.$script_suffix.'.js');
}
add_action('wp_enqueue_scripts', 'your_theme_scripts');

And here's inside of our your_theme/scripts/comment-reply.dev.js. You can place the same thing inside your_theme/scripts/comment-reply.js but you should minify it first using Closure Compiler Service or similar tool.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
addComment = {
    moveForm : function(commId, parentId, respondId, postId) {
        var t = this;
 
        // Remove editor if necessary
        t.red();
 
        var div,
        comm = t.I(commId),
        respond = t.I(respondId),
        cancel = t.I('cancel-comment-reply-link'),
        parent = t.I('comment_parent'), post = t.I('comment_post_ID');
 
        if ( ! comm || ! respond || ! cancel || ! parent)
                return;
 
        t.respondId = respondId;
        postId = postId || false;
 
        if ( ! t.I('wp-temp-form-div') ) {
                div = document.createElement('div');
                div.id = 'wp-temp-form-div';
                div.style.display = 'none';
                respond.parentNode.insertBefore(div, respond);
        }
 
        comm.parentNode.insertBefore(respond, comm.nextSibling);
 
        if ( post && postId )
            post.value = postId;
        parent.value = parentId;
        cancel.style.display = '';
 
        // Add editor if necessary
        t.aed();
 
        cancel.onclick = function() {
            var t = addComment;
 
            // Remove editor if necessary
            t.red();
 
            var temp = t.I('wp-temp-form-div'), respond = t.I(t.respondId);
 
            if ( ! temp || ! respond )
                return;
 
            t.I('comment_parent').value = '0';
            temp.parentNode.insertBefore(respond, temp);
            temp.parentNode.removeChild(temp);
            this.style.display = 'none';
            this.onclick = null;
 
            // Add editor if necessary
            t.aed();
 
            return false;
        }
 
        return false;
    },
    I : function(e) {
        return document.getElementById(e);
    },
    red : function() {
        /* TinyMCE defined means wp_editor has Visual or both Visual and HTML/Text editors enabled
         * If editor is in HTML editor only mode our work here is unnecessary
         */
        if(typeof(tinyMCE) == 'undefined')
            return;
 
        var tmce = tinyMCE.get('comment');
        if (tmce && !tmce.isHidden()){
            /* Remove TinyMCE from textarea if necessary
             * and mark the current editor tab as Visual
             */
            this.mode = 'tmce';
            tmce.remove();
        }else{
            /* Html editor can be moved in DOM without removal
             * so we just mark current editor tab as Html
             */
            this.mode = 'html';
        }
    },
    aed : function() {
        if(typeof(tinyMCE) == 'undefined')
            return;
 
        if (this.mode == 'tmce'){
            /* Add Visual editor to textarea using code from wp-includes/js/editor.js
             * enqueued by _WP_Editors PHP class whenever Visual editor is enabled.
             * This code switches to Visual editor with id 'comment'
             */
            switchEditors.go('comment', 'tmce');
        }else if (this.mode == 'html'){
            /* Add HTML/Text editor to textarea using code from wp-includes/js/editor.js
             * enqueued by _WP_Editors PHP class whenever Visual editor is enabled.
             * If Visual editor is not enabled 'return' above makes this code off limits.
             * This code switches to HTML editor with id 'comment'
             */
            switchEditors.go('comment', 'html');
        }
    }
}

I wont get into this code details because I've commented important parts. Long story short, when we do wp_editor() besides other stuff this enqueues editor.js that supports TinyMCE and Quicktags by allowing you to shuffle between them and similar. I've used function switchEditors.go from editor.js to add visual editor back after it has been moved in DOM. I'm also saving currently selected editor tab (Visual or HTML/Text) into variable to be able to restore proper one. This code has worked 100% percent fine so far, if you encounter any problems please reply.

Second solution for fixing comment-reply.js to support visual editors

Second solution is built on the idea of Reuben from Revood.com. His code doesn't work fine for me because he didn't take into an account that he must do some magic when user clicks cancel reply link. So here it is my second solution.

Basically we use comment_reply_link filter to make clicking on reply link call our own your_theme_editor.move_form() function instead of WordPress comment-reply.js AddComment.MoveForm() function. This function of ours then does removal of TinyMCE if necessary and then calls WordPress comment-reply.js code to do its thing. Also our own code will modify cancel reply event handler to first remove TinyMCE before moving form in DOM after clicking on cancel reply link.

First things first lets intercept comment reply link event handler using comment_reply_link filter (this code goes into functions.php):

1
2
3
4
function your_theme_comment_reply_link($link) {
    return str_replace('onclick=', 'onclick="your_theme_editor.move_form(this, event);" data-onclick=', $link);
}
add_filter('comment_reply_link', 'your_theme_comment_reply_link');

Now we need to enqueue our intercept code. I've placed it inside file named visual_editor.dev.js inside scripts directory inside my theme root. This is how I've enqueued it:

1
2
3
4
5
6
7
8
function your_theme_scripts(){
    // Load minified or not
    $script_suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '.dev' : '';
 
    // Enqueue our code
    wp_enqueue_script('your_theme_editor', get_bloginfo('template_url').'/scripts/visual_editor'.$script_suffix.'.js');
}
add_action('wp_enqueue_scripts', 'your_theme_scripts');

Following code is to be placed inside visual_editor.dev.js, minified using Closure Compiler Service or similar tool, and then placed inside visual_editor.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
your_theme_editor = {
    /*
     * Called from comment_reply_link as onclick
     */
    move_form : function(element, event) {
        var t = this;
 
        // Prevent default link behavior for reply
        event.preventDefault();
 
        // Pickup original onclick event from data-onclick
        var onclick = element.getAttribute('data-onclick');
 
        // Turn data-onclick into function and save it into old
        var old = new Function(onclick);
 
        // Remove editor if necessary
        t.red();
 
        // Execute saved onclick callback
        old.call(element, event);
 
        // Add editor if necessary
        t.aed();
 
        // Get cancel DOM element
        cancel = addComment.I('cancel-comment-reply-link');
 
        //Save old onclick callback into old_cancel
        old_cancel = cancel.onclick;
 
        // Replace saved onclick callback with our own
        cancel.onclick = function(event){
            // Babysit Microsoft broken browsers (IE8 and older)
            event = event || window.event
 
            // Prevent default link behavior for cancel reply
            event.preventDefault();
 
            // Remove editor if necessary
            t.red();
 
            // Execute saved cancel reply onclick callback
            old_cancel.call(cancel);
 
            // Add editor if necessary
            t.aed();
        }
    },
    red: function() {
        /* TinyMCE defined means wp_editor has Visual or both Visual and Html text editors enabled
         * If editor is in HTML editor only mode our work here is unnecessary
         */
        if(typeof(tinyMCE) == 'undefined')
            return;
 
        var t = this;
        var tmce = tinyMCE.get('comment');
        if (tmce && !tmce.isHidden()){
            /* Remove TinyMCE from textarea if necessary
             * and mark the current editor tab as Visual
             */
            t.mode = 'tmce';
            tmce.remove();
        }else{
            /* Html editor can be moved in DOM without removal
             * so we just mark current editor tab as Html
             */
            t.mode = 'html';
        }
    },
    aed : function() {
        if(typeof(tinyMCE) == 'undefined')
            return;
 
        var t = this;
        if (t.mode == 'tmce'){
            /* Add Visual editor to textarea using code from wp-includes/js/editor.js
             * enqueued by _WP_Editors PHP class whenever Visual editor is enabled.
             * This code switches to Visual editor with id 'comment'
             */
            switchEditors.go('comment', 'tmce');
        }else if (t.mode == 'html'){
            /* Add HTML editor to textarea using code from wp-includes/js/editor.js
             * enqueued by _WP_Editors PHP class whenever Visual editor is enabled.
             * If Visual editor is not enabled 'return' above makes this code off limits.
             * This code switches to HTML editor with id 'comment'
             */
            switchEditors.go('comment', 'html');
        }
    }
}

I really don't think this Javascript code needs additional explanation because I've really commented a lot and it should be relatively clear what I've done. Like my first solution this one also works 100% for me and if you find any bugs please report here.

If you would like to see the end results of this code you can scroll down a bit and leave your comment to this article. TechyTalk.info is currently using my first solution to comment reply not working with TinyMCE problem.

If this procedure looks too complicated or you just do not have time to do it your self, I can be hired to modify your theme to add TinyMCE and Quicktags visual editors to your WordPress comments form.

19 thoughts on “Add TinyMCE and Quicktags visual editors to WordPress comments form

    1. Marko Author

      Thanks. You will need to adjust code to your idea but if you don’t do any DOM manipulation on “live” TinyMCE it must work. Simply add TinyMCE to your element after AJAX success using:

      switchEditors.go('your-id', 'tmce')

      or manually (without using code from editor.js) using:

      tinyMCE.execCommand('mceAddControl', false, '_id_of_tinymce_instance_');

      Good luck!

      Reply
      1. NgocHuyPAV

        Hi, I really like add visual editors to WordPress comments form! But your post is difficult for a beginner like me. Can you create a clip to illustrate this article? If you can, people will thank you very much! Of course I would be very grateful to you!

        Reply
        1. Marko Author

          This article is already one of the longest articles on my blog, so I don’t plan on adding more code to it. You can always click that services link in upper right corner of this site and hire me to implement this on your site.

          Reply
  1. Cam

    Hi Marko,
    I’d love to hire you for a project related to this thread, however, the Contact Form 7 plugin seems to be not working properly when I try to message you through your services and contact page.

    If you’re interested and/or available, shoot me an email at my email address for the details. (Also, feel free to delete this comment if you’d like.)

    ~Cam

    Reply
    1. Marko Author

      Hello Cam,
      I’ve tested all contact forms and right now they appear to be working. What caused problems in the past is that Contact Form 7 is (by default) using Akismet service plugin, and when the service is down, so are my forms. Anyways, I’ve sent you an email. ;)

      Reply
  2. NgocHuyPAV

    Hi there,

    Thank you. I’ve done success with lite Sampression lite, but still a small number of questions as to adjust the width of the text form to suit the theme. Then I try with itheme2, but the author use in comments.php file. I tried to replace and check with the above code but failed. Can you explain more about:

    “If your theme author has created comment form him self you just need to look for inside your theme files (most lẽ comments.php or functions.php) and replace it with something like this ”

    Thank you very much!

    Reply
    1. Marko Author

      Hello,
      that sentence just means that if theme author hasn’t used plain comment_form() template tag, steps to implement visual editor depend on the theme in question. It might be easier or harder than steps outlined in this article. comments.php file you’ve linked to uses comment_form() template tag but it overrides all arguments so steps from this article don’t apply. To implement TinyMCE you should modify $custom_comment_form to output visual editor instead of textarea.

      Reply
      1. NgocHuyPAV

        Thanks to the enthusiastic help from you.I am not a professional editing code, I rather difficult to access with your article.But nonetheless could say I was successful 50%.

        I continue to surf the web and I’ve found a plugin that I have used before: CKEditor For WordPress, but now it’s wonderful in spite of some minor problems, provide an editor of frames per opinion, and of course in the article editor.

        Reply
  3. Rufino

    Thanks for your post, is the only one with full explanation of all concepts.
    Two points that I can´t correct.  Any help will be appreciate.
    I use a BLANK theme with comment form build in comments.php and first solution for fixing comment-reply.js

    • When click Replay bottom of a comment the form doesn’t move under the comment, stay at the end of the page.
    • I can´t see the tags bottoms menu in Text/Html edit mode. ‘‘quicktags’ => array(‘buttons’ => ‘strong,em,link,block,del,ins,img,ul,ol,li,code,close’)

     

    Reply

Leave a Reply

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