Tab areas are one of the most popular methods to showcase content. These are especially useful if you want to display a large amount of content in a limited space. This makes tab areas a very handy tool for website development for phones. In this article, we are going to take a look at how to create an Interactive Tabs Widget in WordPress using jQuery. We are going to use the jQuery UI Tab Widget and use it in our WordPress Widget. We have used it to create a widget in our Themes. It looks something like the image below –

This is from our theme ORO. In this widget, posts from different categories appear in different tabs of the widget. While, this is quite an advanced example, we are going to include simple text in each tab. So, let’s get started!

NOTE: This is a developer level article, so some terms may be difficult to comprehend.

Enqueue the required scripts in WordPress

Since the widget is a part of jQuery, we need to enqueue jQuery in our WordPress Theme or Plugin. Also, since tabs are a part of UI, we also need to include jQuery UI as well. Luckily, WordPress already has jQuery and jQuery UI included with it. We just have to enqueue it. Add the following code to your functions.php or wherever you have enqueued your scripts –

function my_theme_enqueue_scripts() {

  wp_enqueue_script('jquery-ui-tabs');

  wp_enqueue_script( 'my-custom-js', get_template_directory_uri() . '/js/custom.js', array( 'jquery' ) );

}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_scripts' )

In this snippet, we enqueue the jQuery UI for Tabs and a custom JS file to execute any JS code in the front-end. Note that we are enqueuing UI fot Tabs only. WordPress provides us the functionality to enqueue only the parts of jQuery UI that we require for our project. There are a number of jQuery UI widgets that can be enqueued and used in our projects. Take a look at this official documentation of wp_enqueue_script for more information.

We also include a custom.js file to execute any JS code at the front-end. Note that we have added jQuery as a dependency so that jQuery is loaded before custom.js in order to use jQuery in custom.js. Now, with the required JS files enqueued, let’s create a new widget.

Register a new widget

Now that the required scripts have been enqueued, we now need to create the widget in our Theme. Create a new file named tab-widget.php in the inc folder in the theme directory. This is where we would be registering our new widget.

Now go to functions.php and add the following code-

require get_template_directory() . '/inc/tab-widget.php';

This includes the newly created file in our theme. Now, go to tab-widget.php and add the following code-

<?php
// Register and load the widget
function my_tab_widget() {
    register_widget( 'my_tabs' );
}
add_action( 'widgets_init', 'my_tab_widget' );

// Creating the widget
class my_tabs extends WP_Widget {

    function __construct() {
        parent::__construct(

// Base ID of your widget
            'my_tabs',

// Widget name will appear in UI
            esc_html__('Tab Widget', 'my-theme'),

// Widget description
            array( 'description' => esc_html__( 'This is a Tabbed Text widget.', 'my-theme' ), )
        );
    }

// Creating widget front-end

    public function widget( $args, $instance ) {

        $title              = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
        for( $i = 1; $i <= 3; $i++ ) {
        	//${"tab_$i"}	= isset( $instance['category_' . $i] ) ? $instance['category_' . $i] : 0;
        	$tab_title[$i]	=	isset( $instance['tab_' . $i . '_title'] ) ? $instance['tab_' . $i . '_title'] : '';
        	$tab_text[$i]	=	isset( $instance['tab_' . $i . '_text'] ) ? $instance['tab_' . $i . '_text'] : '';
        }

            echo $args['before_widget'];
            if ( ! empty( $title ) )
                echo $args['before_title'] . $title . $args['after_title']; ?>
                
                <div id="tab-widget-wrapper-<?php echo $this->number ?>">
					<ul class="tab_titles">
					<?php
						for( $i = 1; $i <= 3; $i++ ) { 
							
					        if ( !empty( $tab_title[ $i ]) ) { ?>
					        	
				        		<li><a href="#tab_<?php echo $i ?>"><?php echo $tab_title[ $i ]; ?></a></li>
				        	<?php
				        	}
				        }
				        ?>
				    </ul>
				    <div class="tabs-slider"></div>
	
					<?php
	                for( $i = 1; $i <= 3; $i++ ) {
		                if ( !empty( $tab_text[ $i ] ) ) { ?>
			                <div id="tab_<?php echo $i ?>">
				                <p>
					                <?php
						                echo $tab_text[ $i ];
					                ?>
				                </p>
			                </div>
						<?php
	                	}
	                } ?>
                </div>
                
            <?php
    	   echo $args['after_widget'];
    	   
    	   $export	=	array(
	    	   "number"	=>	$this->number,
	    	   "align"	=>	$direction
    	   );
    	   wp_localize_script( 'my-custom-js', 'tab_widget_' . $this->number, $export );
    }

	// Widget Backend
    public function form( $instance ) {

        /* Set up some default widget settings. */
       $defaults = array(
           'title'              => esc_html__( 'Tab Widget', 'my-theme' ),
           'tab_1_title'           	=> '',
           'tab_2_title'           	=> '',
           'tab_3_title'           	=> '',
           'tab_1_text'           	=> '',
           'tab_2_text'           	=> '',
           'tab_3_text'           	=> '',
       );
       
       $instance = wp_parse_args( $instance, $defaults );
       ?>

        <p>
           <label for="<?php echo $this->get_field_id( 'title' ); ?>">
            <span><b><?php _e('Title', 'my-theme'); ?></b></span>
            <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
            </label>
        </p>
		
		
		<?php for( $i = 1; $i <= 3; $i++ ) { ?>
	        <p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'tab_' . $i . '_title' ) ); ?>">
					<h3><b><?php echo __('Tab ' . $i . ' Title', 'my-theme' ); ?></b></h3>
					<input type="text" id="<?php echo esc_attr( $this->get_field_id('tab_' . $i . '_title') ) ?>" name="<?php echo esc_attr( $this->get_field_name('tab_' . $i . '_title') ) ?>" class="widefat" value="<?php echo esc_attr( $instance['tab_' . $i . '_title'] ); ?>" />
				</label>
				
				<label for="<?php echo esc_attr( $this->get_field_id( 'tab_' . $i . '_text' ) ); ?>">
					<h3><b><?php echo __('Tab ' . $i . ' Content', 'my-theme' ); ?></b></h3>
					<textarea type="text" id="<?php echo esc_attr( $this->get_field_id('tab_' . $i . '_text') ) ?>" name="<?php echo esc_attr( $this->get_field_name('tab_' . $i . '_text') ) ?>" class="widefat" value="<?php echo esc_attr( $instance['tab_' . $i . '_text'] ); ?>"></textarea>
				</label>
			</p>
        <?php
		}
	}


    // Updating widget replacing old instances with new
    public function update( $new_instance, $old_instance ) {
        $instance = array();
        $instance['title']      	=   ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        
        for( $i = 1; $i <= 3; $i++ ) {
	        
	        $instance['tab_' . $i . '_title']	=   ( ! empty( $new_instance['tab_' . $i . '_title'] ) ) ? absint( $new_instance['tab_' . $i . '_title'] ) : '';
	        
	        $instance['tab_' . $i . '_text']	=   ( ! empty( $new_instance['tab_' . $i . '_text'] ) ) ? esc_html( $new_instance['tab_' . $i . '_text'] ) : '';
        
        }
        
        return $instance;
    }
}

This is the code we use to register a new widget. The class WP_Widget is extended to create a custom widget. Here, I have explained in detail about the functioning of the widget. The steps are similar to the one I mentioned in a previous article to create a Slider Widget. we just have to replace the slider code with our widget one. Let’s see how it works.

The class contains 4 functions – __construct(), widget(), form() and update(). The __construct() function is used to define the Title, Description, id and other parameters of a widget. For a detailed list of options, check out the official documentation of WP_Widget. The essential part is the id. It needs to be defined in order to register a widget.

Define the back-end of the widget

After registering the widget, it starts appearing in Appearance > Widgets. But there will be no options available in it. In order to show options, we have the form() function. In our widget, we need to define 7 fields, a title of the widget, 3 Tab Title fields and 3 Tab Content fields. We use Input fields for Titles and Textarea fields for Tab Content. Check out the following code used above –

