Styling the first word of a block title (preprocessing edition)
Background: Yesterday, I posted about using PHP to wrap a span around the first word in a block title. I used a block template with the note that I'd be interested in a better method, like a preprocessing solution. I got extremely helpful comments that both pointed out how to do it with preprocessing, and showed a more efficient way to do the code itself. Since it's substantially different from my original post, I've hidden that one from the main blog views, and I'm posting the new and improved method here. If you'd like, you can view the original post and its comments, but the method below is much better.
I'm working on a theme that that needs the first word in all sidebar block titles bolded. The most obvious way to do this is with jQuery (here's a comment that shows a couple ways to do it). Doing it with PHP isn't much tougher, though, and it works regardless of whether JavaScript is enabled, with no chance of a brief flicker before the words are styled.
I'm going into some detail below to show the few lines of code work. Feel free to skip to the Result h2 if you just want the code.
The Process
If you take a quick look at block.tpl.php in your base theme (we're not going to alter this file at all; this is just for reference), you'll see a <?php print $block->subject; ?>. In template.php, you can preprocess this variable to make alterations before block.tpl.php serves it up. This is done using hook_preprocess_block(). In your function, you'll replace hook with your theme name (and MYTHEME with your actual theme name, of course):
function MYTHEME_preprocess_block(&$variables) {
}
Ordinarily, you would reference the block variables with $variables['block']. As a shortcut, you can define a shorter variable for this, so you don't have to type $variables['block']; every time.
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
}
So far we've set it up, but haven't done anything yet. First, we want to break apart the words in the title. The PHP function to use here is explode, which breaks things into their component parts; to figure out where to split up the parts, it uses a separator that you define. Since we're breaking on a word boundary, I'm going to use a space. If you replaced ' ' with ',' below, it would use commas as the dividing line (so "value1,value2,value3" would get split into "value 1" "value 2" "value 3"). If you put '|' it would look for pipes. You get the idea.
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
$words = explode(' ', $block->subject);
}
$words now contains an array with each word from the block title. An array isn't useful to us, as you'll find if you try to store it as $block->subject (which we're going to do with the final product to get it to print in the block title; remember, $block->subject is what's called in the block.tpl.php). If you go ahead and set $block->subject to $words, you'll find "Array" printing as the title of all your blocks.
Instead, we want to use PHP's implode function to collapse this back into a string. Implode sticks a glue string in between each value of the array. Our glue will be a space; since we exploded based on where the space occurs, it makes sense to stick things back together with a space.
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
$words = explode(' ', $block->subject);
$block->subject = implode(' ', $words);
}
Now you'll see that your block titles are printing the way they originally did. Now that we've seen how explode and implode work, the next step is wrapping that span around the first element. Before the implosion, we can modify the first element in the array by using $words[0]:
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
$words = explode(' ', $block->subject);
$words[0] = '<span>' . $words[0] . '</span>';
$block->subject = implode(' ', $words);
}
Finally, we're going to add a check to make sure that the block does have a title. If not, you can wind up with spans on blocks with no title set.
The Result
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
if ($block->subject) {
$words = explode(' ', $block->subject);
$words[0] = '<span>' . $words[0] . '</span>';
$block->subject = implode(' ', $words);
}
}
Without touching block.tpl.php, we've changed the output, so all block titles will look like <h2 class="block-title"><span>FirstWord</span> SecondWord ThirdWord</h2> instead of <h2 class="block-title">FirstWord SecondWord ThirdWord</h2> We can target the new span with some CSS:
h2.block-title span {
font-weight: bold;
}
You can see that the preprocessing function above is shorter and more elegant than the one I originally posted (thanks, commenters).
Bonus: Region-based Modifications
As Peter pointed out in his comment, you can take this further by taking advantage of other block variables. For example, in my use case, I only need to style the first word of block titles in the second sidebar region. I could add a span to each block title on the entire site and use CSS to target just the ones I wanted to style, like this:
.region-sidebar-second h2.block-title span {
font-weight: bold;
}
But it'd be more efficient to adjust my function, so I only ended up with spans in the regions where I wanted them. The function below is modified to take advantage of $block->region:
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
if ($block->subject && $block->region == "sidebar_second") {
$words = explode(' ', $block->subject);
$words[0] = '<span>' . $words[0] . '</span>';
$block->subject = implode(' ', $words);
}
}
If you want to target two different regions, it's a little tweak:
function MYTHEME_preprocess_block(&$variables) {
$block = $variables['block'];
if ($block->subject && ($block->region == "sidebar_second" || $block->region == "content_top")) {
$words = explode(' ', $block->subject);
$words[0] = '<span>' . $words[0] . '</span>';
$block->subject = implode(' ', $words);
}
}
Now I can remove the unnecessary .region-sidebar-second from my CSS. Voilà, a bolded first word in each block title, but only in the regions I want.
Housekeeping
If you want to keep your template.php file a little cleaner, you can move this code snippet into preprocess/preprocess-block.inc. In Omega, you need to tack alpha_ onto the function name; this may not be necessary in other themes. Here's the resulting preprocess-block.inc file.
<?php
function MYTHEME_alpha_preprocess_block(&$variables) {
$block = $variables['block'];
if ($block->subject && ($block->region == "sidebar_first" || $block->region == "sidebar_second")) {
$words = explode(' ', $block->subject);
$words[0] = '<span>' . $words[0] . '</span>';
$block->subject = implode(' ', $words);
}
}
Thanks go to everyone who replied to my original post, especially Peter and kscheirer. This is a neater solution with extra power, and I hope the writeup is useful for others who are trying to do the same thing.