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:

  1. To make sure that the core code was a bit more readable
  2. 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.