The Easiest Javascript Modal for Administrative Pages in WordPress 4.*

February 17th, 2018 Permalink

WordPress is so widely used, and there is so much documentation and discussion, that for some topics it can be a little challenging to wade through the years of code snippets to find the exact use case needed. In my case, in the midst of working on a plugin, I suddenly found the unexpected need for a trivial modal dialog to be added to a specific administrative page. WordPress and modals have an interesting relationship; there are all sorts of plugins and guides for integrating various modal implementations, but none of them are exactly lightweight, something you can run up in a few minutes of work. That niche in the ecosystem is occupied by the built-in use of Thickbox, widely used by plugin developers, despite being one of those things where the code is the documentation and everything else is just commentary.

Thickbox has a number of modes, and near all examples focus on the more common use case of (a) loading content into an iframe or AJAX request, and (b) opening the modal via a designated link element. So, in essence, a matter of adding a little HTML to the page, writing a backend endpoint or page to deliver the modal content, and letting the built-in functionality handle the rest. What if you just want to present content locally, however, opening a modal via direct invocation in Javascript, manipulating the DOM to add content, and then binding a few listeners with jQuery to handle functionality? Fortunately, Thickbox can handle that. For the sake of this example, let us say that we want to add modal functionality to the administrative post editing page, and we are working in a plugin.

Add the Necessary Javascript and CSS to the Page

The first step is to ensure that the necessary Javascript and CSS is added to the edit post page:

/**
 * Add scripts and CSS to the admin post edit page.
 */
function example_add_admin_post_edit_scripts($hook) {
  // Only run the function for an edit post page.
  if ($hook != 'post-new.php' && $hook != 'post.php') {
    return;
  }

  // Set up the thickbox, since we'll be using it. This is simple enough.
  wp_enqueue_style('thickbox');
  wp_enqueue_script('thickbox');

  // Queue up our own custom styles and scripts for managing the modal
  // and contents.
  wp_enqueue_style(
    'example-admin-post-css',
    plugin_dir_url(__FILE__) . 'css/admin-post-edit.css'
  );
  wp_enqueue_script(
    'example-admin-post-js',
    plugin_dir_url(__FILE__) . 'js/admin-post-edit.js',
    array(
      'jquery'
    )
  );
}

add_action('admin_enqueue_scripts', 'example_add_admin_post_edit_scripts');

Assemble the Modal Opening Javascript

Next, sort out the modal opening code in admin-post-edit.js. In this case the example is a simple choice modal with defined buttons, but once a modal is opened all things are possible: population via AJAX, complicated logic to change content in response to user actions, and so forth.

jQuery(document).ready(function() {
  // ID of the modal content wrapper.
  var modalContentWrapperId = 'example-modal-wrapper';
  // Define the initial content of the modal; this will be reset each time.
  var initialModalContent = [
    '<div class="example-modal-content">',
    '<p class="example-modal-text></p>',
    '<button class="example-modal-yes">Yes</button>',
    '<button class="example-modal-no">No</button>',
    '</div>'
  ].join('\n');

  // Add a hidden wrapper div to the page to hold the modal content.
  // When the Thickbox modal opens, the contents of this div are moved into 
  // the modal, but this div stays put.
  jQuery(document.body).prepend([
    '<div id="' + contentWrapperId + '" class="example-hidden"></div>'
  ].join('\n');

  /**
   * Open the modal with a given title and text content.
   */
  function openModal (title, text, callback) {
    // Create the initial content, destroying any prior content from
    // previous uses of the modal.
    jQuery('#' + contentWrapperId).html(initialModalContent);
    
    // Set the text.
    jQuery('.example-modal-text').text(text);

    // Add listeners to the buttons. The call to tb_remove() closes
    // the thickbox modal.
    jQuery('example-modal-yes').one('click', function () {
      tb_remove();
      callback(null, true);
    });
    jQuery('example-modal-no').one('click', function () {
      tb_remove();
      callback(null, false);
    });

    // Open the thickbox, which moves the child DOM elements
    // of the specified element ID into the modal.
    tb_show(
      title,
      '#TB_inline?inlineId=' + modalContentWrapperId
    );
  }
});

Now it is Done

That is really all there is to it. This approach saves a lot of time on those occasions when all that is needed is a simple and straightforward modal. If the content and function of the modal must be much more complex than the example here, then it is worth considering the other modes of the Thickbox modal, and have it load via AJAX, or into an iframe, with a supporting backend endpoint.