Javascript | PHP | Wordpress

Adding Content To Yoast SEO Analysis Using YoastSEOJS

I recently received a request from a previous client asking if I could debug why their custom field content had stopped being analysed by their Yoast SEO plugin. It turns out they had updated the plugin and Yoast made a breaking change in regards to adding content to be analysed. To quote Yoast directly:

In the upcoming 3.0 release of Yoast SEO (for WordPress), we’re moving the entire content analysis from the server to the client side. This way our users can get instant feedback on the content they’re writing, while they’re writing it.

With the moving of content analysis to the client side Yoast removed the WordPress hook previously used to add content for analysis. This was obviously the cause of the issue the client was experiencing. To fix this you must create a plugin using Yoast’s new YoastSEOJS system. At the time of creating the plugin there wasn’t much in the way of documentation on how to do it, so I figured I would share my experience to help anyone else having issues.

The short version of what you need to do is register a plugin with YoastSEOJS and have your plugin return the modified content. Unfortunately this plugin is not a standard WordPress plugin and is a special one used by Yoast.

This post has been re-written to be more informative and provide better code. Please follow along again if you previously read this tutorial.

The code for the client was quite specific so I’m instead going to show an example I put together on my local install. First let’s attach our JavaScript file, we haven’t wrote it yet but we can still enqueue it first without any issues.

In your theme’s functions.php file add the following:

Feel free to change the prefix on the function and also the path as needed. Even better add it to a Theme Setup Class but that is outside the scope of this tutorial. The dependency is required and was previously yoast-seo but I recently couldn’t get it to work until I changed it to yoast-seo-admin-script, YMMV.

Note: If the fields are only available on a specific post type, then feel free to add in a conditional to only enqueue the script when it will be needed.

Now for the JavaScript code. Make you JS file and store it where ever you pointed to when en-queuing the script. Now add the following code:

Now there are a lot of areas where they will be specific to the project you are working on, however I know a lot of developers like to use Advanced Custom Fields so I went with that as an example. I’m not going to go into massive detail here since the code is pretty simple (except maybe the use of Prototypes).

Anyway. Here we are registering a plugin with Yoast SEO using a method called registerPlugin. The plugin is called myYoastPlugin, feel free to change the name as you wish. Note that the name of your JavaScript Object (MyYoastPlugin) does not have to match this, but it would be best to keep them at least related if not the same. We then execute a method to grab the data we want to analyse, I’ve just called it getData.

getData should do whatever you need to grab the data. If you need to call something via AJAX, say from an external service, that is fine. If the content is on the page you can just grab the text via JavaScript. Here I have two example Advanced Custom Fields set up. One is a simple text field, the other a TinyMCE editor. Getting content from the text field is easy. Just use a jQuery expression to select it, in ACF’s case it uses acf-field-{field_name} for its IDs, where field name is the name (not label) you gave the field. We assign this to a object variable called custom_content, tell the plugin we are ready and the register our modification method, which will finally add our extra content to the default content area, with Yoast.

Now you might be wondering why we don’t get the TinyMCE field data there. Well from my experience, you cannot. TinyMCE appears to not be initialized at that point and so we instead do it when the getCustomContent method is called. In the case of the ACF TinyMCE field, we need to know its name so we can get the content from the global tinymce object. To do that we target the placeholder textarea that TinyMCE hooks to and get its ID as that happens to be the ID used to in the editors array.

If you are registering the field yourself, the principle should be the same. The only difference would be you wouldn’t need to grab the ID for your TinyMCE field as you’ll have registered it so you would be able to hard-code the name. You could hard-code it with ACF too, but if you change the name via the interface this way it should automatically change in the JS code too.

One last thing we do is actually instantiate our Yoast Plugin, to do that we have to wait until Yoast is initialized. So we listen for Yoast firing its ready event and then instantiate.

That’s it. This tutorial has been rewritten as it was far to specific to my clients needs, thanks to Kevin in the comments below for pointing it out and for help with cleaning up the code. Also the documentation on how to use the Yoast JS system was pretty lacklustre at the time of writing so there may well be better ways to do this, but this seems to work well enough.