// Widget Backend
    public function form( $instance ) {

        /* Set up some default widget settings. */
       $defaults = array(
           'title'              => esc_html__( 'Tab Widget', 'my-theme' ),
           'tab_1_title'           	=> '',
           'tab_2_title'           	=> '',
           'tab_3_title'           	=> '',
           'tab_1_text'           	=> '',
           'tab_2_text'           	=> '',
           'tab_3_text'           	=> '',
       );
       
       $instance = wp_parse_args( $instance, $defaults );
       ?>

        <p>
           <label for="<?php echo $this->get_field_id( 'title' ); ?>">
            <span><b><?php _e('Title', 'my-theme'); ?></b></span>
            <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
            </label>
        </p>
		
		
		<?php for( $i = 1; $i <= 3; $i++ ) { ?>
	        <p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'tab_' . $i . '_title' ) ); ?>">
					<h3><b><?php echo __('Tab ' . $i . ' Title', 'my-theme' ); ?></b></h3>
					<input type="text" id="<?php echo esc_attr( $this->get_field_id('tab_' . $i . '_title') ) ?>" name="<?php echo esc_attr( $this->get_field_name('tab_' . $i . '_title') ) ?>" class="widefat" value="<?php echo esc_attr( $instance['tab_' . $i . '_title'] ); ?>" />
				</label>
				
				<label for="<?php echo esc_attr( $this->get_field_id( 'tab_' . $i . '_text' ) ); ?>">
					<h3><b><?php echo __('Tab ' . $i . ' Content', 'my-theme' ); ?></b></h3>
					<textarea type="text" id="<?php echo esc_attr( $this->get_field_id('tab_' . $i . '_text') ) ?>" name="<?php echo esc_attr( $this->get_field_name('tab_' . $i . '_text') ) ?>" class="widefat" value="<?php echo esc_attr( $instance['tab_' . $i . '_text'] ); ?>"></textarea>
				</label>
			</p>
        <?php
		}
	}

Basically, we are defining fields inside a form. First, we register the IDs of the fields with their default values. Then, these values are parsed using wp_parse_args() to check if there are already values for these IDs. After that, we register the input field for widget title, and in a for loop an Input and a Textarea field for the tab titles and contents of the 3 tabs. Notice the appropriate id and name attributes. These are necessary to register these fields and save their values for use in the widget.

The Front-end of the Widget

Now, we are able to see the fields in Widgets Page (Appearance > Widgets). Now, we need to make the front-end of the widget. For that, we use the widget() function. Before we discuss further, take a look at the official jQuery UI documentation of the Tab Widget. If you observe the source code of the tab widget, you’ll notice the titles are displayed as links in a list in the HTML markup and the content is displayed as 3 separate divs. The titles are linked to the content divs using unique IDs. Take a look at the widget() function we used above –

//Widget Front-end
public function widget( $args, $instance ) {

        $title              = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
        for( $i = 1; $i <= 3; $i++ ) {
        	$tab_title[$i]	=	isset( $instance['tab_' . $i . '_title'] ) ? $instance['tab_' . $i . '_title'] : '';
        	$tab_text[$i]	=	isset( $instance['tab_' . $i . '_text'] ) ? $instance['tab_' . $i . '_text'] : '';
        }

            echo $args['before_widget'];
            if ( ! empty( $title ) )
                echo $args['before_title'] . $title . $args['after_title']; ?>
                
                <div id="tab-widget-wrapper-<?php echo $this->number ?>">
					<ul class="tab_titles">
					<?php
						for( $i = 1; $i <= 3; $i++ ) { 
							
					        if ( !empty( $tab_title[ $i ]) ) { ?>
					        	
				        		<li><a href="#tab_<?php echo $i ?>"><?php echo $tab_title[ $i ]; ?></a></li>
				        	<?php
				        	}
				        }
				        ?>
				    </ul>
				    <div class="tabs-slider"></div>
	
					<?php
	                for( $i = 1; $i <= 3; $i++ ) {
		                if ( !empty( $tab_text[ $i ] ) ) { ?>
			                <div id="tab_<?php echo $i ?>">
				                <p>
					                <?php
						                echo $tab_text[ $i ];
					                ?>
				                </p>
			                </div>
						<?php
	                	}
	                } ?>
                </div>
                
            <?php
    	   echo $args['after_widget'];
    	   
    	   $export	=	array(
	    	   "number"	=>	$this->number
    	   );
    	   wp_localize_script( 'my-custom-js', 'tab_widget_' . $this->number, $export );
    }

In this function, we set the instances of widget title in $title, tab title in $tab_title array, the tab titles in the $text array and the tab content in $tab_text array. Then, we set up the Markup of the Widget. In the wrapper div, we add the ID for each widget instance using the $this->number property. The number property will be different for every instance of the widget.

