Add class to before_widget for all widgets with a dropdown and a counter

Some predefined widgets offer the option to use a select element (dropdown) and/or a counter after each entry. How can I add the CSS classes widget-with-dropdown or widget-with-counters to these widgets?

There are already solutions for custom widgets, known sidebar ids or positions, but I have to inspect the parameters for the current instance, and I want to catch all possible positions or widgets (if they follow the WordPress naming scheme).

Related posts

3 comments

  1. The CSS classes are applied in the function dynamic_sidebar(). There is no specific filter for that:

    // Substitute HTML id and class attributes into before_widget
    $classname_ = '';
    foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
        if ( is_string($cn) )
            $classname_ .= '_' . $cn;
        elseif ( is_object($cn) )
            $classname_ .= '_' . get_class($cn);
    }
    $classname_ = ltrim($classname_, '_');
    $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
    

    But there is a filter for $params right after this code block:

    $params = apply_filters( 'dynamic_sidebar_params', $params );
    

    To get the settings for the current widget, we have to search in the global variable $wp_registered_widgets for an entry with the key $params[ 0 ][ 'widget_id' ].
    If it exists, it has a class instance as callback, and we can use that object’s method get_settings() to … well … get the settings.
    The returned array has probably a key that equals $params[1][ 'number' ].
    Associated with this key is an array again, and here we might find another key dropdown (or count) with a value of 0 or 1.
    If it is 1, we add the new classes to the string in $params[0]['before_widget'].

    I think this easier to read as code:

    is_admin() || add_filter( 'dynamic_sidebar_params', 'wpse_widget_classes' );
    
    /**
     * Add classes for widgets with counters or dropdowns.
     *
     * @param  array $params
     * @return array
     */
    function wpse_widget_classes( $params ) {
    
        global $wp_registered_widgets;
    
        $classes     = array();
        $instance_id = $params[1][ 'number' ];
        $widget_id   = $params[ 0 ][ 'widget_id' ];
        $settings    = $wp_registered_widgets[ $widget_id ][ 'callback' ][ 0 ]->get_settings();
    
        if ( empty ( $settings[ $instance_id ] ) )
            return $params;
    
        if ( ! empty ( $settings[ $instance_id ][ 'dropdown' ] ) )
            $classes[] = 'widget-with-dropdown';
    
        if ( ! empty ( $settings[ $instance_id ][ 'count' ] ) )
            $classes[] = 'widget-with-counters';
    
        if ( empty ( $classes ) )
            return $params;
    
        $params[0]['before_widget'] = str_replace(
            'class="',
            'class="' . join( ' ', $classes ) . ' ',
            $params[0]['before_widget']
        );
    
        return $params;
    }
    
  2. Or, you can just use a plugin:

    Both plugins are doing pretty much the same as @toscho showed in his answer.

    Widget CSS Classes plugin

    $this_id    = $params[0]['id']; // Get the id for the current sidebar we're processing
    $widget_id  = $params[0]['widget_id'];
    $widget_obj = $wp_registered_widgets[$widget_id];
    $widget_num = $widget_obj['params'][0]['number'];
    

    Source link to v1.2.1

    Custom Widget Classes plugin

    global $wp_registered_widgets;
    $widget_id    = $params[0]['widget_id'];
    $widget_obj    = $wp_registered_widgets[$widget_id];
    $widget_opt    = get_option($widget_obj['callback'][0]->option_name);
    $widget_num    = $widget_obj['params'][0]['number'];
    

    Source link to v1.1

    Just to prove that @toscho is right and there seems to be no other option to go on this.

  3. first add a custom placeholder class in the constructor

    <?php
    public function __construct() {
       $widget_ops  = array(
          'classname'                   =>; 'widget_text eaa __eaa__', //__eaa__ is my placeholder css class
          'description'                 =>; __( 'AdSense ads, arbitrary text, HTML or JS.','eaa' ),
          'customize_selective_refresh' =>; true,
       );
       $control_ops = array( 'width' =>; 400, 'height' =>; 350 );
       parent::__construct( 'eaa', __( 'Easy AdSense Ads &amp; Scripts', 'eaa' ), $widget_ops, $control_ops );
    }
    ?>
    

    Then replace it with the class/es of your choice based on the widget options like this

    <?php
    if ( $instance['no_padding'] ) {
       $args['before_widget'] = str_replace( '__eaa__', 'eaa-clean', $args['before_widget'] );
    }
    ?>
    

    We are using the place holder to limit the ways in which the html of before_widget may affect our plugin functionality

    You can find the details with example at
    http://satishgandham.com/2017/03/adding-dynamic-classes-custom-wordpress-widgets/

Comments are closed.