Skip to content

Instantly share code, notes, and snippets.

@jeffreyvr
Created September 4, 2020 09:33
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffreyvr/434fe738d9a9d074e073ec14da672e16 to your computer and use it in GitHub Desktop.
Save jeffreyvr/434fe738d9a9d074e073ec14da672e16 to your computer and use it in GitHub Desktop.
Using wp.editor in widgets

Within your widget, you can include a editor with a textarea and the class custom-widget-wp-editor.

 <textarea id="<?php echo $this->get_field_id( 'header' ); ?>" name="<?php echo $this->get_field_name( 'header' ); ?>" class="custom-widget-wp-editor"><?php echo $header; ?></textarea>

Make sure you include the JS-file within the widgets and customizer view.

/**
 * Add a fix for tinymce within widgets.
 *
 * @return void
 */
function custom_widgets_widget_editor_script() {
  global $pagenow;

  if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
    wp_enqueue_editor();

    wp_enqueue_script( 'wp-editor-widgets', get_stylesheet_directory_uri() . '/js/wp-editor-widgets.js', array( 'jquery' ), '1.0', false );
  }
}

add_action( 'admin_enqueue_scripts', 'custom_widgets_widget_editor_script' );
jQuery(document).ready(function ($) {
/**
* Removes all active editors. This is needed so they can be
* re-initialized after a widget-updated event.
*/
function custom_widgets_remove_editors() {
custom_widgets_get_editors().forEach(function (id) {
wp.editor.remove(id);
});
}
/**
* Searches the widgets areas for editors.
*
* @return array
*/
function custom_widgets_get_editors() {
let editors = [];
// Doesn't look so great, but we need to target like this to support both the regular
// widgets view and the Customizer. Also, we don't want to include the placeholder
// widget that is in the DOM on the widgets.php page.
$(document).find("#customize-theme-controls .custom-widget-wp-editor, #widgets-right .custom-widget-wp-editor").each(function () {
editors.push($(this).attr('id'));
});
return editors;
}
/**
* Initializes all wp.editor instances.
*/
function custom_widgets_set_editors() {
custom_widgets_get_editors().forEach(function (id) {
wp.editor.initialize($('#' + id).attr('id'), {
tinymce: {
wpautop: true
},
quicktags: true,
mediaButtons: true
});
});
// To prevent the editor from not submitting the value, we click the switch html tab.
$(document).contents().find('.widget-control-save').off().on('click', function (e) {
custom_widgets_get_editors().forEach(function (editor) {
let form = $('#' + editor).closest('form');
form.find('.switch-html').click();
});
});
}
// Trigger removal and setting of editors again after update or added.
$(document).on('widget-updated widget-added', function () {
custom_widgets_remove_editors();
custom_widgets_set_editors();
});
// Init.
custom_widgets_set_editors();
});
@bartvollebregt
Copy link

Hi Jeffrey,

This sadly doesn't seem to be working anymore...

@jeffreyvr
Copy link
Author

@bartvollebregt Do you have more info? Maybe I can look into it. Seems to working fine for me still.

@molcsa
Copy link

molcsa commented Mar 25, 2021

Hi @jeffreyvr,

thanks for the code, I managed to get it work, and it helps me a lot to achieve what I want in my widget!

However live update doesn't work for me in customize.php window, and the editor value also does not get submitted on widget saving, because there is no Save button for widgets in this window, which you are targeting with the // To prevent the editor from not submitting the value, we click the switch html tab. part of the code.
I found a solution for this by adding an event listener to the wp.editor.initialize function:

function custom_widgets_set_editors() {
        custom_widgets_get_editors().forEach(function (id) {
            wp.editor.initialize($('#' + id).attr('id'), {
                tinymce: {
                    wpautop: true,
                    setup: function (editor) {
                        editor.on('dirty', function (e) {
                            editor.save();
                            $('#' + id).change();
                        }); 
                        // fix for missing dirty event when editor content is fully deleted    
                        editor.on('keyup', function (e) {
                            if(editor.getContent() === '') {
                                editor.save();
                                $('#' + id).change();
                            }
                        });
                    }
                },
                quicktags: true,
                mediaButtons: true
            });
        });
}

Notes:

  • Optionally you can add a delay of about 1000 ms to the event listeners, so they get triggered only when you stop typing.
  • The keyup event listener is a fix for a tinymce bug which occurs on my side (maybe it's only by me, idk). Namely that if I select all text in the editor and delete it, the dirty event doesn't get triggered for some reason. I tried with other event types (input, change), but no luck. The keyup event on the other hand does not capture text formatting, pasting content from context menu etc., so they must be used together. If someone knows a better fix, feel free to share.
  • By using the event listeners the // To prevent the editor from not submitting the value, we click the switch html tab. part of your code is not needed, because this methods works both in cutomize.php and widgets.php window.
  • Additionally it solves the issue of triggering the publish and save buttons of these windows respectively, which was asked under your blog post.

@jeffreyvr
Copy link
Author

Thanks for sharing @molcsa!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment