First, a quick caveat. While you can use WordPress’ new feature pointers in your themes and plugins today, the practice is highly discouraged. There are a number of reasons why, but let’s leave it with one:
Feature pointers were introduced with WordPress 3.3 as a core feature, but need further development before they’re ready for distributed use by other developers.
Keeping this in mind, you can still use this cool new feature in themes and plugins on your own site or distributed for client use.
A simple feature pointer consists of three different things:
- The PHP code that wires up the pointer
- The HTML markup of the pointer itself
- The JavaScript that manages its behavior.
PHP Wireup
Your PHP code takes care of registering your feature pointer with WordPress.
This functionality would be entirely useless if the end user can’t turn it off. So the first thing you need to do is assign your feature pointer a unique name. Then, before you include the markup or front-end scripts, check to see if the user has previously dismissed the pointer.
If I’ve already read your message and dismissed it, forcing me to read it again is the best way to ensure I never use your plugin again. Remember this; it’s important!
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
add_action( 'admin_enqueue_scripts', 'jdm_tut_pointer_header' );
function jdm_tut_pointer_header() { $enqueue = false; $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ); if ( ! in_array( 'jdm_tut_pointer', $dismissed ) ) { $enqueue = true; add_action( 'admin_print_footer_scripts', 'jdm_tut_pointer_footer' ); } if ( $enqueue ) { // Enqueue pointers wp_enqueue_script( 'wp-pointer' ); wp_enqueue_style( 'wp-pointer' ); } } |
We check to see if our pointer, “jdm_tut_pointer,” has been dismissed by this user.
If it hasn’t been dismissed, then we wire up the appropriate scripts and styles to add our pointer. We also queue up a callback function to add our HTML markup and custom JavaScript.
HTML Markup
The “wp-pointer” stylesheet includes all of the styles we need to build a pointer. The arrows, icons, colors, etc are pre-defined for you.
A typical pointer will consist of a headline and some body text. So we’ll use an h3
tag for the headline and p
tags for the body.
1
2 3 4 5 6 7 |
JavaScript
We’ll keep everything in one function for simplicity. While we build our markup, we’ll also build and output our JavaScript.
The feature pointer script is actually just a jQuery plugin. We select the element to which the pointer will be attached (in this case the comments menu on the admin screen) and set the position of the arrow on the side of the bubble.
In addition, we output the HTML markup defined above directly inside the script for the pointer.
We also define the behavior exhibited when the pointer is closed or dismissed. In this case, we fire off an AJAX request instructing WordPress to remember that our specific pointer has been dismissed by the user.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// Continue jdm_tut_pointer_footer() ....
?> } // End jdm_tut_pointer_footer() |
All Together
And there you have it! A quick feature pointer in just a few lines of code.
You can use this to call out custom menu items, provide clients with a narrated tour of their new CMS, or to point out changes during the ongoing deployment of a new system. Really, the potential uses for this new feature are endless.
How will you use the new feature pointers on your client sites?
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/* Plugin Name: Jumping Duck Pointer Tutorial Plugin URI: https://jumping-duck.com/tutorial/wordpress-feature-pointers/ Description: Demonstrate feature pointers in WP 3.3 Author: Jumping Duck Media Version: 1.0 Author URI: https://jumping-duck.com */ add_action( 'admin_enqueue_scripts', 'jdm_tut_pointer_header' ); function jdm_tut_pointer_header() { $enqueue = false; $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ); if ( ! in_array( 'jdm_tut_pointer', $dismissed ) ) { $enqueue = true; add_action( 'admin_print_footer_scripts', 'jdm_tut_pointer_footer' ); } if ( $enqueue ) { // Enqueue pointers wp_enqueue_script( 'wp-pointer' ); wp_enqueue_style( 'wp-pointer' ); } } function jdm_tut_pointer_footer() { $pointer_content = ' Welcome Jumping Duck Readers!';$pointer_content .= ' This is an example of an admin pointer. ';$pointer_content .= ' You can use it in your themes ' ;$pointer_content .= 'and plugins.'; ?> echo $pointer_content; ?>', position: { edge: 'left', align: 'center' }, close: function() { $.post( ajaxurl, { pointer: 'jdm_tut_pointer', action: 'dismiss-wp-pointer' }); } }).pointer('open'); }); // ]]> } ?> |
Thanks Eric. Great tutorial and just what I was looking for. All the other tutorials I could find on the new ‘Feature Pointers’ are missing the all important code to remember if the pointer has been dismissed or not.
This is a great example of how to use pointers. I have been looking at many and this is the first one I have seen that is straight forward enough to follow.
Do you know how I could link several of them together, to form a tour? I’d like to have a pointer pop up after activating my plugin and offer to give the user a tour. If they opt into the tour then it would walk them through the features of the plugin. If you have any insight how I could link several pointers together to accomplish this it would be greatly appreciated.
Thanks again for a great article!
Nick
I see in the JS there appears:
This is brittle as any single quote will that appears in
$pointer_content
will cause a JavaScript parse error, since it is not getting escaped in any way. The JS really needs to be:My company wrote a GUI for this as well: http://wordpress.org/plugins/custom-pointers/
In the future we plan to integrate an Export option so developers can create a Collection of Pointers for their Plugin using the GUi and then export that as code for inclusion in their Plugins.