10

I'm trying to add a div to a widget's content in my dynamic sidebar.

Here is the register code;

register_sidebar(array(
        'name' => "Sidebar1",
        'id' => 'home-sidebar-1',
        'before_widget' => '<div class="sidebar-box">',
        'after_widget' => '</div>',
        'before_title' => '<div class="title">',
        'after_title' => '</div>',
    ));

But this code causes like this;

<div class="sidebar-box">
    <div class="title">{WIDGET TITLE}</div>
     {WIDGET CONTENT}
</div>

Here is what I am trying to do;

<div class="sidebar-box">
    <div class="title">{WIDGET TITLE}</div>
    <div class="content">
           {WIDGET CONTENT}
    </div> 
</div>

How it can be done?

7 Answers 7

19

In addition to Toscho's answer here's what you need for a robust solution:

// if no title then add widget content wrapper to before widget
add_filter( 'dynamic_sidebar_params', 'check_sidebar_params' );
function check_sidebar_params( $params ) {
    global $wp_registered_widgets;

    $settings_getter = $wp_registered_widgets[ $params[0]['widget_id'] ]['callback'][0];
    $settings = $settings_getter->get_settings();
    $settings = $settings[ $params[1]['number'] ];

    if ( $params[0][ 'after_widget' ] == '</div></div>' && isset( $settings[ 'title' ] ) && empty( $settings[ 'title' ] ) )
        $params[0][ 'before_widget' ] .= '<div class="content">';

    return $params;
}

From Toscho's answer:

register_sidebar(array(
    'name' => "Sidebar1",
    'id' => 'home-sidebar-1',
    'before_widget' => '<div class="sidebar-box">',
    'after_widget' => '</div></div>',
    'before_title' => '<div class="title">',
    'after_title' => '</div><div class="content">',
));

What this does is:

  • checks the settings for the registered sidebar for widgets ending in 2 closing <div>s
  • checks if there's no title
  • if not, it modifies the before_widget output to show the opening widget content div

You could use this approach to change the sidebar arguments in other ways to based on the widget instance's settings.

4
  • I have a problem with that: some widgets (like WP default ones, for example) set a default title if you leave that blank. So your function adds the div because there isn't a title in the params at that point, but the widget adds it afterwards and the code is messed up. Do you know how to check for the title from "within" the widget? Thanks
    – Cmorales
    Jan 25, 2013 at 1:36
  • Not without adding a widget by widget check or some horrendously complex code to render the widget silently then parse it for a title... Will have a think Jan 25, 2013 at 17:07
  • Maybe hooking to this codex.wordpress.org/Function_Reference/the_widget would help? They use it (for other reason) in this article wp.smashingmagazine.com/2012/12/11/…
    – Cmorales
    Jan 29, 2013 at 0:53
  • 1
    the_widget() isn't used in the dynamic_sidebar() function, it's a means of calling any widget with options you specify in place. It's another place to filter though so my solution doesn't cover that eventuality. Cheers Jan 29, 2013 at 17:14
3

I liked the idea of both @tosco and @sanchothefat answers - however I have struggled with that combination because whilst empty( $settings[ 'title' ] might indeed return true the widget itself might output a default title if none is set. This title is then wrapped in the before_title and after_title markup and again the page breaks. That's the case with some of the default widgets anyway.

Easier to just stick to standard widget registration parameters;

register_sidebar(array(
    'id' => 'sidebar1',
    'name' => __( 'Sidebar 1', 'bonestheme' ),
    'description' => __( 'The first (primary) sidebar.', 'bonestheme' ),
    'before_widget' => '<div class="block">',
    'after_widget' => '</div>',
    'before_title' => '<div class="block-title">',
    'after_title' => '</div>',
));

and then add a filter action as per Marventus' answer here;

http://wordpress.org/support/topic/add-html-wrapper-around-widget-content

function widget_content_wrap($content) {
    $content = '<div class="block-content">'.$content.'</div>';
    return $content;
}
add_filter('widget_text', 'widget_content_wrap');
1
  • 3
    and that works only for "Text Widgets" Dec 15, 2014 at 14:23
2

Add the second div to the title parameter:

register_sidebar(array(
        'name' => "Sidebar1",
        'id' => 'home-sidebar-1',
        'before_widget' => '<div class="sidebar-box">',
        'after_widget' => '</div></div>',
        'before_title' => '<div class="title">',
        'after_title' => '</div><div class="content">',
    ));
3
  • That fails if there's no widget title - it can be done though Dec 3, 2012 at 10:15
  • Yes it fails when there is no title..
    – MBraiN
    Dec 3, 2012 at 12:06
  • I've added an answer with some code to fix the titles problem. Should probably be merged with Toscho's really but he has epic rep already :) Dec 3, 2012 at 12:56