If you have any questions, just let me know in the comments below and I will help however I can. Also if you can fit it into 140 characters you can ask me on Twitter @paulbrobinson.

About Paul Robinson

WordPress plugin & theme developer, user of Laravel and learner of React.js. Can usually be found listening to Japanese, and Korean Pop or Vocaloid music while developing.

Advertisment

6 responses to “Adding Content To Yoast SEO Analysis Using YoastSEOJS

  1. Kevin

    Hi, Paul. Thanks for your post. I’ve been struggling with adding content to Yoast SEO’s content analysis. Specifically, I have two other TinyMCE editors on a custom post type that I want to include in the analysis. I won’t pretend to know as much about this as you but I’m confused about one aspect of your script: It pulls the content to be analyzed from the post meta field “my_super_awesome_post_data”. This doesn’t seem to fit the concept of live content analysis. Is this the only option? Wouldn’t it require that the content would have been saved at some point in order to pull it in? Why not refer to the field on the post editor page that contains the data to be analyzed?

    Also, if I’m not mistaken, you’re call to wp_localize_script is missing a couple parentheses at the end of the line.

    Reply
    • Paul Robinson

      Hi Kevin,

      First. Thanks for spotting the mistake on the localize script. I copied this code from my library of scripts and must have missed them. It’s fixed now at least.

      Yep. You can grab the data however you wish. In this case the client wanted it to work as it did previously. Yes, that means doing the analysis on save. So I just chose to grab the data from the custom field via AJAX.

      By all means grab the content however you feel suits the project or the requests of your client. For standard text fields just grab it via jQuery. In fact you are spot on that I should re-factor this as it is very specific to the client and in no way follows the real-time analysis aspect of Yoast. I’ll get onto rewriting it tonight.

      Anyway. Thanks for your comments & hopefully, once re-factored, it will be helpful to you.

  2. Kevin

    I think that was the fastest response I’ve ever gotten to a comment. 🙂 Thanks! First off, your post was a HUGE help once I realized it was doing a couple things I didn’t need. I’m including what I did in my comments below for anyone else who has my issue and finds your post. I hope you don’t mind. Fair warning to others: this could be a clunky implementation but it worked for me.

    First, I ended up removing the add_custom_content_to_analysis() function altogether. Again, I’m no expert but I couldn’t seem to get this function to work. To be fair, I didn’t spend much time trying to figure it out. Is there something missing in the example that would make an Ajax call to this function or should it work as written?

    Then, I revised the way the javascript file was being enqueued so it only gets included when editing a specific post type. This way I can be sure that the TinyMCE editors the script looks for are on the page.

    Finally, I revised the call to getCustomContent to reference the two additional TinyMCE editors that I wanted to analyze thanks to this article: http://stackoverflow.com/questions/5759297/getting-wp-tinymces-content. I changed this…

    to this…

    Once I did this, the script worked like a charm. It seems like it could be easily changed to accommodate any other fields I might want to include. Now I can stop banging my head on the wall! You have no idea what a lifesaver this post was. Thanks so much!

    Reply
    • Paul Robinson

      Haha, no worries. I’m busy working so I’m doing other stuff, answering comments etc in between waiting for responses to emails. 😉

      Ah, glad it was helpful in some way. AJAX in WP can be a royal pain sometimes, it is probably best not to use that if not needed though, as I mentioned it was specific to the client hence why I want to re-factor the code a little.

      Thanks for the code examples. If you are okay with it I’d love to use it to help improve the tutorial.

      Also I changed your code so it was code highlighted. I know it messes up the width of the comments, I need to fix that while I have some spare time.

    • Kevin

      Thanks, Paul! I’m looking forward to seeing how you rewrite the example. Feel free to use what I wrote, such as it is. Again, I really appreciate your help on this!

    • Paul Robinson

      Haha. It won’t be anything spectacular, but hopefully it will get the idea across. I decided to go with using Advanced Custom Fields as an example since I know a lot of developers that use if for clients due to how easily it can be integrated into a theme. The same ideas should apply regardless of how the meta boxes are added though.

      Still, even if it only helps a little, it’ll be worth the effort.

Leave a Reply

Your email address will not be published. Required fields are marked *