skip to Main Content

I need to grab the_content() via an AJAX request and render all Gutenberg blocks with their inline styling in the page.

Problem is, unique block classes are added to the footer in theme templates.

.wp-container-5 {
  display: flex;
  gap: 2em;
  flex-wrap: nowrap;
  align-items: center;
}

When get_the_content() is used via an AJAX request, that unique block styling isn’t rendered. I would guess this is because the inline block styling relies on a hook of some sort that doesn’t get fired with an AJAX request. do_blocks() does not render the inline styling.

I’ve searched the database and scoured the WordPress source files and cannot find where classes like .wp-container-5 are coming from. I thought if I could find the location of the inline styling, I could simply query it and render it in the page.

Does anyone know where the unique block styles are stored and/or how to query them and include them via an AJAX request?

2

Answers


  1. Chosen as BEST ANSWER

    I managed to solve this after many hours of frustration.

    In wp-includes/block-supports/layout.php there is a function called wp_render_layout_support_flag(). This function takes a block's content and a block object and assigns the unique class wp-container- with a unique ID at the end. It then renders the inline styling with the function wp_get_layout_style() and enqueues that styling with wp_enqueue_block_support_styles().

    Problem is, wp_render_layout_support_flag() doesn't return the styling. It returns the block content with CSS classes and inserts the styling into the footer with CSS classes that match. So, it's not as simple as just calling wp_get_layout_style() because a unique ID is assigned in wp_render_layout_support_flag() that is only matched when wp_get_layout_style() is called inside the wp_render_layout_support_flag() function.

    The solution was to copy and paste (not ideal but it works) the wp_render_layout_support_flag() function and slightly alter it.

    function my_render_layout_support_flag( $block_content, $block ) {
        $block_type     = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
        $support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
    
        if ( ! $support_layout ) {
            return $block_content;
        }
    
        $block_gap             = wp_get_global_settings( array( 'spacing', 'blockGap' ) );
        $default_layout        = wp_get_global_settings( array( 'layout' ) );
        $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
        $default_block_layout  = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
        $used_layout           = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
        if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
            if ( ! $default_layout ) {
                return $block_content;
            }
            $used_layout = $default_layout;
        }
    
        $class_name = wp_unique_id( 'wp-container-' );
        $gap_value  = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
        // Skip if gap value contains unsupported characters.
        // Regex for CSS value borrowed from `safecss_filter_attr`, and used here
        // because we only want to match against the value, not the CSS attribute.
        if ( is_array( $gap_value ) ) {
            foreach ( $gap_value as $key => $value ) {
                $gap_value[ $key ] = $value && preg_match( '%[\(&=}]|/*%', $value ) ? null : $value;
            }
        } else {
            $gap_value = $gap_value && preg_match( '%[\(&=}]|/*%', $gap_value ) ? null : $gap_value;
        }
    
        $fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
    
        // If a block's block.json skips serialization for spacing or spacing.blockGap,
        // don't apply the user-defined value to the styles.
        $should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
        $style                         = wp_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value );
        // This assumes the hook only applies to blocks with a single wrapper.
        // I think this is a reasonable limitation for that particular hook.
        $content = preg_replace(
            '/' . preg_quote( 'class="', '/' ) . '/',
            'class="' . esc_attr( $class_name ) . ' ',
            $block_content,
            1
        );
    
        // This is where the changes happen
    
        return '<style>' . $style . '</style>' . $content;
    }
    

    The only change is near the end where wp_enqueue_block_support_styles() was removed and now styling and content are returned.

    Now, Gutenberg blocks can be rendered and have the proper styling when using an AJAX call!

    $content = get_the_content(null, true, $post_id);
    $blocks = parse_blocks( $content );
    
    foreach ($blocks as $block) {
        echo my_render_layout_support_flag( render_block($block), $block );
    }
    

    This solution feels kind of ridiculous but it works... WordPress really should support the rendering of blocks asynchronously.


  2. I have not tested this within the response from an AJAX request using the old admin-ajax.php method, however, I had been trying to accomplish this same thing within a response from the REST API.

    Having found this thread and reading over the solution provided by Myles — thanks for your details, btw — and looking into the wp_render_layout_support_flag function a bit more, I came up with a potential alternative.

    Generically, with the REST API, you could add something like this:

    register_rest_field('post', 'styles', [
        'get_callback' => function ($post) {
            ob_start();
    
            do_action('wp_footer');
    
            $content = ob_get_clean();
    
            // perform any needed manipulation
    
            return $content;
        },
        'schema' => [
            'description' => __('Styles'),
            'type' => 'string',
        ],
    ]);
    

    You’d probably want to strip out non-style content in the result, in the event you have other functions pushing content that would appear within wp_footer.

    Perhaps something like this would get you what you are looking for:

    ob_start();
    
    the_content();
    
    do_action('wp_footer');
    
    $content = ob_get_clean();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search