0

Is there a way to pass data between the enclosing and enclosed shortcodes? For example, I would like to do something like:

[boxes per_row=4]
    [box]foo[/box]
    [box]bar[/box]
    [box]baz[/box]
    [box]quux[/box]
[/boxes]

And be able to have the different boxes styled differently (eg class='first', class='last', etc). Additionally, I'd like to be able to create a new row when the current row is full.

Basically, I want the parent to be able to get called back in some way when the child is called.

1 Answer 1

1

You can try this plugin:

/**
 * Plugin Name: My Boxes Shortcode 
 * Author: birgire
 * Author URI: http://wordpress.stackexchange.com/a/121423/26350
 */

add_action( 'plugins_loaded', array( 'My_Boxes_Shortcode', 'get_instance' ) );

if( ! class_exists( 'My_Boxes_Shortcode' ) )
{

    class My_Boxes_Shortcode
    {
        static private $instance = NULL;

        static public function get_instance() 
        {        
            if ( NULL === self::$instance )
                self::$instance = new self;

            return self::$instance;            
        }

        public function __construct() 
        {
            add_shortcode( 'boxes', array( $this, 'boxes_callback' ) );   
            add_shortcode( 'box',   array( $this, 'box_callback'   ) );   
        }        

        public function box_callback( $atts, $content ) 
        {        
            $atts = shortcode_atts( 
                        array(
                            'class'     => 'box',
                    ), $atts, 'my_box_shortcode' );

            // input
            $content               = esc_textarea( trim( $content ) );
            $atts['class']         = esc_attr( $atts['class'] );

            // output                       
            return sprintf( '<div class="%s">%s</div>', $atts['class'], $content );             
        }

        public function boxes_callback( $atts, $content ) 
        {        
            $atts = shortcode_atts( 
                        array(
                            'class'     => 'boxes',
                            'per_row'   => '3',
                    ), $atts, 'my_boxes_shortcode' );

            // input
            $content               = str_replace( '<br />', ' ', $content );
            $atts['class']         = esc_attr( $atts['class'] );
            $atts['per_row']       = (int) $atts['per_row'];

            // if per_row is not positive then force it to the default value.
            $atts['per_row'] = ( 1 > $atts['per_row'] )? 3 : $atts['per_row'];

            // split the content into boxes
            $content = str_replace( '[/box]', '[/box]<!--x-->', $content ); 
            $boxes = explode( '<!--x-->', do_shortcode( $content ) );

            // output           
            $html = '';

            // add a div wrapper around each row
            if( 0 < count( $boxes ) )
            {
                $html .= '<div class="row-wrapper">';

                foreach( $boxes as $i => $box )
                {           
                    $html .= $box;

                    // inject the row seperator
                    if(  ( $atts['per_row'] - 1 ) === ( $i % $atts['per_row'] ) )
                        $html .= '</div><div class="row-wrapper">';
                }
                $html .= '</div>';

                // remove the last row wrapper if it's empty
                if( 1 === ( count( $boxes ) % $atts['per_row'] ) )
                    $html = substr( $html, 0, (-1) * strlen( '<div class="row-wrapper"> </div> ' ) );
            }

            return sprintf( '<!-- start boxes wrapper: -->
                                     <div class="%s">%s</div>
                             <!-- end boxes wrapper -->', $atts['class'], $html );                      
        }

    } // end class 

} // end if class ! exists

I guess there are much better ways to do this, but you can adjust the class to your needs.

Then this kind of shortcode setup:

[boxes class="myboxes" per_row="3"]
    [box class="box first"]foo[/box]
    [box]bar[/box]
    [box]baz[/box]
    [box class="box last"]quux[/box]
[/boxes]

should give you the following HTML:

<!-- start boxes wrapper: -->
<div class="myboxes">
    <div class="row-wrapper"> 
        <div class="box first">foo</div> 
        <div class="box">bar</div> 
        <div class="box">baz</div> 
    </div>
    <div class="row-wrapper"> 
        <div class="box last">quux</div>
    </div>
</div>
<!-- end boxes wrapper -->

Your Answer

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

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