Adding Modal Windows in the WordPress Admin Without JavaScript
When creating a theme or plugin for WordPress which requires users to read some documentation, it is a good idea to display the documentation right within the WordPress admin. Otherwise, making the user browse an external site could produce a negative experience from the additional friction.
I have written about displaying documentation within Gutenberg, which enables to make use of the available React-based components for different use cases. For instance, we can display our documentation by reusing the <Guide>
component:
However, what happens if we are not operating within Gutenberg? For instance, we may have coded our plugin long time ago, and migrating it to Gutenberg is too much of an effort. In that case, how could we manage to open a modal window in the WordPress admin?
Unsurprisingly, we can reuse any functionality shipped anywhere by WordPress, not just through Gutenberg. And WordPress does already provide an implementation of a modal window, which is originally used to display plugin information:
Even though designed for plugins, the modal window can nevertheless display any arbitrary content, since it is simply an iframe
embedding the intended page, for any page from the same domain. And opening the modal window can be triggered from anywhere within the WordPress admin, not just from within the plugins page.
For instance, similar to the plugins page, our plugin could print a table with custom entries, and a “View details” link below each entry:
And when clicking on this link, it opens a modal window to display the entry’s corresponding documentation:
In this article we will learn how to do this. The goal is to reuse the WordPress admin’s modal windows for our own themes or plugins, without depending on Gutenberg for this basic functionality.
Loading the JS/CSS assets
WordPress handles the modal windows through the following 3 files:
File wp-includes/js/thickbox/thickbox.js
loads ThickBox, the library that provides the functionality for the modal window.
File wp-includes/js/thickbox/thickbox.css
gives the styles for the modal window, including its placement on the page, and transitions when opening/closing the modal.
File wp-admin/js/plugin-install.js
loads the event which, when clicking on a corresponding link, opens the modal window.
WordPress loads these 3 files for the plugins page:
However, these files are not always loaded in the WordPress admin, only in the plugins page. So, we must add some code to load them in the page where we want to open the modal window. Since the 3 assets are already registered (through file wp-includes/script-loader.php
), we can simply enqueue them for the corresponding page:
add_action('admin_enqueue_scripts', 'enqueue_modal_window_assets');
function enqueue_modal_window_assets()
{
// Check that we are on the right screen
if (get_current_screen()->id == 'my_menu_page') {
// Enqueue the assets
wp_enqueue_style('thickbox');
wp_enqueue_script('plugin-install');
}
}
This code loads the assets for a menu page with slug "my_menu_page"
. Enqueuing file plugin-install.js
also loads thickbox.js
, so we can skip enqueueing this latter one.
Printing the link to open the modal window
The JavaScript assets introduced above load the modal window in the page, and listen for certain links to be clicked; when that happens, the URL under the link’s href
attribute is extracted, and loaded into the modal window’s iframe
.
The following code will print a link which, upon clicking, will open the modal window and load within a custom page from the WordPress admin:
$url = admin_url('admin.php?page=my_menu_page&TB_iframe=true&width=600&height=550&modal_window=true');
printf(
'<a href="%s" class="thickbox open-plugin-details-modal" data-title="%s">%s</a>',
$url,
__('View details', 'graphql-api'),
__('Plugin documentation', 'graphql-api')
);
The link has the following attributes:
- Mandatory classnames
"thickbox"
and"open-plugin-details-modal"
; without these, when clicked the link would be treated normally, opening the URL in the browser window - URL parameter
TB_iframe=true
; otherwise, the modal window opens with an unsuitable format - URL parameters
width
andheight
are added by the ThickBox library when calculating theiframe
dimensions based on the viewport’s size; the WordPress code contains these parameters in advance on the links (for instance, here) but manually adding them or not makes no difference - Optional attribute
data-title
allows to set theiframe
‘stitle
attribute; otherwise,"Plugin details"
is used
When clicking the link, the JavaScript logic creates a new iframe
with the URL under the link’s href
attribute, minus its parameters TB_iframe
, width
and height
. Then, to let our page know that it is being opened inside the modal window, we further add URL parameter modal_window=true
.
Printing the menu page or the modal window
In the WordPress admin, we add pages for our plugin through functions add_menu_page and add_submenu_page. Registered under its own slug, each page is accessed under admin.php?page=menu_page_slug
.
In order to display content in the modal window, this has to be registered as a menu page; but then, this page would also be accessible through the admin menu on its own:
This is something we want to avoid: a link to open the modal window’s contents on its own must not be added to the menu.
We use URL parameter modal_window
added to the link above to provide a solution to this problem: we always register a single page under my_menu_page
, and then print either the content for the normal page, or the content for the modal window, depending on parameter modal_window
being present on the URL or not:
add_action('admin_menu', 'register_my_custom_menu_page');
function register_my_custom_menu_page() {
// If requesting the modal window, print a different output
$option_function = $_REQUEST['modal_window'] ? 'print_modal_window' : 'print_menu_page';
add_menu_page(
__('My Menu Page'),
'my menu',
'manage_options',
'my_menu_page',
$output_function
);
}
/**
* Print the contents for the normal menu page
*/
function print_menu_page() {
// ...
}
/**
* Print the contents displayed within the modal window
*/
function print_modal_window() {
// ...
}
Hiding the admin menu inside the modal window
Because the modal window is being registered as a menu page, it will display the top admin bar:
To hide the admin bar, we create an extra modal-window.css
file for the modal window:
#adminmenumain,
#wpadminbar {
display: none;
}
html.wp-toolbar {
padding-top: 0;
}
And we load this file whenever the parameter modal_window
is present in the URL:
add_action('admin_enqueue_scripts', 'enqueue_modal_window_css');
function enqueue_modal_window_css()
{
// Check that we are on the right screen
if (get_current_screen()->id == 'my_menu_page' && $_REQUEST['modal_window']) {
wp_enqueue_style('modal-window', plugins_url('css/modal-window.css', __FILE__));
}
}
Adjusting the styles inside the modal window
Finally, when printing the contents of the modal window, they may not be directly suitable for this format. For instance, images are by default not resized to fit within the viewport:
To fix this style (and any other that might arise from some particular content), we first wrap the content with a div
with classname "modal-window-container"
:
function print_modal_window()
{
print('<div class="modal-window-container">');
// ...
print('</div>');
}
And now, we can add the necessary styles in file modal-window.css
(added above). To make the images fit within the viewport, we add this CSS code:
.modal-window-container img {
max-width: 100%;
width: auto;
height: auto;
}
Now, images display properly within the modal window:
End result and conclusion
Putting all pieces together, we can appreciate the end result:
Opening modal windows in the WordPress admin can be useful for our themes and plugins, for displaying information to our users. Since this functionality is already shipped within WordPress, it can be achieved very easily, with just a few lines of PHP and CSS code.
modal_window=true needs to be before TB_iframe in the url for this to work
This saved my day!!