Then, using the for loop, we create a list Markup with the tab titles as anchor tag in each list item. Each anchor tag is redirecting to its corresponding content div which is created after the title. Notice that between the Tab titles and Tab Contents, we insert a div. This div will be used to create an arrow to the active tab.

After the markup has been created, we need to use some data from the widget in the front-end JS. For that, we use the wp_localize_script function. For the details about this function, you can check out its official documentation. We localize the number property for every instance of the widget. It will be used in the JS which we’ll see next. The data will be available in the window object in front-end JS file (custom.js in our case).

Saving the widgets

Before we get to the JS, there is a thing we still need to do. At the current stage, the Back-end form is set up but we cannot save its value. To save the values of the fields in the back-end form, we make use of the update() function. Check out the code below –

 // Updating widget replacing old instances with new
    public function update( $new_instance, $old_instance ) {
        $instance = array();
        $instance['title']      	=   ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        
        for( $i = 1; $i <= 3; $i++ ) {
	        
	        $instance['tab_' . $i . '_title']	=   ( ! empty( $new_instance['tab_' . $i . '_title'] ) ) ? absint( $new_instance['tab_' . $i . '_title'] ) : '';
	        
	        $instance['tab_' . $i . '_text']	=   ( ! empty( $new_instance['tab_' . $i . '_text'] ) ) ? esc_html( $new_instance['tab_' . $i . '_text'] ) : '';
        
        }
        
        return $instance;
    }
}

The update() function accepts 2 arguments – the old instance and the new instance of each field. In this function, we compare the old instance and new instance of each input field in the form() function and if the values are different it saves the new instance in the database.

Summarising the whole thing, we use the form() function to create the back-end form from which we provide data to the widget. The input values are saved using the update() function where new instance and old instance of input fields in form(), if the new instance differs from new one, the new value is saved in the database. Then using these saved values, we render the front-end in the widget() function.

If you performed the above steps correctly, on inserting widget title, tab title and tab content, these corresponding values will be rendered at the front-end in the widget. Note the id attribute of every widget instance would be different depending on the $number property of the widget.

Currently, it’s just simple HTML with no Tab functionality. In order to render the tabs, we need to head over to JS.

Rendering Tabs in Front-end JS

Notice that we enqueued custom.js file in the first step. And later on, we localized the $number property for every instance of the widget. This $number property will be available in the window object in the tab_widget_{number} property. For example, if $number is 1 for some instance of widget, this will be available in the tab_widget_1 property in window object. Adding this instance identifier is necessary as the JS rendering the tabs needs to identify each widget instance separately. Now, add the following code to custom.js –

// Tab Widget
	var tabWidgets = [];
    
    if ( 'undefined' !== tabWidgets && tabWidgets.length > 0 ) {
	    console.log(tabWidgets)
	    for (tabWidget in window) {
		    if ( tabWidget.indexOf("tab_widget") != -1 ) {
			    tabWidgets.push( window[tabWidget] );
		    }
	    };
	    tabWidgets.forEach( function( item ) {
		    
		    var widget 			=	jQuery("#tab-category-wrapper-" + item.number),
		    	containerLeft	=	widget.find('ul').offset().left,
	    		currentArrow	=	widget.find('.tabs-slider'),
	    		arrowWidth		=	currentArrow.width();
	    		
		    widget.tabs();
		});
	}

This is the code we use to render Tabs using jQuery UI. Notice that we used tabs() to render jQuery tabs. For more info, check out the official documentation of jQuery UI tabs widget. This is the basic implementation of the tabs widget and will not look as charming as you would expect. To add a little interaction to it, replace widget.tabs()with the following code-

 widget.tabs({
  beforeActivate: function( event, ui ) {
    jQuery(ui.oldPanel[0]).fadeOut()
    jQuery(ui.newPanel[0]).fadeIn()
  }
});

This would add a nice faceIn() and fadeOut() effect to the transition.

Now, you can add the styling as per your requirement. The styling is not covered in this article as it is the personal choice of every individual. As an example, you can take a look at the tab widgets in our themes ORO and Olively. It has been integrated nicely to work coherently with the theme.

So, that’s it! We now have a WordPress Tab Widget with 3 Tabs. This is a simple example and you enhance it by increasing the number of Tabs or adding custom functionalities in the Tab Content like recent posts, images, categories, etc. This widget can be treated as a boilerplate to a Custom Tab WordPress Widget. Hope you learned something from this article.

Last Update - June 4th, 2021

Leave a Reply

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