We made a simple XML file (an SVG “image”) in the post “Inkscape SVG Button”. It is extremely simple yet showcases many concepts concerning the use of a vector program like Inkscape and developing an efficient workflow for file creation. Now we’re going to that “image” and add it to a post! I keep putting image in quotes because an SVG “image” is not a typical binary file, like a jpeg or png or even a gif but a text file and as such can be, amongst other things, encoded. This is important, because in order to use our svg file the way we wish, it will have to be encoded in a format the browser can read!
There are Javascript and PHP functions that can encode our file for us, but to keep it simple, we will use an online encoder. In particular, we will be using “base 64” encoding. All this stuff is interesting and helpful in other areas of computer skills so I highly encourage finding out more about “Number base” and “encoding” & “decoding” data.
Be careful which online service you use, not all produce the same quality results! I use https://www.base64encode.org/ and what about if I have a file I want to decode? Well it just so happens the good folks at base64encode have made things really easy for us by providing the ying to the yang https://www.base64decode.org/.
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="40px" viewBox="0 0 40 40" version="1.1" style="enable-background:new 0 0 40 40;" id="Layer_1" height="40px"><path id="path875" style="clip-rule:evenodd;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 20,5 C 25.6602,-6.0820001 40.5625,3.0932999 40,13 39.4146,23.313499 22.8955,32.1919 20,40 17.104,31.997101 0.7812998,23.5088 0,13 -0.7344001,3.1050001 14.957,-6.1265001 20,5 Z" /></svg>
Becomes this when encoded:
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDAgNDA7IiBpZD0iTGF5ZXJfMSIgaGVpZ2h0PSI0MHB4Ij48cGF0aCBpZD0icGF0aDg3NSIgc3R5bGU9ImNsaXAtcnVsZTpldmVub2RkO2ZpbGw6bm9uZTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC4xO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIGQ9Ik0gMjAsNSBDIDI1LjY2MDIsLTYuMDgyMDAwMSA0MC41NjI1LDMuMDkzMjk5OSA0MCwxMyAzOS40MTQ2LDIzLjMxMzQ5OSAyMi44OTU1LDMyLjE5MTkgMjAsNDAgMTcuMTA0LDMxLjk5NzEwMSAwLjc4MTI5OTgsMjMuNTA4OCAwLDEzIC0wLjczNDQwMDEsMy4xMDUwMDAxIDE0Ljk1NywtNi4xMjY1MDAxIDIwLDUgWiIgLz48L3N2Zz4=
But we are not done yet, we still need an html image tag and the proper attributes and properties for this to come off correct! Wrap the above with the following:
<img width="28" src="data:image/svg+xml;base64,
BASE64_ENCODED_DATA_BETWEEN_PRECEEDING_COMMA_AND_TRAILING_QUOTES
">
Okay, now we have the “image” to put in the post, in a format and following the standards that browsers depend on! Next. The Plan.
Here’s where things get really complicated, but at the same time the pieces are simple. Kind of like a paradox. Instead of parallel computing we’re paradoxically computing… Let’s make a list:
This sounds like a tall order, but not only is this a sweet little addition to your posts and pages, more importantly, I’m showing you so many of the pieces of dynamic web development using WordPress, PHP, Javascript utilizing AJAX, as well as nice content creation skills (vectors, svg, and interactive UX design…)
Stay tuned in as I flesh this tutorial out, play with my love button on this post to keep yourselves interested…
Next we’re going to add the html to the DOM, in our case via a PHP script known as a WordPress template file. There is a lot of strategy here so keep your mind awake, you could work this part of code into the WordPress functions.php file for example, I choose to target 2 of my partial templates which get called into play during the WordPress “Loop”, they are both “content” templates called inside the single.php template file. You have to have a knowledge of how the WordPress template file hierarchy system and how it works. If you work with WordPress the template hierarchy is a fundamental concept of this CMS. All that being said, I use JQuery to target the DOM elemens I need, and I am conscious of the DOM elements I am targeting because I designed their structure and implemented the needed classes all within the partial template file I use for content processing. All of this is important to understand how my script finds the DOM elements it needs, and what JQuery functions should be implemented.
On that note, I’m using html 5 and I choose to encapsulate the post content in <article> tags. It makes sense for me to put a class of “post” on this tag because that semantically describes this unit: an article of post. This can be done autommatically thanks to the builting WordPress function post_class(), which adds standard, familiar, predictable WordPress class names to the element where it is used. Now for some even smoother magic, I add the post ID to the article element id attribute, which are 2 separate things, and utilize this data late on when we want to determine if this is a post we want to add our Javascript to! Mind Blower! Keep hose gears churning and you will think of ways you can use these methods to create your own brand of magic. For now, here is what that tag looks like:
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
Now, we’re going to look ahead a little to our JQuery Javascript to show you 1 reason for doing what we did with the article tag…
var post_id = parseInt( $(this).parents('article.post:first').attr('id').replace( 'post-', '' ) );
Okay, this is a lot to explain for one line of code (html & server-side PHP) acted apon by another line of code (client-side Javascript JQuery) and we won’t even cover the entirety of what is going on here at the moment… The goal of it all is to get the “current” post ID (via JQuery), which now exists in the generated markup (via PHP).
We’re using JQuery parents() function which traverses up as many levels as needed to find the selector (‘article.post:first’). Know that there is a parent() function NO ‘s’ which traverses only 1 level, so be aware of this, save yourselves some huge head trauma! We grab the contents of the article “id” attribute which we strategically and programatically filled with the WordPress “Template Tag” (that’s what WordPress calls functions of this type…) the_ID()
, we actually appended to custom text (“post-“) to make it more intuitive to find with our Javascript, and when we do find it, we strip that custom text and make sure we have an integer, and lastly store it in a Javascript variable, called “post_id”, which we will be sending back to the server via “AJAX”.
Now, remember how I told you about the difference between parents() and parent()? Just so happens we get to see it in action immeadiately, in the next line of our script where we need to access the DOM to get the numeral that will be displayed next to our svg “heart”, the part of the the interface which actually utilizes and illustrates the power of AJAX, the manipulation and changing of the DOM/data that the user experiences without a page refesh! You can see, understand, and then employ these concepts in your own UX designs. var $number = $(this).parent().find('.number')
What happens here, is we get the parent of the item clicked, the image, “$(this)”, which is not all that important as to why we do that, that is only wanting to go up 1 level in the DOM. We are looking to get the container of the image, as also we have put the element with the class “number” inside this container. We then use the function find() to get that element. Hopefully explaining the process as we go along in a holistic approach will give you a clear bird’s eye view of how all the components are working together!
Referring back to our list, 3rd item down, add the html markup to the DOM (in our case we’ll be using a WordPress template file which is PHP) ✓
So, how do we get our value into the WordPress database so we can store and retrieve it at will? We have to make a spot for it if it does not exist (create it), if it does exist we will either be reading/retrieving it, or updating it. The only thing we won’t be doing is deleting it. Why did I include that? Because we have just seen the 4 actions known as “CRUD”, create, read, update, delete. We will be concerned with the first 3 only. Wow, so many fundamentals in this little design!
First we will take a look at the function we will place in WordPress functions.php file. This function will be called by JQuery ajax() function. That is another process we will explain a little further down the post, but for now, focus on how the data is stored. We will be using 2 WordPress functions: get_post_meta()
& add_post_meta()
<?php
function ajax_test_have_heart() {
$heart = get_post_meta( $_POST['post_id'], 'heart_post_meta_field', true );
$heart = ( empty( $heart ) ) ? 0 : $heart;
$heart++;
update_post_meta( $_POST['post_id'], 'heart_post_meta_field', $heart );
echo $heart;
die();
}
Pay close attention, I’m about to give you another fundamental gem, this one concerning working with WordPress database fields.Going through the above function, we start with get_post_meta()
and notice we have 3 arguments/parameters, the first being the only one required by the function. Notice we’re retrieving the global $_POST[‘post_id’] value, which comes from our JQuery ajax() function, the same value we pulled from the DOM using JQuery parents() function from the id attribute of the html article tag… that was a mouth full! But see how this is all coming together? The second argument is the field name from the “post_meta” table of the WordPress database, which may or may not exist yet! The 3rd argument “true” tells WordPress we only want a single value, the first value, returned, some keys can have multiple values, specifying “false” will return these values in an array. We check our returned value empty()
for a value, we could have also uses isset()
or === ''
(equals empty string?) and there are reasons why or why not we didn’t, which could be a whole other post. The gem to take here is this: get_post_meta() does not create/add the field and value if it does not exist… that comes with the 2nd function, update_post_meta()
, I’ll let WordPress.org tell it:
“ The function update_post_meta() updates the value of an existing meta key (custom field) for the specified post.
This may be used in place of add_post_meta() function. The first thing this function will do is make sure that $meta_key already exists on $post_id. If it does not, add_post_meta($post_id, $meta_key, $meta_value) is called instead and its result is returned.”
So the important gem to take away here is that it is within this function, specifically update_post_meta()
where the field for the table of the database is created! Because below, the code succeeding this paragraph, is actually responsable for the initial reading/displaying of the table field if it exists and if not, show “0”.
<?php $heart = get_post_meta( get_the_ID(), 'heart_post_meta_field', true );
$heart = ( empty( $heart ) ) ? 0 : $heart;
echo '<span class="heart-button"> <img width="28" src="data:image/svg+xml;base64,BASE64ENCODED_DATA_REMOVED"><span class="number">' . $heart . '</span></span>';
?>
Now it behooves me at this juncture to briefly mention “how” this function ajax_test_have_heart()
is fired. We attach it to a “hook”. WordPress hooks are basically queues where you attach, or “hook”, your function so when the queue is processed (there are many and different queues) your function gets fired at the appropriate time. Using the WordPress add_action()
function, we attach our function to the hook 'wp_ajax_have_heart'
& 'wp_ajax_nopriv_have_heart'
which are unique advanced hooks. I say unique and advanced because when making AJAX calls using hooks, in WordPress we append “wp_ajax_
” (for back-end scripts) & “wp_ajax_nopriv_
” (for front-end scripts) to the name of the “action” parameter (‘have_heart’) we will be using in our ajax() function in our JQuery script. WordPress sees when the action ‘have_heart’ from the JQuery script is called we must run the PHP function ajax_test_have_heart()
!
// have heart icon and function part 2
// Processing the Request
add_action( 'wp_ajax_have_heart', 'ajax_test_have_heart' );
add_action( 'wp_ajax_nopriv_have_heart', 'ajax_test_have_heart' );
We have many of the pieces to the puzzle, yet here we must include a most import part, how WordPress knows to translate betweeen PHP and Javascript. This is done through a very interesting function known as wp_localize_script()
.
We’re going to take this to a whole other level by encapsulating it in a shortcode so we can call the javascript when we want it to appear on particular posts and pages only.
We’re on a role. Going back to our list:
We’re almost done!
The right way to translate PHP to Javascript is by using wp_localize_script
of which, by the way there is also an Object Orientated class & method, the method being called statically known as WP_Scripts::localize
“Accepts an associative array $l10n and creates a JavaScript object:
"$object_name" = {
key: value,
key: value,
...
}
… IMPORTANT!
wp_localize_script()
MUST be called after the script has been registered usingwp_register_script()
orwp_enqueue_script()
.”
The function very simple takes 3 arguments: the script “handle”, the name of the Javascript object, and lastly the data itself as an associative array. If an associative array is not used, you will not get a Javascript object otherwise you will get a Javascript Array! (good to know if considering JSON)
Here are the 2 functions we will be using, and to take it to another level, we will also wrap a shortcode function arround it, enabling us to add the script and localize the script whereever and whenever we choose. Below is the meat of what we want to accomplish, given this way because we don’t need to put this in a shortcode, I just choose to.
wp_enqueue_script( 'have_heart_scripts', get_stylesheet_directory_uri() . '/js/script.js', array( 'jquery' ), '1.0.0', true );
wp_localize_script( 'have_heart_scripts', 'ajaxTest', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
In our wp_enqueue_script()
call, you see first the “handle” which we will use to tell wp_local_script()
which script we want to use. The 2nd argument is the location of the script (whether or not you use a child theme will determine if you use get_stylesheet_directory_uri()
or get_template_directory_uri()
) the former gets the current theme (including child) the latter gets the parent. See, always giving you gems, a virtual treasure chest. The third argument is a group of dependencies, here’s another hint, use an empty array here if you have no required dependencies array()
. The fourth argument is the version of the script, and the fifth (and one of the most important), is whether it is loaded in the head or the foot. Which by the way may become important when localizing a script!
On to our 2nd vital function wp_localize_script()
and in our case we are using it to pass to Javascript the endpoint for our PHP AJAX processor! Kind of a curve ball, but this is one of the most common uses for this function, not it’s intention, but perfect for the requirements. For the first argument we pass it the handle of our script created in the first function (“have_heart_scripts”),for the second argument we pass a string which we would like to be used as the name of the Javascript object to be created (in our case “ajaxTest”),and for the third argument we pass an PHP assoiative array which we can access on the other side (the Javascript side) by using the Javascript Object.Property notation (because at this point it is now available as a Javascript Object, and the object has a property!) like so ajaxTest.ajax_url
For the salt on the fries, I will wrap the 2 functions in a shortcode (we get to learn so much here!).
add_shortcode('have_heart', 'have_heart_function');
function have_heart_function( $atts ) {
$atts = shortcode_atts( array(
'ids' => '',
), $atts, 'have_heart' );
wp_enqueue_script( 'have_heart_scripts', get_stylesheet_directory_uri() . '/js/script.js', array( 'jquery' ), '1.0.0', true );
wp_localize_script( 'have_heart_scripts', 'ajaxTest', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
}
Now, if we wanted to add this via the post editor it would be as simple as adding [have_heart {$post_id}]
where {$post_id} can be found by mousing over any post title link in WordPress Admin screen to edit Posts (go to Dashboard Menu->Posts->All Posts), and simultaneously looking in the status bar, divulging the location of the link which happens to include the {$post_id} as a number in the query part of the URL (?post=’#’). But We’re doing something a little different because I want the script to fire in everypost. So we put it in the template file! This has to be called differently. We use the WordPress do_shortcode()
function to call the shortcode from within the template file like so: <?php do_shortcode("[have_heart ".get_the_ID()."]"); ?>
This makes it super easy because we programmatically or to use a buzzword instead of a wod I made up, dynamically retrieve the post id using the WordPress function get_the_ID()
and this works because our partial “content” template is called within the WordPress Loop! If you don’t know why it’s important to know whether or not your in the loop, don’t worry, sooner or later I’ll get you in the loop with a loop tutorial. See how I did that. Nice.
I’ll come back to describe what’s going on here in our Javascript JQuery file, honestly we’ve done most of the heavy lifting already. But it won’t hurt to describe what’s going on with our $.ajax() function, and maybe a quick noConflict() explanation is in order too…
(function($) {
$(document).on( 'click', '.heart-button img', function(){
// data requirements: The post ID
var post_id = parseInt( $(this).parents('article.post:first').attr('id').replace( 'post-', '' ) );
var $number = $(this).parent().find('.number')
// Sending An AJAX Request
$.ajax({
// data requirements: wp_localize_script(arg1, arg2/object_as_parameter, arg3/object_value_as_parameter)
// admin-ajax.php file within wp-admin
url: ajaxTest.ajax_url,
type: 'post',
data: {
// data requirements: callback function
action: 'have_heart',
post_id: post_id,
},
// Performing UI Changes Using the Response
success: function( response ) {
$number.text(response);
}
})
})
})( jQuery );