1

Here's what you do to achieve this:

register_sidebar(array(
        'name' => "Sidebar1",
        'id' => 'home-sidebar-1',
        'before_widget' => '<div class="sidebar-box"><div class="content">',
        'after_widget' => '</div></div>',
        'before_title' => '</div><div class="title">',
        'after_title' => '</div><div class="content">',
    ))

When there is no title you will get:

<div class="sidebar-box"><div class="content">
{CONTENT}
</div></div>

When there is a title you will get:

<div class="sidebar-box"><div class="content">
</div><div class="title">
{TITLE}
<div class="content">
{CONTENT}
</div></div>

To make sure the first empty content-div doesn't show in the latter case you add this to your css:

.sidebar-box .content:empty {display:none !important;}
1
  • Very smart way and its work.
    – Mohammed
    Jan 21, 2021 at 18:38
0

After looking at the previous answers, I think I fixed the problem Cmorales identified with sanchothefat's answer. Define your widget as follows:

register_sidebar( array(
    'name' => 'Sidebar',
    'id' => 'sidebar',
    'before_widget' => '<div class="panel panel-default %2$s" id="%1$s">',
    'after_widget' => '</div>',
    'before_title' => '',
    'after_title' => ''
) );

It's important that the before_title and after_title defaults are removed. After some trial and error I noted that the first filter that showed a default title was widget_title. Then use the widget_title filter like so:

function widget_title( $title ) {
    if ( $title )
        $title = '<div class="panel-heading"><h3 class="panel-title">' . $title . '</h3></div>';
    $title .= '<div class="panel-body">';
    return $title;
}

Basically, <div class="panel-heading"><h3 class="panel-title"> is my before_title and </h3></div> is my after_title. Finally, use dynamic_sidebar_params to prepend a closing div for our content wrap:

function dynamic_sidebar_params( $params ) {
    $params[0]['after_widget'] = '</div>' . $params[0]['after_widget'];
    return $params;
}

It isn't pretty, but it works.

1
  • Was initially very excited by this approach, but I ran into a problem with widgets that use esc_html($title) (i.e. BuddyPress). When that's the case, the added HTML is displayed escaped and not rendered on the front end.
    – Ryan
    Aug 25, 2014 at 2:53
0

I just slightly change the code so that don't need to touch register_sidebar. Register a sidebar with regular way:

register_sidebar(array(
    'name' => "Sidebar1",
    'id' => 'home-sidebar-1',
    'before_widget' => '<div class="sidebar-box">',
    'after_widget' => '</div>',
    'before_title' => '<div class="title">',
    'after_title' => '</div>',
));

And below is modified code From Sanchothefat's Solution:

/**
 * If no title given then add widget-content wrapper to before_widget
 * If title given then add content wrapper to before_widget
*/
add_filter('dynamic_sidebar_params', 'check_widget_template');
function check_widget_template($params){
    global $wp_registered_widgets;

    $settings_obj = $wp_registered_widgets[$params[0]['widget_id']]['callback'][0];
    $the_settings = $settings_obj->get_settings();
    $the_settings = $the_settings[$params[1]['number']];

    if (isset($params[0]['id']) && $params[0]['id'] == 'home-sidebar-1') {
        if (!isset($the_settings['title']) && empty($the_settings['title'])) {
            $params[0]['before_widget'] .= '<div class="widget-inner">';
            $params[0]['after_widget']  .= '</div>';
        } else {
            $params[0]['after_widget']  .= '</div>';
            $params[0]['after_title']   .= '<div class="widget-inner">';
        }
    }

    return $params;
}
0

As mentioned in the comments, some core widgets add their own title which then gets wrapped twice with the content div. To remove them, you could simply return an empty string. The only downside is that you have to enter all the titles manually.

function remove_default_widget_title( $title, $instance = [], $id = '' ) {
    if ( isset($instance['title']) && trim($instance['title']) == '' ) {
        return '';
    }
    return $title;
};
add_filter( 'widget_title', 'remove_default_widget_title', 10, 3 );

Correct me if I'm missing something here, but I think that's quite solid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.