For a recent project, I needed to introduce functionality that added a widgetized area to the header of the blog, but only allowed a single instance of a specific widget to be added: the “Search” widget.
Since the dashboard for the widgetized areas are driven the by jQuery and jQuery UI libraries, the implementation is almost completely written in JavaScript, and although I know there may be some criticisms about only allowing a certain type of widget in a widgetized area, here’s how you can enforce adding a single widget in WordPress.
Adding a Single Widget in WordPress
As I mentioned, for this particular project, I needed to:
- Introduce a widgetized area for the header to support the widget
- Only support the “Search” widget in said header area
The requirements are straight forward enough. Here’s the break down of how I did it:
1. Introduce The Widgetized Area
First, I added the following code to functions.php
.
register_sidebar( array( 'name' => __( 'Header Search', 'standard' ), 'id' => 'sidebar-0', 'description' => __( 'This area is designated for adding the search widget to the header area.', 'standard' ), 'before_widget' => '<div id="%1$s" class="widget %2$s">', 'after_widget' => '</div>', 'before_title' => '<h3 class="widget-title">', 'after_title' => '</h3>' ) );
Notice that it includes a description that the area is intended specifically for the search widget. I also named this sidebar-0
so that it’d appear above all other widgetized areas in the dashboard.
Usually, I like my widgetized areas to flow in the same order that they appear on the page in order to help to provide some kind of similarity to the front end for the user.
That is, I wouldn’t add this to, say, the middle or the bottom of the sidebar area in the dashboard since it belongs in the, y’know, header.
2. Render Widgetized Area
This, like any other sidebar or widgetized area in WordPress, was a matter of making the following call in the header.php
template:
<?php if ( is_active_sidebar( 'sidebar-0' ) ) { ?> <div id="header-search" class="span4"> <?php dynamic_sidebar( 'sidebar-0' ); ?> </div><!-- /#header-search --> <?php } // endif ?>
Simply put, I first check to see if the widgetized area is active (that is, contains any widgets). If it does, then I render a div
element with the widgetized area.
Notice that I do have a class name of span4
. In the project, I’m using a grid so I did have to make some conditional statements elsewhere in the code in order to make sure that the header region is either span12
or span8
depending on if this area is active; however, that’s outside the scope of this post.
3. Enforce The “Search” Widget
Because the dashboard’s widgetized areas are all using the jQuery UI Sortable library, it makes it relatively easy to hook into the update
function in order to check, enforce, and manipulate the DOM based on whatever restrictions you’d like.
Case in point: Since I named my area sidebar-0
, I was able to introduce the following:
$('#sidebar-0').sortable({ update: function( evt, ui ) { // More to come... } // end update });
Whenever something happens within the sidebar-0
area, the update
function will fire. This is where we’re able to manipulate the elements that are dropped within the container.
The logic that I implemented for this solution was as follows:
- Look through each of the child elements of the container
- If the given child element is not the widgetized area’s description nor is it the “Search” widget, then remove it
- Lastly, if there is more than one “Search” widget, remove it, too
To do this, I added two really similar helper functions for two reasons:
- To make sure that the core code was a bit more readable
- To make sure that I was able to abstract some of the conditional logic into their own functions for future work
I introduced a function called isSearchWidget
and isWidgetAreaDescription
. Each of them are as follows:
/** * Determines if the specified element is the widget area's description. * * @param object $ A reference to jQuery * @param element $elem The element used to evaluate * @return boolean True if it's the widgetized area's description; otherwise, false */ function isWidgetAreaDescription( $, $elem ) { return $elem.hasClass('sidebar-description'); } // end isWidgetAreaDescription /** * Determines if the specified element is a search widget * * @param object $ A reference to jQuery * @param element div The div element to evaluate * @return boolean True if the element is the search widget; otherwise, false. */ function isSearchWidget( $, div ) { return 1 === $(div).children('.widget-inside').children('form').length && 'search' === $(div).children('.widget-inside').children('form').children('.id_base').val(); } // end isSearchWidget
After that, I implemented the core logic as outlined above:
$('#sidebar-0').sortable({ update: function( evt, ui ) { // Iterate through each of the child elements... $(this).children().each(function() { // If it's not 'Search' or the area description, remove it. if ( ! ( isSearchWidget( $, $(this) ) || isWidgetAreaDescription( $, $(this) ) ) ) { $(this).remove(); } // end if }); // And if there are more than one search boxes, remove all but one if ( 2 !== $(this).children().length ) { $(this).children(':last').remove(); } // end if } // end update });
Done and done.
Notes on UX
As far as the user experience and/or the user interface is concerned, I don’t think that simply providing a description in the widgetized area is enough because I’m not convinced that people read these.
To that end, I think that adding a WordPress notification message based on the user’s action would be more effective, but, again, that’s outside the scope of this article.
Additionally, there are likely other considerations that I’m missing so I’m all ears as to suggestions, recommendations, thoughts, and even code reviews on this particular implementation.
At any rate – it works, it’s readable and maintainable (at least for a 1.0), and meets the requirements of the project.
Leave a Reply
You must be logged in to post a comment.