Enqueueing Scripts or Styles only when your plugin needs it
Using Javascipt or CSS in BuddyPress plugins are interesting ways to take care of the user experience. I think, as a plugin author, you need to have two concerns :
- you might not be the only one to use Javascript, other plugins can be activated in the community website,
- your CSS rules might not get along with the activated theme
As a result, i advise you to :
- enqueue your Javascript or CSS only when your plugin needs it,
- allow the theme to override your CSS rules
To illustrate an usage example, you can test the following tutorial. It comes with a BuddyPress plugin “boilerplate” (BuddyPlug) that you will find in the additional resources and will help you to achieve this “roadmap”:
Steps
- Choose the best hook to enqueue your scripts
- Build your own conditional tags
- Allow the theme to override your stylesheet
- Additionnal resources
Choose the best hook to enqueue your scripts
Depending on the context, BuddyPress offers two hooks to rely on in order to safely load your scripts. The first one is bp_enqueue_scripts
for enqueueing your script on the front end, here’s an example on how to use it from your plugin’s class:
class BuddyPlug { /* focusing on bp_enqueue_script */ private function __construct() { $this->setup_globals(); $this->setup_hooks(); } private function setup_globals() { // let's define some globals to easily build the url to your scripts $this->version = '1.0.0'; $this->file = __FILE__; // url to your plugin dir : site.url/wp-content/plugins/buddyplug/ $this->plugin_url = plugin_dir_url( $this->file ); // url to your plugin's includes dir : site.url/wp-content/plugins/buddyplug/includes/ $this->includes_url = trailingslashit( $this->plugin_url . 'includes' ); // url to your plugin's js dir : site.url/wp-content/plugins/buddyplug/includes/js/ $this->plugin_js = trailingslashit( $this->includes_url . 'js' ); // url to your plugin's css dir : site.url/wp-content/plugins/buddyplug/includes/css/ $this->plugin_css = trailingslashit( $this->includes_url . 'css' ); $this->component_id = 'buddyplug'; $this->component_slug = 'buddyplug'; } private function setup_hooks() { // As soon as WordPress meets this hook, your cssjs function will be called add_action( 'bp_enqueue_scripts', array( $this, 'cssjs' ) ); } public function cssjs() { // Your css file is reachable at site.url/wp-content/plugins/buddyplug/includes/css/buddyplug.css wp_enqueue_style( 'buddyplug-css', $this->plugin_css . 'buddyplug.css', false, $this->version ); // Your script file is reachable at site.url/wp-content/plugins/buddyplug/includes/js/script.js wp_enqueue_script( 'buddyplug-js', $this->plugin_js . 'script.js', array( 'jquery' ), $this->version, true ); } }
As you can see, your function cssjs()
waits for bp_enqueue_scripts
before using the two WordPress functions to load your style (wp_enqueue_style()
) and your script (wp_enqueue_script()
). At this point, your stylesheet and your javascript will load everywhere on the front end of the site.
The second one is bp_admin_enqueue_scripts
for enqueueing your script within the WordPress Administration screens. If for instance your plugin is using a settings page, it can be interesting to load some extra javascript. Concerning CSS, a good practice, in this area, is to use the WordPress Administration markup so that your plugin administration pages are styled the same way than the other part of the Administration. That’s why, the following example does not enqueue a stylesheet:
class BuddyPlug { /* focusing on bp_admin_enqueue_scripts */ private function setup_hooks() { add_action( 'bp_enqueue_scripts', array( $this, 'cssjs' ) ); // As soon as WordPress Administration meets this hook, your admin_scripts function will be called if( is_admin() ) add_action( 'bp_admin_enqueue_scripts', array( $this, 'admin_scripts' ) ); } public function admin_scripts() { // Your script file is reachable at site.url/wp-content/plugins/buddyplug/includes/js/admin.js wp_enqueue_script( 'buddyplug-admin-js', $this->plugin_js .'admin.js', array( 'jquery' ), $this->version, 1 ); } }
In the above code, your function admin_scripts()
waits for bp_admin_enqueue_scripts
before enqueueing your script (wp_enqueue_script()
). At this point, your javascript will be loaded in every Administration screens.
Build your own conditional tags
Your plugin do not need to play on every page of the community site. To avoid any troubles with other plugins, it’s best you add some control on where your scripts are loaded. The “where” are the areas your BuddyPress plugin creates. For instance, it could be:
- A new component, using the BP_Component API
- An extension of a core component
- A new group extension, using the BP_Group_Extension API
- Your plugin’s setting page in the Administration
The core file /buddypress/bp-core/bp-core-template.php
contains some useful functions to help you identify your plugin’s areas before enqueueing your javascript or your css. When creating a new component, you can build a directory page, in this case your first conditional tags will be:
function buddyplug_is_current_component() { /* buddyplug_get_component_id() is a function that returns your component id ($this->component_id in BuddyPlug class) for instance the activity component id is 'activity'. In this example the function simply returns 'buddyplug' I advise you to observe how the component.php file of the BuddyPlug plugin is creating its component */ return bp_is_current_component( buddyplug_get_component_id() ); } function buddyplug_is_directory() { if( buddyplug_is_current_component() && bp_is_directory() ) return true; return false; }
So in order to only load script in your directory pages you simply need to check for buddyplug_is_directory()
before enqueueing your script. If you use the BuddyPlug plugin, it will show you the result of these two functions, here is a screen capture to illustrate.
If you simply check for buddyplug_is_current_component()
, then your script will also load on the plugin’s tab in the members profiles.
Your plugin can also extend the settings component to add a new tab to it to let the member customize its behavior. The following code creates a new conditional tag that you will be able to use to be sure your script is loaded in this area
function buddyplug_is_user_settings() { /* buddyplug_get_component_slug() is a function that simply returns the slug of the plugin ($this->component_slug in BuddyPlug class) in this case it's 'buddyplug' it means the sub navigation added to the settings component is reachable at site.url/members/userlogin/settings/buddyplug I advise you to have a look at the setup_settings_bp_nav() function in the component.php file of the BuddyPlug plugin to see how the sub navigation is created. */ if( bp_is_settings_component() && bp_is_current_action( buddyplug_get_component_slug() ) ) return true; return false; }
Here’s a screen capture of this particular tab:
If you are using the Group Extension API, you can build some new conditional tags to check if the Group main tab, the Group admin edit tab, or the creation step of your plugin are loaded.
/** * The Group Main tab for your plugin */ function buddyplug_is_group_front() { if( bp_is_single_item() && bp_is_groups_component() && bp_is_current_action( buddyplug_get_component_slug() ) ) return true; return false; } /** * The Group creation step for your plugin */ function buddyplug_is_group_create() { if( bp_is_group_creation_step( buddyplug_get_component_slug() ) ) return true; return false; } /** * The Group Admin Edit tab for your plugin */ function buddyplug_is_group_edit() { if( bp_is_group_admin_page() && bp_is_action_variable( buddyplug_get_component_slug(), 0 ) ) return true; return false; } /** * Are we in your plugin's group area ? */ function buddyplug_is_group_area() { if( buddyplug_is_group_front() || buddyplug_is_group_create() || buddyplug_is_group_edit() ) return true; return false; }
This is a screen capture that illustrates the buddyplug_is_group_edit()
conditional tag
Now, as an example, let’s imagine you wish to only load your script when on any of the area your plugin’s has created on the front end, then your function cssjs() in the BuddyPlug class could be :
public function cssjs() { if( ( bp_is_active( $this->component_id ) && buddyplug_is_component_area() ) || ( bp_is_active( 'groups' ) && buddyplug_is_group_area() ) ) { // Your css file is reachable at site.url/wp-content/plugins/buddyplug/includes/css/buddyplug.css wp_enqueue_style( 'buddyplug-css', $this->plugin_css . 'buddyplug.css', false, $this->version ); // Your script file is reachable at site.url/wp-content/plugins/buddyplug/includes/js/script.js wp_enqueue_script( 'buddyplug-js', $this->plugin_js . 'script.js', array( 'jquery' ), $this->version, true ); } }
Note that we check if the groups component is active using the BuddyPress function bp_is_active()
.
As the last step of this part, you need to also make sure the javascript your plugin is using for its settings page is only loaded on this particular page. To do so you could use the $_GET['page']
variable or use the WordPress get_current_screen()
function, or when the 1.9 version will be released simply check for the bp_admin_enqueue_scripts argument. See the following example:
class BuddyPlug { /* focusing on bp_admin_enqueue_scripts */ private function setup_hooks() { if( is_admin() ) { add_action( bp_core_admin_hook(), array( $this, 'admin_menus' ) ); add_action( 'bp_admin_enqueue_scripts', array( $this, 'admin_scripts' ), 10, 1 ); } } /** * Creates the admin pages */ public function admin_menus() { $this->hook_suffixes[] = add_submenu_page( $this->settings_page, __( 'BuddyPlug Options', 'buddyplug' ), __( 'BuddyPlug Options', 'buddyplug' ), 'manage_options', 'buddyplug', 'buddyplug_admin_settings' ); /* Using an array can help you get the screen ids of each of your administration menus... $this->hook_suffixes[] = add_menu_page() */ } /** * Eqnueues script */ public function admin_scripts( $hook = '' ) { if( empty( $hook ) ) $hook = bp_core_do_network_admin() ? str_replace( '-network', '', get_current_screen()->id ) : get_current_screen()->id; /* only loads the script if you are in one of your plugin's Administration screen if( in_array( $hook, $this->hook_suffixes ) ) { wp_enqueue_script( 'buddyplug-admin-js', $this->plugin_js .'admin.js', array( 'jquery' ), $this->version, 1 ); } } }
Allow the theme to override your stylesheet
Finally, like BuddyPress does (see first link of the Additional resources section), you definatively should allow the theme to override the main css of your plugin. The very great reason is that, this way, the community Administrator will be able to add the final touch to your plugin that will make it look perfect in his theme 🙂
class BuddyPlug { /* focusing on allowing theme to override the plugin's css file on front end */ public function cssjs() { //Search in theme's folder $css_datas = (array) $this->css_datas(); // before enqueueing the best style wp_enqueue_style( $css_datas['handle'], $css_datas['location'], false, $this->version ); } /** * The theme can override plugin's css */ public function css_datas() { $file = 'css/buddyplug.css'; // Check child theme if ( file_exists( trailingslashit( get_stylesheet_directory() ) . $file ) ) { $location = trailingslashit( get_stylesheet_directory_uri() ) . $file ; $handle = 'buddyplug-child-css'; // Check parent theme } elseif ( file_exists( trailingslashit( get_template_directory() ) . $file ) ) { $location = trailingslashit( get_template_directory_uri() ) . $file ; $handle = 'buddyplug-parent-css'; // use our style } else { $location = $this->includes_url . $file; $handle = 'buddyplug-css'; } return array( 'handle' => $handle, 'location' => $location ); } }
Additional resources
- wp_enqueue_script() in WordPress Codex
- wp_enqueue_style() in WordPress Codex
- Add BuddyPress styles to a theme
- You can learn more about BP_Component from its Codex page
- You can learn more about BP_Group_Extension from its Codex page
- You can download the BuddyPlug example plugin from his github repository