Author Archives: Tom Woodward

WP Rest API Tips

WP Rest API Tips

Cowboy Jason Stanley performing a riding trick at the Round-Up, Pendleton, Oregon flickr photo by UW Digital Collections shared with no copyright restriction (Flickr Commons)

I’ve been doing quite a bit more with the WordPress Rest API lately. There’s plenty of documentation and tutorials out there but most of it still feels a bit scattered to me so I’m going to stick a few of the basics here and add a few things that have come up repeatedly that aren’t quite as basic. There’s an attempt here to move upwards in complexity with the examples but to keep them as clean as possible.

This will deal entirely with getting the data. I haven’t done much with using the API to write or modify data.

Get the Info

There are many ways to get data depending on your library of choice or if you’re using vanilla JS. I’ve played with fetch and Axios on the lighter side and jQuery, Vue, and Angular (v1) on the heavier/more involved side of things.

I’ll use jQuery in this version because it’s fairly popular but here’s a Vue example. The example below does a basic jQuery ajax call for the JSON associated with blog information.

See the Pen simple jquery get of WP JSON for the site by Tom (@twwoodward) on CodePen.

The URL Structure/Accessing the Blog Info

Using the codepen above as the example . . .

First, we’re fetching the JSON from the following URL. You can go there now and just take a look at it in wild if you’d like.

I use a Chrome extension called JSON formatter to make the JSON all pretty (like the image below).

Navigating the JSON is pretty straightforward but it’s never a bad idea to over-explain. In these examples, I stored the JSON as a variable named data. You could name it anything but data is pretty standard.

  • get the site title –
  • get the site description/tagline – data.description
  • get the site URL – data.url
  • get the third namespace – data.namespaces[2] – I don’t really know what you’d do with this but I did it as an example of navigating an array (square brackets) vs an object (curly brackets). Alos worth noting that array counting starts at 0 rather than 1. Asking for namespace[3] will get you an ‘undefined’ response.

See the Pen simple jquery get of WP JSON by Tom (@twwoodward) on CodePen.

Getting Posts

The vanilla post URL is That’ll get you all the basics- title, excerpt, content etc. What isn’t necessarily obvious is that now gives you access to the features images, tags, categories etc. That’s handy. I’ll be using the second version in this example so we can pull the featured image (if it exists).

See the Pen simple jquery get of WP JSON with _embedded by Tom (@twwoodward) on CodePen.

Some additional things worth knowing about the posts endpoint . . .

Those are the ones I’ve used most frequently and they’ll provide a decent pattern for the other parameters which are documented here.

Now that we’ve got some data from some URL we’ve constructed, let’s display the data. It’s going to work pretty much the same as the first example but we’re going to throw in an ‘each’ element to repeat the action for each post that is returned.

  $.each(data, function(index, item) {
              $('#posts').append('<h2 ' + backgroundImg(item) +'>'+item.title.rendered+'</h2>');//adds an h2 element with the title to the div with id posts
            }); //each 

That’s fairly in line with what we’ve done before with the exception of the backgroundImg() function. I do that just to simplify things a bit because the path to the background img url is a bit involved and I’d like to have a backup image if the post doesn’t have a featured image.

