The very broad answer for why this doesn't work is that the internet is stateless. Basically each request for a page is a separate and unique instance from other page requests.
Example
Let's say post-1 is in category-1 and category-2.
When Wordpress loads post-1 on single.php
it has no way of knowing how the user arrived at the page. It only knows that both category-1 and category-2 are associated with the post.
It is not possible to get the "current category" because it is no longer in a category.
This behavior also creates confusion with breadcrumbs in Wordpress. A user's breadcrumb path might look like this.
Step 1. Home
Step 2. Home->category-1
Step 3. Home->post-1
The expected behavior is often for the last step to look like this.
Step 3. Home->category-1>post-1
But if that worked, then this would also work.
Step 3. Home->category-2->post-1
And that would be bad for search engines because now the same post has two different URLs.
However
If you really want to show the category that the user came from you could try something like this. Remember, the internet is stateless so you need a way to pass information from one page to another.
Category Template
On category.php
you can alter your permalink to the single post and include a query string parameter in the form of ?category=id
. That might look something like this.
<?php
$cat_obj = get_category( get_query_var( 'cat' ) );
$cat_id = ( $cat_obj ? $cat_id = $cat_obj->term_id : $cat_id = '' );
?>
<a href="<?php echo get_permalink() . '?c=' . $cat_id ?>"><?php the_title(); ?></a>
Your permalink should now look something like this where ?c=19
is appended to the normal permalink.
http://www.example.com/post-name/?c=19
Single Template
On single.php you need to grab the category ID from the query string, get the name of the category and then display on the page. The code for that might look like this.
You'll want to check if the query string parameter is set. If it is, use esc_html()
to sanitize it for security, then check to make sure it's numeric and not something else like "blah".
Once done with the validation, get the category name and output on the page.
if ( isset( $_GET['c'] ) ) {
$cat_id = (integer) esc_html( $_GET['c'] );
if ( is_numeric( $cat_id ) ) {
$cat_obj = get_category($cat_id);
$cat_name = $cat_obj->name;
echo '<span>' . $cat_name . '</span>';
}
}
Caveats
If the query string parameter does not exist no category will be displayed. There are many ways that a user might arrive at a single post page and this only handles a very specific use-case.
This may also negatively impact SEO if the search engine perceives there to be multiple unique URLs to the exact same content.