There has been a lot of talk over the last two days about loading scripts, particularly jQuery, correctly in WordPress themes and plugins, and anyone who follows me on Twitter probably knows that this is an issue I bring up a lot. When providing support for my plugins, I discover themes (and plugins) that are loading jQuery incorrectly and thus causing a conflict with my plugin all the time. It is, unfortunately, a very common issue, so now I’m going to show you how NOT to load scripts in the WordPress admin, and also walk you through the correct way of doing it.

A lot of WordPress plugins use jQuery scripts in their settings or other admin pages, and a lot of them do it incorrectly. While I don’t often see this, here is an example of how you absolutely should never do it:

1
2
3
4
function pw_loading_scripts_wrong() {
	echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>';
}
add_action('admin_head', 'pw_loading_scripts_wrong');

Using a function like this will cause a second version of jQuery to be loaded into the head section of every single page in the WordPress admin. Not only will this place the extra jQuery library everywhere it is not needed, it will also most likely break most WordPress interfaces that rely on jQuery.

What about if you want to load a custom jQuery script, but not jQuery itself? You could do this:

1
2
3
4
function pw_loading_scripts_wrong() {
	echo '<script type="text/javascript" src="https://yoursite.com/path/to/custom.js"></script>';
}
add_action('admin_head', 'pw_loading_scripts_wrong');

However, if you do this, you will mostly likely be ensuing the wrath of plugin developers when they find you are breaking their correctly coded systems.

These two examples are not, however, the only incorrect ways to load jQuery and custom JS scripts. Here is another:

1
2
3
4
function pw_loading_scripts_wrong_again() {
	wp_enqueue_script('custom-js', 'wp-content/my-plugin-dir/js/custom.js');
}
add_action('admin_init', 'pw_loading_scripts_wrong_again');

You may ask why this is wrong, especially as it might look like the function does everything in an okay manner, but it doesn’t.

The first reason it is incorrect is that it is using the “admin_init” hook to load the scripts, which is not the correct hook to use. Instead the admin_enqueue_scripts hook should be used in order to ensure that the scripts are loaded at the right time.

The second reason it is incorrect is that, just like in the first two examples, the custom.js file is loaded into every single admin page in WordPress. You should never, ever do this. Always do your best to make sure your plugin is as efficient as possible and is only loading resources when those resources are needed.

And the third reason it is incorrect is because it is using a relative path for the custom.js file. It might load the file correctly in some WP installs, but it will fail in others. You should always load scripts through a complete path.

How do we fix the three problems with this example? Well, let’s start by using the correct hook:

1
2
3
4
function pw_load_scripts() {
	wp_enqueue_script('custom-js', 'wp-content/my-plugin-dir/js/custom.js');
}
add_action('admin_enqueue_scripts', 'pw_load_scripts');

That’s a little better, but not quite. Let’s now fix the problem of the script being loaded through a relative path. To do this, we should use a function that outputs the exact URL to the directory the file is stored in. When writing plugins, the best function to use for this is plugins_url(). Assuming our custom.js file exists inside of a folder called js that is in our plugin’s main folder, the exact function will look like this:

plugins_url( 'js/custom.js' , dirname(__FILE__) )

Add this into our function and we have this:

1
2
3
4
function pw_load_scripts() {
	wp_enqueue_script( 'custom-js', plugins_url( 'js/custom.js' , dirname(__FILE__) ) );
}
add_action('admin_enqueue_scripts', 'pw_load_scripts');

This is getting much closer to being correct, but we still have the problem of our script being loaded on all pages. How do we take care of that?

Luckily, the admin_enqueue_scripts hooks allows us to pass a variable called $hook to our pw_load_scripts() function. This $hook will contain the name, or hook, of the current admin page, which we can then use to conditionally load our scripts.

For this first example, let’s assume that we want to load some extra JS into the edit.php page, which is the primary page/file loaded when viewing a list of any post type, such as the Posts or Pages page.

1
2
3
4
5
6
7
8
function pw_load_scripts($hook) {
 
	if( $hook != 'edit.php' ) 
		return;
 
	wp_enqueue_script( 'custom-js', plugins_url( 'js/custom.js' , dirname(__FILE__) ) );
}
add_action('admin_enqueue_scripts', 'pw_load_scripts');

This says the following: “if we are NOT on the edit.php page, get out of here and do not continue this function”, which means that our “custom.js” file will only be loaded if we ARE on the edit.php page. Pretty cool right?

What about some of the other pages? Perhaps you want to load scripts anytime a post list, a new post, or an edit post page are displayed? Then we can do this:

1
2
3
4
5
6
7
8
function pw_load_scripts($hook) {
 
	if( $hook != 'edit.php' && $hook != 'post.php' && $hook != 'post-new.php' ) 
		return;
 
	wp_enqueue_script( 'custom-js', plugins_url( 'js/custom.js' , dirname(__FILE__) ) );
}
add_action('admin_enqueue_scripts', 'pw_load_scripts');

You can do the same thing for any page in the WordPress admin, and here is a list of some of the other pages:

  • index.php – the Dashboard
  • upload.php – the Media library
  • link-manager.php – the Links page
  • edit-comments.php – the Comments list page
  • comment.php – editing a specific comment
  • themes.php – Appearance
  • widgets.php – Appearance > Widgets
  • nav-menus.php – Appearance > Menus
  • plugins.php – Plugins
  • users.php – Users
  • options-general.php – Settings > General
  • options-writing.php – Settings > Writing

Most pages are named pretty straight forward, so it’s easy to figure out which files display which pages, but what about custom admin pages? Many (if not most) plugins create custom settings pages in the WordPress admin. How do we go about only loading JS files on those pages? It’s actually quite easy. There are multiple ways to do it actually, but the way I want to show you also assumes you have created your settings page correctly.

Settings pages are created like this:

1
2
3
4
5
function pw_create_settings_page() {
	global $pw_settings_page;
	$pw_settings_page = add_options_page(__('My Plugin Settings', 'my-domain'), __('Plugin Settings', 'my-domain'), 'manage_options', 'my-page-slug', 'pw_callback_function');
}
add_action('admin_menu', 'pw_create_settings_page');

The settings page is stored in a global variable called $pw_settings_pages (yours will be named differently). This variable will then be equal to the $hook variable in our admin_enqueue_scripts function, and since it is a global variable, we can access it, like so:

1
2
3
4
5
6
7
8
9
10
function pw_load_scripts($hook) {
 
	global $pw_settings_page;
 
	if( $hook != $pw_settings_page ) 
		return;
 
	wp_enqueue_script( 'custom-js', plugins_url( 'js/custom.js' , dirname(__FILE__) ) );
}
add_action('admin_enqueue_scripts', 'pw_load_scripts');

We have now successfully loaded our custom script into our plugin’s settings page AND avoided loading it anywhere else within the WordPress admin. Awesome.

