Skip to main content

An horizontal sidebar

Last year I was working on a way to get a horizontal sidebar. I worked out a PHP only solution and added a nice selectbox to the widget admin page. Obviously you can just add the amount of sidebar to the widget area and do the magic in your template. I also going to show how you can just use jQuery Masonry to get something simular. This because Twenty Thirteen is using this.

All the code samples I’m showing is a part of a bigger plugin. I’m just handeling the parts what is good to show. The plugin can be find on GitHub: https://github.com/WPUnknown/Horizontal-sidebar/

Registering a horizontal sidebar

Registering a sidebar will be done by calling the function horizontal_sidebar_register. The first argument is the sidebar id and the second an int or an array with the column possibilities. The first value will be the default one. The order will be numeric but that is something that is going to be fix later.

add_action( 'widgets_init', 'horizontal_sidebar_example', 100 );

function horizontal_sidebar_example() {
	horizontal_sidebar_register( 'sidebar-id', array( 4, 2 ) );
}

Widget Admin

The first thing is adding the selectbox. It’s not easy to add something as a sidebar setting and you can call this a hack. I will using the filter “dynamic_sidebar_params”. This filter is used for the sidebar arguments for every widget. In this case I’m only going to need to change the first widget for every sidebar.

In the code below you will see this back. The high priority is just to be sure. I check if the sidebar is a horizontal one, if we already added the selectbox and if we need to add it. Saving is quite easy to do with jQuery on change and an ajax call. For the exact code look at the GitHub repository.

add_filter( 'dynamic_sidebar_params', array( $this, 'add_selectbox' ), 10000 );

public function add_selectbox( $params ) {
	global $wp_registered_sidebars;

	$sidebar_id = $params[0]['id'];

	if( ! isset( $this->sidebars[ $sidebar_id ] ) )
		return $params;

	$screen = get_current_screen();

	if( 'widgets' == $screen->base ) {
		if( ! isset( $this->dropdown_showed[ $sidebar_id ] ) && count( $this->sidebars[ $sidebar_id ] ) > 1 ) {
			echo $this->show_selectbox( $sidebar_id, $this->sidebars[ $sidebar_id ] );

			$this->dropdown_showed[ $sidebar_id ] = true;
		}
	}

	return $params;
}

PHP solution

So all the code is written now to register a horizontal sidebar and saving how many columns it has. Now we need to deal with the front-end. There is a counter property that stores the current widget position per sidebar. It can easily be one counter but I find this cleaner. The only thing this code do is adding a class to the last widget per row and a clearfix class after that widget. The CSS to make it clear needs to be added by the theme. The plugin can also contain it but personally I rather have the theme dealing with it.

There is one filter for this solution called ‘horizontal_sidebar_classes’. I use this to return a specific class that the theme uses. In case of 3 widgets per row and using Twitter Bootstrap the class returned would be span4.

class Horizontal_Sidebar_Default {
	public static $end_row = '<div class="clearfix"></div>';
	private $counters = array();

	function __construct() {
		if( ! is_admin() )
			add_filter( 'dynamic_sidebar_params', array( $this, '_sidebar_params' ), 10000 );
	}

	function _sidebar_params( $params ) {
		global $horizontal_sidebar;

		$sidebar_id = $params[0]['id'];
		$sidebars   = $horizontal_sidebar->registered_sidebars();

		if( ! in_array( $sidebar_id, $sidebars ) )
			return $params;

		if( ! isset( $this->counters[ $sidebar_id ] ) )
			$this->counters[ $sidebar_id ] = 1;

		$amount_columns = $horizontal_sidebar->get_columns_for_sidebar( $sidebar_id );

		$additional_classes = trim( apply_filters( 'horizontal_sidebar_classes', '', $amount_columns, $sidebar_id ) );

		if( $this->counters[ $sidebar_id ] % $amount_columns == 0 ) {
			$additional_classes .= ' last';
			$params[0]['after_widget']  = $params[0]['after_widget'] . self::$end_row;
		}

		if( $additional_classes )
			$params[0]['before_widget'] = preg_replace( '/class="/', "class=\"" . $additional_classes . " ", $params[0]['before_widget'], 1 );

		$this->counters[ $sidebar_id ]++;

		return $params;
	}
}

jQuery Masonry solution

This solution is probably best to have it in the theme since there are a lot of specifics. Still it’s cool that the user can select how many widgets per row he want. This time we need to enqueue a script and there is a check to be sure a sidebar needs to load the script. And when the script is enqueued we will output the jQuery Masonry code.

As told this solution isn’t that clean and has four filters and needs at least two of them. The selector and item selector need to be passed for knowing exactly about which sidebar we are talking. The item selector probably can be guessed but better save then sorry I guess. The width can be passed. By default it’s fluid and the item width changes on the total width it has. What can give weird results when the total width is really small.

<?php

class Horizontal_Sidebar_Masonry {

	function __construct() {
		add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
		add_action( 'wp_footer', array( $this, 'wp_inline_scripts' ) );
	}

	function wp_enqueue_scripts() {
		global $horizontal_sidebar;

		$sidebars = $horizontal_sidebar->registered_sidebars();

		foreach( $sidebars as $sidebar_id ) {
			if ( is_active_sidebar( $sidebar_id ) ) {
				wp_enqueue_script( 'jquery-masonry' );
				break;
			}
		}
	}

	function wp_inline_scripts() {
		global $horizontal_sidebar;

		if( wp_script_is( 'jquery_masonry', 'done' ) ) {
			$sidebars = $horizontal_sidebar->registered_sidebars();
			?>

			<script type="text/javascript">
				( function( $ ) {
					if ( $.isFunction( $.fn.masonry ) ) {
						<?php
						foreach( $sidebars as $sidebar_id ) {
							$amount_columns = $horizontal_sidebar->get_columns_for_sidebar( $sidebar_id );
							$selector       = apply_filters( 'horizontal_sidebar_masonry_selector', false, $sidebar_id );
							$item_selector  = apply_filters( 'horizontal_sidebar_masonry_item_selector', false, $sidebar_id );
							$width          = apply_filters( 'horizontal_sidebar_masonry_width', 0, $amount_columns, $sidebar_id );
							$gutter_width   = apply_filters( 'horizontal_sidebar_masonry_gutter_width', 20, $amount_columns, $sidebar_id );

							if( ! $width )
								$width = 'function( containerWidth ) { return containerWidth / ' . $amount_columns . '; }';

							if( ! $item_selector || ! $item_selector )
								continue;
						?>

						var columnWidth = <?php echo $width; ?>;

						$( '<?php echo $selector; ?>' ).masonry( {
							itemSelector: '<?php echo $item_selector; ?>',
							columnWidth:  columnWidth,
							gutterWidth:  <?php echo $gutter_width; ?>
						} );

						<?php } ?>
					}
				} )( jQuery );
			</script>

			<?php

		}
	}
}

Conclusion

Both solution are great. I personally love a grid so my personally preferences would be the PHP one instead of Masonry. A part of it is that Masonry does magic in the placement of it. So I can see why some users would looking weird to it. That said the Masonry solution does reduce the total height of a sidebar. Since the gap betweens widgets in de same column would be the same and don’t depend on the other columns. So the best solution really depends on the content of the widgets and what looks nicer. Maybe updating the code to select per sidebar what support it needs can make sense to have full control when to use what.

So please let me know what you think about https://github.com/WPUnknown/Horizontal-sidebar/ in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.