function backgroundImg (item) {
//first check if there is a featured image or else things will fail
//then ge the url for the image size you want 
       var imgUrl = item._embedded['wp:featuredmedia'][0].media_details.sizes.thumbnail.source_url;
        return 'style="background-image:url('+imgUrl+');"';
    else {
        return 'style="background-image:url(;"';

Take note of the way we access the featured image url.
That’s not necessarily intuitive. The colon would break things if you didn’t have the brackets and the quotes and then since it’s an array, you need to move to the first element with the [0]. I don’t know the logic behind throwing in a colon for this key name but it would have made life easier if they had done something else.

You can do lots of fun things with this stuff. Here’s another example that auto-loads additional posts via scroll trigger. Nothing too fancy, but it starts to get at some interesting possibilities.

Custom Fields

By default WP doesn’t expose your custom fields in the JSON. That could be good if you have secrets there . . . but I don’t. There’s a plugin to do it but you can also allow access on a field by field basis by adding the following to your functions.php file. This is straight from the WP handbook and is referencing a custom field called bp_avatar.

// The object type. For custom post types, this is 'post';
// for custom comment types, this is 'comment'. For user meta,
// this is 'user'.
$object_type = 'post';
$args1 = array( // Validate and sanitize the meta value.
    // Note: currently (4.7) one of 'string', 'boolean', 'integer',
    // 'number' must be used as 'type'. The default is 'string'.
    'type'         => 'string',
    // Shown in the schema for the meta key.
    'description'  => 'bp avatar',
    // Return a single value of the type.
    'single'       => true,
    // Show in the WP REST API response. Default: false.
    'show_in_rest' => true,

register_meta( $object_type, 'bp_avatar', $args1 );//this is the actual name of the field rather than the description above

WP JSON to Timeline JS

As part of some thoughts on building out a series of reflective views for student portfolio blogs, I thought seeing your WordPress posts in the TimelineJS view might be a useful way to look back over your progress.

I intend to wrap this into a custom spreadsheet template and/or a plugin1 but figured I’d sketch out how it works so far in case anyone was interested.

The WP Rest API makes it pretty easy to write the data via Google Script. I just want to cut out chunks of the data and put it in the right fields. The following script does that and writes it to a page named “wp.”

function getPosts() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('wp');
//get the data from the feed and get the last 50 items in this case (set via the URL)
  var url = '';
  var response = UrlFetchApp.fetch(url); // get feed
  var json = response.getContentText(); // 
  var data = JSON.parse(json);
  for (i = 0; i < data.length; i++){
    var ssData = [];    
    ssData.push(data[i].date.substring(0,4)); //post year   
    ssData.push(data[i].date.substring(5,7)); //post month   
    ssData.push(data[i].date.substring(8,10)); //post day   
    ssData.push(data[i].date.substring(11,19)); //post time 
    ssData.push(""); //filler for now for end date  info

    ssData.push(theUrl(data[i]) + data[i].title.rendered + '</a>'); //post title w link
    ssData.push(data[i].excerpt.rendered); //post excerpt
    ssData.push(featuredCheck(data[i])); //post feature image

//have a featured image? then get it . . . 
function featuredCheck (data) {
  try {data._embedded['wp:featuredmedia'][0].media_details.sizes.full.source_url;
       return data._embedded['wp:featuredmedia'][0].media_details.sizes.full.source_url;
  catch (err){
    return "";

//make things a bit neater by building the href via function
 function theUrl (data) {
   return '<a href="' + data.guid.rendered + '">'; //build the url 

1 Or maybe just a page where you throw in a blog URL and get a bunch of alternate view/data options.

Weekly Web Harvest for 2017-05-14

  • Judge rules emoji are proof of intent

    One piece of evidence used against the defendants was an emoji-filled text message they sent to Dahan, which mentioned the house and included the emoji chain “?.”

  • Animating the Virtual DOM by sdrasner

    some really slick stuff in this

  • Many pet rabbits will die in Second Life on Saturday | Rock, Paper, Shotgun

    Virtual rabbits across Second Life [official site] will fall asleep on Saturday then never wake up, now that the their digital food supply has been shut down by a legal battle. The player-made and player-sold Ozimals brand of digirabbits are virtual pets that players breed and care for in the sandbox MMO, and even need to feed by buying DRM-protected virtual food. But they rely on servers. Waypoint reported earlier today that the seller of Ozimals and the Pufflings virtuabirds has received a legal threat he says he cannot afford to fight, so they’ve shut down. By Saturday, rabbits will run out of food and enter hibernation.

  • Arctic stronghold of world’s seeds flooded after permafrost melts | Environment | The Guardian

    It was designed as an impregnable deep-freeze to protect the world’s most precious seeds from any global disaster and ensure humanity’s food supply forever. But the Global Seed Vault, buried in a mountain deep inside the Arctic circle, has been breached after global warming produced extraordinary temperatures over the winter, sending meltwater gushing into the entrance tunnel.

  • WordPress Performance in Theme Development • Calvin Koepke

    I know, some of you are screaming at me already for using CSS Grid to create some basic columns.

    First off, relax. Web development is fun and so is CSS Grid. </div><!-- .entry-content --><footer class=Filed Under: thirdspace

Weekly Web Harvest for 2017-05-07

  • TaffyDB – The JavaScript Database
  • Dave Brat’s Horrible, Terrible, No Good Problem with the First Amendment

    When a questioner said that health care is a human right, Brat countered the question with, “.”I don’t think y’all want the separation of church and state (…) In the west rights come from God”. In a press interview immediately following the event, Brat was asked how excited he was about the bill, he replied, “I’m a Calvinist,” he said. “I’m the frozen chosen. I’m an economist. So, it’s like—excitement? Whatever.” He also added, “I don’t think people get that excited on policy in general,”

    Chase added that it is the responsibility of the church, not the government to protect the poor, needy and vulnerable. “If a person needs help they should join a church.”

  • Home | Better

    Better is a privacy tool for Safari on iPhone, iPad, and Mac. It protects you from behavioural ads and companies that track you on the web by enforcing the principles of Ethical Design. You can download the Better apps from the App Store and Mac App Store.

  • A New Model | Mastery Transcript

    No Standardization of Content
    The performance areas, credit standards (rubrics, etc.) and credits are specific only to the individual crediting school, and will never be standardized across schools.

    No Grades
    Letter grading (or numerical equivalent) will not be used.

    Consistent Transcript Format
    Transcript has to be readable by college admission officers (once trained) in less than two minutes. Therefore, the transcript format has to be reasonably consistent across MTC schools.

  • 1wheel/awesome-interactive-journalism: A list of awesome interactive journalism projects.
Managing Rico Theta Videos on a Mac

Managing Rico Theta Videos on a Mac

The theta software sucks but the camera is decent #theta360 – Spherical Image – RICOH THETA

At one point you could mount a Ricoh Theta camera on a Mac like so . . .

Mass Storage: Press Wireless and Shutter. While continuing to hold these buttons, plug THETA into computer with USB cable. Camera will be powered off.

via this

It appears this no longer works on Macs running an updated OS. I never found one that worked . . . but supposedly it does on older version. I tried a number of things on various machines here but even diskutil list in terminal wasn’t seeing the Ricoh.

Molly even went to great lengths to call and talk to a person on a phone but he was less than no help. There seems to be a pretty basic assumption that you only want to take video in the camera and upload it via their app.

Much like the GoPro, it’s hard to tell what’s going on in the Ricoh via the device itself. It tells you it’s on, wi-fi is up etc. but storage remaining and a few other things that might matter are not apparent.

Get Your Video & Cleanse the Camera

To get the content to your phone and remove it from the camera, you’ll need to go into the Ricoh app settings on your phone and do this.

From there, you can do the transfer from your phone to your computer using your mechanism of choice.

Fix Adobe’s Annoying Photoshop PNG Transparency Clipping

Photoshop decided it was smarter than me and would override my decisions about exporting a PNG layer with transparencies. It’d clip it and mess up my careful alignment. You can see someone else suffering from this on the Adobe forums.

This could not stand.

If you don’t want to see the setup for the issue and just want the answer . . . take the paint brush set the opacity to 0 and make a slash across the top prior to export.

Weekly Web Harvest for 2017-04-30

  • itsnotmythrowaway1 comments on Senior Arizona Lawmaker: Teachers Should Expect To Work Second Jobs And Have Lower Salary Expectations

    Worse yet, many of those charters are deliberately manipulating the enrollment process to ensure they do not have to educate poor and underprivileged students.
    How does a “public” school do this? Let me give you an example. Many charter schools do not offer bus service. This means parents are required to drive their children to school in order to go to these charter schools. I sat in a meeting at one of these charter schools where the powers-that-be within the school were concerned that “bad” students and english language learning students (read: hispanic students) might still come to the school because they could bicycle or walk to school.
    The solution? The school set up a policy where students cannot walk or cycle to school, and they removed all bicycle parking. They did it under the claim that they were interested in student safety, and that riding or walking to school was un-safe. If you can’t drive your child to the school (because you’re working or otherwise occupied), your child can’t go to that school.

  • In the Dark – Futility Closet

    “This paper was undertaken in an attempt to shed light on some very mysterious problems. I fear I have done little more than show which lamps have cords too short to reach the outlets.”

  • Leaving Apple Inc.

    At the end of my senior year, I received an e-mail from a Software QA Manager at Apple (who followed my comments at the bottom of TechCrunch articles) inviting me for an on-site interview. Following an offer, I moved to the Bay Area to start my first post-undergrad job in Cupertino.

    –but not GitHub etc. . . . wonder if that’s bc recruiters are non-technical?

  • Pl@ntNet

    ID plants via app . . .

  • Shipwreck Diary of a Content Marketer – McSweeney’s Internet Tendency

    As far as I can tell, there are no other influencers on this island. If I am going to get rescued, I will have to create a truly incredible content marketing campaign.

  • inotify – get your file system supervised

Historical Will Annotation Continued: A WP API Experiment

The Judah Will Project (now with new URL!) has continued to grow as Ryan has been putting in serious work on the research and writing side of things. I have no choice but to step up my game and it’s been an interesting learning experience as it’s the first time I’ve tried anything sophisticated with WP providing the writing/data side of things while presenting that information somewhere else entirely. Headless?

So here’s a recap of changes since the last update.

More Obvious

I talked to Jim about the project a few days ago. It became clear to me that it wasn’t obvious that the names in the will transcription were clickable prior to actually clicking on one. I fixed that with a simple dashed underline. This was one of those times where I was trying to keep the visual elements minimal but ended up going too far.

I also threw in a modal popup for initial directions to make things more obvious. I just used this simple modal jquery plugin.

It immediately drove me crazy by popping up all the time. So I looked around and found a solution to set cookies which I’d never done before.

function setCookie(cname, cvalue, exdays) {
  var d = new Date();
  d.setTime(d.getTime() + (exdays*24*60*60*1000));
  var expires = "expires="+d.toUTCString();
  document.cookie = cname + "=" + cvalue + "; " + expires;

function getCookie(cname) {
  var name = cname + "=";
  var ca = document.cookie.split(';');
  for(var i=0; i<ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1);
    if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
  return "";

I also used a modal for the ever-growing family tree. When you have 12 kids in a generation, things get pretty wide.

Permanent URLs

Since this is a single page with data being loaded on the fly from the JSON API I had to build a way to track and reference the user interactions so people could send a link to the Family Tree page rather saying ‘go to this URL and then click on this …’

So I needed two things to happen – button clicks (events if we want to be fancy) need to write something to the URL (hash parameter) to reference and that URL had to be checked to see if there was anything there that it needed to know.

function hlPerson(person) {
  for (var i = 0; i < _people.length; i++) {
    var thePerson = _people[i].dataset.person;
    if (thePerson == person) {
        window.location.hash = thePerson;//sets the hash to the person variable
function urlHash(){
  if(window.location.hash) {
      var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
        return hash;
      // hash found
  } else {
      // No hash found
      return 'about'; //defaults to the about page if there's no hash given

I then figured out that hitting the back button didn’t work. That was irritating but fixable . . . I didn’t get the vanilla js version working the way I thought it should but StackOverflow provided a jQuery version.1

/lets you do the back and forward properly -- I failed on the regular js version 
$(window).on('hashchange', function() {

WordPress URLS

One of the thing I overlooked was that the links in the WordPress site that would be intuitive for Ryan to make . . . would bring the user out of the js front end and to the blog itself. Not good.2 I had a variety of options to fix this (including just redoing them by hand) but I decided since this was an exercise to learn more about js . . . I ought to see what I could do that.

I had a variety of variables to worry about. There were at least two URL patterns ( and to re-write and I needed to avoid messing up any non-Judah Will related URLs.

function urlFix(){
  var theLinks = document.getElementsByClassName('person');//get all the links in the wp post that have the class person
  console.log(theLinks);//just checking
  var pageSlug;
  for(var i=0; i<theLinks.length; i++) {
    if (! theLinks[i].getAttribute('data-person')) { //see if it already has the data attribute set, if not ...
      if (theLinks[i]"judahwill.rampages")>0){ //does the url have judahwill.rampages in it? if so . . . 
         pageSlug = theLinks[i].href.substring(30); 
      } else {
        pageSlug = theLinks[i].href.substring(36).slice(0, -1); //this is our other pattern
      theLinks[i].dataset.person = pageSlug;
      theLinks[i].setAttribute('href', '' + pageSlug);//set the new url


1 It could also be that I got it working and confused myself. The mix of Chrome caching js and a dev environment that occasionally kicks my links through to the real page leads to me getting confused just at times. There is likely a smart way around this . . .

2 And rather obvious in retrospect . . . but hey. I continue to learn.

Simple Interview Formatting Using Pseudo Elements

I had a quick question today about formatting an interview. That led to some quick experimentation in Code Pen and some simple uses of pseudo class selectors.1

When I first looked at the transcript I started to assign a class to each paragraph. That would work but was tedious. Given we were switching speakers at every paragraph I could be clever/lazy. nth-of-type will let me select odd and even elements. You can also be far fancier (every 5th etc.) but even/odd was all I needed.

p:nth-of-type(odd) {
      background-color: #efefef; /*background color for speaker c - set to white if you want it vanilla*/

p:nth-of-type(even) {
  background-color: pink;/*background color for speaker g - set to white if you want it vanilla*/

Given how pleasant that was, I figured I’d try a few more.

p::first-letter { 
  font-size: 2em;
  color: #424242;
  padding: 0 5px;
  margin-right: 10px;
  line-height: 1.2em;  

This CSS lets me grab the first letter (and the colon) of the paragraphs and make them a bit like a drop cap.2


I wondered if I could use the selectors in combination to alternately style the first letters drop caps. Turns out you can.

Anyway a fun 10 minutes or so that might be of interest to other wanderers out there.

See the Pen demo for clare – fancy version by Tom (@twwoodward) on CodePen.

1 Disregard my neapolitan-ice-cream-inspired color scheme.

2 I had to look up the word for ‘big letters in medieval manuscripts’ and didn’t actually read/follow the tips for drop caps at the URL linked here . . .

Contact us

Academic Learning Transformation Lab - ALT Lab
1000 Floyd Ave, Suite 4102 | Richmond, Virginia 23284 | 804-828-1651

Last updated: April 27, 2016

Virginia Commonwealth University

HTML tutorial