The process itself is quite simple and there is not a single reason you shouldn’t be doing it this way.

  1. Ben Voran

    Hi Pippin, I’m curious if this theme, http://www.rootstheme.com/ which I use frequently is subject to this jQuery loading issue. It looks like there is something going on in regards to sniffing out the admin pages… But I figured you’d be able to tell quickly!

    • Pippin

      After exploring the source code on Github, I don’t see anything. Doesn’t actually look like it loads a single script in the admin ๐Ÿ™‚

  2. paul

    how do you load the script only on a specific post type edit/new screen?

    • Pippin

      Inside of your function hooked to “admin_enqueue_scripts”, use something like this:

      1
      2
      
      if( ( !isset($post) || 'YOUR POST TYPE' != $post->post_type )
           return;

      That will make it so only the scripts below that line will get loaded if you are on the post type “YOUR POST TYPE”.

    • bali50

      Tried the code below Pippin but it didn’t work. I removed the extraneous opening bracket “(” after the if, but still didn’t work. But when I called the global $post it worked fine.

      global $post;
      if ( !isset($post) || 'product' != $post->post_type )
      return;

    • Pippin

      Doesn’t work in what way? Can you elaborate?

    • Bali Rakhra

      Maybe I was doing something wrong, but I added it to my plugin’s main file as a conditional to load my JS scripts, however the scripts failed to load. But then, by adding the call to the global $post variable first, your code worked, and my scripts loaded just fine. So I’m not sure if everyone has to call the global first, or if it’s just something peculiar to my own set up, but that’s the only way it would work for me.

      My full script is this:

      // check if in Admin section AND on Edit-Post page (post.php).
      if( !is_admin() || $hook != 'post.php' )
      return;
      // now check to see if the $post type is 'product'
      global $post;
      if ( !isset($post) || 'product' != $post->post_type )
      return;
      wp_enqueue_script(........

    • Pippin

      Anytime you reference $post (except inside of a loop), you must set the global.

    • Bali Rakhra

      Cheers Pippin – and my apologies if my comment was too ‘obvious’, I’m not that far up the learning curve yet! Thanks again for all you do!

  3. Aj Clarke

    Thanks for this Pippin. The method I was using before was not nearly as clean and straight forward as using the $hook variable. This is great!

  4. casas en venta

    excellent scripts, I hope I serve to my projects

  5. Wordpressor

    Just out of curiosity, what was wrong with admin_print_styles-$page? The only difference between the old way of enqueing and admin_enqueue_scripts is using global variables for custom options and theme pages, which is, pure evil I guess?

  6. Wordpressor

    Oh, I’ve also checked wp_print_styles() and looks like it got replaced with wp_enqueue_scripts() too! Naming is a bit confusing, but it’s still better to have one action, than two separate ones for js and css.

    Cheers!

  7. Josh Kurz (@JoshKurz)

    great post, Im just wondering one thing. The examples show how to not load pages on optional admin pages, but what about random posts and pages that are not admin which use the shortcode or a widget call to the plugin.

    I would like to only load the scripts and css on certain non admin pages.

    I understand this is not what the post is about, but I cant seem to find the answer and this is the most thorough post I have seen on this issue.

  8. Aman

    I have a question.
    Suppose I have plugin which make some changes to the frontend of my blog using javascript and my javascript code requires jquery. How should I load properly the javascript code. Some themes enque them but not all, so whats the best method do do it it from a plugin.

    • Pippin

      The only proper way to do it is with wp_enqueue_script().

  9. Barry Laminack

    Hey Pippin – first of all thanks for this great site, I just found it last week and it’s awesome.

    I’m glad I found this post, but I can’t for the life of me get the last part to work on my Submenu page. It either works on all pages in the admin menu or none. It might be because of my design, but I don’t know.

    From my base plug-in page I install my table in the db and then call an adminmenu.php page.

    On the adminmenu.php page I create my main menu page (caling add_menu_page) then I create my submenu pages (calling add_submenu_page). Each of these calls a function that simply has an include of the PHP file with the page content.

    Here is some code if it helps –
    ===========================================================
    My base plugin page contains the following:
    if (is_admin () ){
    require_once( plugin_dir_path( __FILE__ ) . ‘includes/adminmenu.php’);
    }
    ===========================================================
    adminmenu.php page:

    //Create the Admin Menus system and content
    function bl_recp_admin_menu () {
    global $bl_recp_manageOptions_page;
    global $bl_recp_clientManager_page;

    //CREATE MAIN MENU
    $bl_recp_manageOptions_page = add_menu_page( ‘RE Client Portal Settings’, ‘RE Client Portal’, ‘manage_options’, __FILE__, ‘bl_recp_settings_content’, plugins_url(‘/images/icon_arrow.gif’,plugin_dir_path( __FILE__ )) );

    //Create “Manage Clients” Sub Menu
    $bl_recp_clientManager_page = add_submenu_page( __FILE__, ‘RE Client Portal – Client Manager’, ‘Manage Clients’, ‘manage_options’, __FILE__.’_client_manager’, bl_recp_manageclients_page);
    }

    //Call the General Setting Page HTML
    function bl_recp_settings_content () {
    //Add the HTML
    include( plugin_dir_path( __FILE__ ) . ‘general_settings.php’);
    }
    //Call the Manage Clients Page HTML
    function bl_recp_manageclients_page () {
    //Add the HTML
    include( plugin_dir_path( __FILE__ ) . ‘client_manager.php’);
    }

    add_action(‘admin_menu’, ‘bl_recp_admin_menu’);
    ===========================================================

    client_manager.php has all the form data and other html. I need the script to run on the file, so using this tutorial this is what I have done:

    I added the following to the adminmenu.php file:

    function bl_recp_load_scripts() {

    global $bl_recp_clientManager_page;

    if( $hook != $bl_recp_clientManager_page )
    return;

    wp_enqueue_script(‘recp-client-insert-js’, ‘/wp-content/plugins/js/test_client_insert.js’, ”, ‘0.0.1a’);
    }
    add_action(‘admin_enqueue_scripts’, ‘bl_recp_load_scripts’);

    ===========================================================

    As is it doesn’t load the script on ANY page , but if I comment out the global line, the if($hook line and the return line the script loads…it just loads on every page.

    Any ideas? I want to do this write, but I need to move on and I’ve spent 2 days trying to figure this out.

    Thanks again, being brand new to WP Plugin development, your site has already helped me a tone.

    • Pippin

      Can you post the code to snippi.com and share the link? That way the formatting remains.

    • Pippin

      I see the problem. Change

      function bl_recp_load_scripts() {

      to

      function bl_recp_load_scripts( $hook ) {
  10. Barry Laminack

    That was it. Thanks!

    • Pippin

      Great!

  11. Dewi Rosalin

    thanks for your tutorial..it really helps me since i’m now working on my plugin..but i have problem with wp_ajax_nopriv..my plugin has working but just for admin..it crashed when non-logged in user and logged-in user (non-admin) using it..i don’t know where’s my fault..here’s my function code: http://snippi.com/s/oe2au26
    and my javascript code:
    http://snippi.com/s/fxlks83
    i hope you want to help me..thank you.. ๐Ÿ™‚

    • Pippin

      What happens for logged out users? Anything?

    • Dewi Rosalin

      that give me it’s page..it looks like wp_ajax_nopriv doesn’t work..is there something special code that must be added to use this hook??

    • Pippin

      The nopriv action only fires when logged out. Are you by chance testing while logged in?

    • Pippin

      Can you confirm that the ajaxurl is setup for the logged out user?

  12. Dewi Rosalin

    i’ve tested while logged out either logged in..it makes me confused..it only works for admin,,but not for public and registered..
    is it okay if i use template_redirect ??

  13. James Bowles

    When using wp_enqueue_script() in the admin interface for a custom script using jQuery, do we also need to enqueue jQuery or is it safe to say that the WordPress admin panel always has jQuery loaded?

    • James Bowles

      Is jQuery UI also loaded by default in the admin panel or should that be enqueued as well?

    • Pippin

      The WP admin always has jQuery and jQuery UI enqueued.

  14. Glenn Hughes

    Hi Pippin,
    I have a question:
    Do, or can you recommend coders or resources where I might get simple and complex WP plugins created?

  15. Rayssa

    Thank you so much for sharing this! Reading it, I just realized I was doing it the wrong way in my plugin! Now my plugin works fine and is correct too, thanks to you!

  16. THUNDER

    All wrong. load-$pagehook is a very good place to enqueue your scripts and styles if you don’t want to pollute the whole WordPress admin with scripts and styles that only your page needs. The other useful hook is admin_print_scripts-$pagehook and admin_print_styles-$pagehook that might be used to achieve the same, since wp 3.6 you can use wp_enqueue_XXX

    • Pippin

      Those works are all excellent, though they don’t make “admin_enqueue_scripts” incorrect to use. Both have their purposes and both are 100% fine to use.

  17. BJ

    Dear Pippin,

    Is it possible to only load a stylesheet in my head (front-end) when the Javascripts of wp-mediaelement + mediaelement are being called upon?

    I’ve found the helpful Github page of Justin Tadlock about this but he some kinda hardcore the new stylesheet in the head section. Now the new media stylesheet is always being loaded even on pages for this who don’t have an audio or video player.

    https://github.com/justintadlock/theme-mediaelement

    • Pippin

      Are the files getting loaded by WordPress core or by a plugin?

  18. Antonio Sรกnchez

    Perfect, that helped me a lot ๐Ÿ™‚ I had the same problem trying to add jQuery. Thanks!

  19. Kumar Parth

    Hi Pippin,

    I have installed the latest version of WordPress i.e 4.1.2 and when i view the page source of the post edit page, i don’t see the jquery.js or jquery.min.js scripts being loaded. I am using twentyfifteen theme.Can you tell me how we load the jquery.js file in admin and if it is default loaded then why i am not able to see it when i view the source page.

    • Pippin

      Have you instructed WordPress to load them?

  20. Samuel Elh

    Thanks a lot!! helped me enqueue admin scripts only in the right pages and not having them enqueued all around the wp-admin.
    Thanks Pippin ๐Ÿ˜Ž

  21. Rob Gelhausen

    Thank you very much for this post. I was looking everywhere for the answer to this question. I was able to enqueue scripts on Admin Pages that ended in the normal .php but not on the Settings Page I had created for my plugin.

    The global variable did the trick.

    Thank You!

  22. Matt Cromwell

    Hey Pippin,

    Always thankful for these write-ups. I was trying to DEQUEUE scripts with this method and noticed a couple things:

    1) admin_print_scripts() would be a better hook to dequeue with, but it doesn’t seem to pass the $hook variable. So I just set the priority with admin_enqueue_scripts() really high and that seemed to work in this case

    2) I’m not sure why but I couldn’t get !=$hook to work. I had to do this instead:
    if( $hook = ‘widgets.php’ ) {
    wp_dequeue_script( ‘handle’ );
    wp_deregister_script( ‘handle’ );
    }

    Not sure if you have any insight on that, but thought I’d add to the discussion. Thanks!

    • Pippin

      Are you trying to dequeue a script that is loaded on widgets.php or trying to dequeue a script on all pages but widgets.php?

    • Matt Cromwell

      Dequeue it ONLY on the widgets.php page . This worked exactly as intended:

      function wip_dequeue_enfold_gmaps($hook) {

      if( $hook = ‘widgets.php’ ) {
      wp_dequeue_script( ‘avia-google-maps-api’ );
      wp_deregister_script( ‘avia-google-maps-api’ );
      }
      }

      add_action(‘admin_enqueue_scripts’, ‘wip_dequeue_enfold_gmaps’, 999 );

    • Pippin

      That’s actually setting $hook to the value of “widgets.php” so technically that’s not right and will dequeue it everywhere.

      If you var_dump() $hook there, what’s the value you get?

  23. Matt Cromwell

    I see now… I think I only needed the priority set in order to dequeue after the other script was loaded.

    Thanks!

  24. Barrett Golding

    I am trying to get a list of registered script handles, for a script-loading plugin I’m building. Trouble is when I get the list in my plugin’s settings/options page, with something like :
    wp_list_pluck( $wp_scripts->registered, ‘handle’ );

    This grabs scripts registered with hook: ‘admin_enqueue_scripts’.

    What I want is those registered to front-end, with hook: ‘wp_enqueue_scripts’.

    But I might have to be in the front-end environment to get that, or emulate it somehow from the back-end. Aka, I need to generate the front-end list of registered scripts but do it from the back-end admin section. (Hope I’m being clear). Thoughts?

  25. Barrett Golding

    I’m building a plugin that loads scripts per-post. On the admin settings page, I’d to grab all the scripts registered via the ‘wp_enqueue_scripts’ hook.

    But the admin pages fires only ‘admin_enqueue_scripts’ (not ‘wp_enqueue_scripts’). So the global $wp_scripts only has scripts registered for admin.

    Any idea how to — from the back-end — trigger all the ‘wp_enqueue_scripts’ front-end firings (including the active theme’s), then return that $wp_scripts value for use on a settings/option page?

    • Barrett Golding

      In case anyone else runs into this same issue, found two solutions. Used this first one:

      The problem is the scripts get registered in memory, so to access the front-end memory from the back-end I fired the front-end hook using `do_action( ‘wp_enqueue_scripts’) then set $wp_scripts as the value of a transient. Had to hook the function to ‘shutdown’ b/c if I fired it any earlier, then it effected the admin display (likely b/c scripts/styles were enqueued that were meant for the front-end but now running in the back-end.)

      The other method was a function that loaded any post via `wp_remote_get` hooked to ‘wp_enqueue_scripts’, but with a really high number for priority, so it fired last — after all the other scripts were regestered — then also set transient of $wp_script. This worked too. I’ll post code if anyone’s interested.

  26. hope737mamun

    i need to pass custom category from custom post in tineMCE 4 but is’s not working

    // Localize the script with new data
    $translation_array = array( ‘categories’ => json_encode( list_categories(0) ) );

    wp_enqueue_script( ‘some_handle’, ‘mce_options’, $translation_array );

    function list_categories( $cat_id, &$output = array() )

    {
    $categories = get_categories( ‘hide_empty=0&orderby=name&order=ASC&parent=’ . $cat_id );

    foreach( $categories as $cat ) {
    $output[] = array( ‘text’ => $cat->name, ‘value’ => $cat->term_id );
    list_categories( $cat->term_id, $output );
    }
    return $output;
    }

    $list = list_categories(0); // to get an array of categories

    add_action( ‘admin_enqueue_scripts’, ‘mce_admin_scripts’ );
    function mce_admin_scripts( $hook ) {
    global $post;
    if( ( !isset($post) || ‘bxcarousel’ != $post->post_type ) {
    add_action( “admin_head-$hook”, ‘mce_admin_head’ );
    }
    }
    function mce_admin_head() {
    echo ‘var mce_options=’ . json_encode( array( ‘categories’ => list_categories(0) ) ) . ‘; ‘;
    }

    • Pippin

      Could you show me the complete code you’re using?

  27. Saqib Ali

    Hi,
    Thanks For every one for helping.
    Below function did not show the proper path there is one thing missing.
    function pw_load_scripts() {
    wp_enqueue_script(‘custom-js’, ‘wp-content/my-plugin-dir/js/custom.js’);
    }
    add_action(‘admin_enqueue_scripts’, ‘pw_load_scripts’);

    you have to include / before wp-content, like this one
    wp_enqueue_script(‘custom-js’, ‘/wp-content/my-plugin-dir/js/custom.js’);

    Thanks

  28. Gaurav Kumar

    hi, If i want to change the Navigation menu my current theme does not have good navigation menu. i love other theme navigation menu what are the codes to touch for successfully and responsive working. Or any other method.

Comments are closed.