I use the Genesis framework on every project because of its speed, security, and ease of customization. There are hundreds of Genesis hooks and filters available that allow you to customize just about anything that you need. On rare occasions, though, I have needed to create my own hook(s) as a workaround.
A great example of needing a workaround is when trying to insert HTML between a container and its immediate descendant .wrap
element. By default, Genesis adds a structural wrap to the header, menu-primary, menu-secondary, footer-widgets, and footer elements. I often add it to the .site-inner
container as well to help with styling. But sometimes you may need to insert HTML between the structural wrap and its parent container, like when you want a full width page header. There are a few ways to do this, but the cleanest way that I’ve found is to add your own hook. Here’s how:
<?php | |
// Prefixing is recommended if you are not using a namespace. | |
// namespace TimJensen\GenesisStarter\Setup; | |
/** | |
* Adds hooks immediately before and after Genesis structural wraps. | |
* | |
* @version 1.1.0 | |
* | |
* @return void | |
*/ | |
function add_hooks_outside_structural_wraps() { | |
$structural_wraps = array( | |
'header', | |
'menu-primary', | |
'menu-secondary', | |
'site-inner', | |
'footer-widgets', | |
'footer' | |
); | |
foreach ( $structural_wraps as $context ) { | |
/** | |
* Inserts an action hook before the opening div and after the closing div for the structural wraps. | |
* | |
* @param string $output HTML markup for opening or closing the structural wrap. | |
* @param string $original_output Either 'open' or 'close'. | |
* | |
* @return string | |
*/ | |
add_filter( "genesis_structural_wrap-{$context}", function ( $output, $original_output ) use ( $context ) { | |
$position = ( 'open' === $original_output ) ? 'before' : 'after'; | |
ob_start(); | |
do_action( "{$position}_genesis_{$context}_wrap" ); | |
if ( 'open' === $original_output ) { | |
return ob_get_clean() . $output; | |
} else { | |
return $output . ob_get_clean(); | |
} | |
}, 10, 2 ); | |
} | |
} | |
add_hooks_outside_structural_wraps(); |
Here we have added three twelve new hooks:
And we can use these just like any other WordPress hook. So to accomplish the full width page header you could do something like this:
<?php | |
add_action( 'before_genesis_site-inner_wrap', 'do_full_width_page_header' ); | |
/** | |
* Adds a div with a background image before the site-inner wrap. | |
*/ | |
function do_full_width_page_header() { | |
printf( '<div class="full-width-page-header" style="background: url(\'%s\');"></div>', | |
wp_get_attachment_image_url( 2623, 'full' ) | |
); | |
} |
Simple and clean, right?
Damien Carbery says
Are there any benefits of this method instead of using add_filter()?
Tim Jensen says
Hi Damien, thanks for your comment! Do you mean why not do something like
instead of generating the filter tag dynamically? Yes, you can definitely do it that way. The way I presented it in this post helps to keep the code DRY (don’t repeat yourself), assuming that you want to reuse it. But if you intent to add a hook before only one of the structural wraps, then you can forego the
foreach
loop.Damien Carbery says
Yes.
I think that your functions.php code could be written as:
`add_filter( ‘genesis_structural_wrap-header’, ‘do_full_width_page_header, 10, 2 );
function do_full_width_page_header( $output, $original_output ) {
return sprintf( ”,
wp_get_attachment_image_url( 2623, ‘full’ )
) . $output;
}`
I don’t think it is any less DRY than using add_action(), though, as a self taught programmer (I studied Electronic Engineering in college many many years ago) I am not really familiar with such good practices.
I do find add_action() code easier to write because they rarely have parameters to remember to declare and to return.
Damien Carbery says
My code got trashed upon submission.
Here is what it should look like:
https://gist.github.com/damiencarbery/0aeae4bd9fcae40bb81902b67ab68952#file-add-structural-wraps-genesis-php
Tim Jensen says
Exactly. You can do it that way, but you would need to add the conditional to make sure you insert your additional content only after the wrap
open
, otherwise it will be inserted twice: once before the wrap opens, and once before it closes. The code you suggested above omits that check, which supports the idea that it’s more robust and less repetitive to create your own hook.Also, with creating your own hook you can then give priorities to whatever callback functions you want to execute on that hook, making it simple and easy to reorder the content as needed.
Damien Carbery says
Ah, I see your DRY point now – especially that the filter may not provide ‘open’ or ‘closed’ to the filter function (because $output could be something other than ‘open’ or ‘closed’). Thanks for the explanation.
Next I’ll try understand the get_field() replacement function from your other post – once code goes recursive it takes longer to ‘get.’
Lee Anthony says
Nice one Tim. This should be in Genesis.
Tim Jensen says
Thanks, Lee. Several months ago I inquired about whether hooks like these would be added to Genesis, and impression I got was probably not. That’s actually what got me thinking about finding a way to do it. I haven’t needed to use this code very often, but there have been a few times where it made things much easier and cleaner.
Lee Anthony says
https://github.com/seothemes/genesis-starter/commit/cff81334fa59c687c899b6d6d7ba879a650d9123
Tim Jensen says
I see you’re grabbing all the structural wraps using
get_theme_support()
. That’s exactly how I normally implement this function; in this blog post, though, I chose to explicitly provide an array of contexts for clarity.https://github.com/seothemes/genesis-starter/commit/cff81334fa59c687c899b6d6d7ba879a650d9123#diff-31998374215054ca1b24b25e3d74eb5dR332
Rob says
Hey man – thanks for this – this is exactly what I needed for my project. I’ve got one issue though. I cannot get the hooks to fire for before_genesis_site-inner_wrap. What am I missing?
https://gist.github.com/roborracle/e92b57fe86fcbc4cb8e8b32384c4d96d
Any thoughts/feedback would be most welcome.
To clarify one thing – it DOES work for the header and the footer, but that’s it.
Tim Jensen says
Hey Rob, are you certain that your theme has the structural wrap for site-inner?
One other thing to check is that the conditional is evaluating to true where you want it to.
Rob says
Hey Tim – so evidently, I was wrong in my last comment – please feel free to delete it. I had to explicity add support for structural wraps. Once I did that, it worked like a charm. Thanks again for this. I’m going to keep this handy as we do a LOT of Studiopress/template sites with Genesis.
Jesse says
Any idea on why this function would be creating a 404?
`https://sitename.test/wp-content/themes/themename/%EF%85%80 404` appears in the console. When I remove the function, the 404 error is gone. Kinda stumped. Cheers!
Jesse says
Sorted it out! If you have removed a structural wrap from your theme, and use the
$wraps = get_theme_support().
approach, you will get a 404 for any wraps you have removed. Not sure if there’s a way around that, but I moved to listing the wraps instead and that solved it.Tim Jensen says
Oh, interesting. Thanks for reporting your solution!
Ramanand mehta says
wow really amazing article sir, Thank you. I want to ask something.
If I want to add an HTML code just before footer widgets, how can I do that? can you please share PHP code?
shabdshiksha says
Great share Tim! I use Genesis Framework so I benefited from reading your blog… thanks.