Skip to content
← All writing

When has_shortcode() can’t see a form added with do_shortcode()

A contact form gave me two odd symptoms at once. After submit, the page reloaded and showed “Nothing found.” And when a message did appear, it had no background, just bare text on the page.

Two symptoms usually mean two bugs. This time they shared one cause.

I started with the most boring check. I opened the page source and searched for the form plugin’s JavaScript file. It was not there. That single missing file explained both problems. Form plugins submit through JavaScript, so with no script the form falls back to a plain browser submit that reloads the page and lands on a URL with nothing to show. No script also meant no styles, so the message had no background.

So the real question became: why did the plugin never load its files?

Here is the code that decides, and the trap inside it:

function nrfm_enqueue_frontend_assets() {
    global $post;

    // Bail unless the shortcode sits inside the page's saved content.
    if ( ! is_a( $post, 'WP_Post' )
        || ! has_shortcode( $post->post_content, 'nrfm_form' ) ) {
        return;
    }

    // ... register and enqueue the script and styles ...

    // Safety net meant to catch template-embedded forms:
    add_action( 'wp_footer', function () {
        if ( ! empty( $GLOBALS['nrfm_form_rendered'] ) ) {
            wp_enqueue_script( 'narrative-forms' );
        }
    } );
}

has_shortcode() only reads the page’s saved content. But I had added the form through the theme template with do_shortcode(), not in the page content. So that check found nothing, hit return, and the function stopped.

Now look where the safety net is. That wp_footer block was built for exactly this case. It fires whenever a form actually renders. But it lives after the early return, so for a template form the code never reaches it. The net was there. The bug just walked around it.

The fix was to lift the loading out of that gate. Register the script up front, which outputs nothing on its own, then enqueue it whenever a form truly renders:

function nrfm_enqueue_frontend_assets() {
    // Register up front. Registering alone puts nothing on the page.
    wp_register_script( 'narrative-forms', $js_url, array(), $ver, true );
    wp_localize_script( 'narrative-forms', 'nrfm_ajax', $data );

    // Enqueue for ANY form that rendered, including ones added
    // through a theme template with do_shortcode().
    add_action( 'wp_footer', function () {
        if ( ! empty( $GLOBALS['nrfm_form_rendered'] ) ) {
            wp_enqueue_script( 'narrative-forms' );
        }
    } );
}

One detail made this safe. In WordPress, register and enqueue are different things. Registering a script only tells WordPress it exists: no tag, no request, nothing on the page. Only enqueue loads it, and that still happens only when a form is really there. So empty pages stay empty, and template forms finally work.

After that, the form submitted quietly, the message came back styled, and the “Nothing found” page was gone.


Leave a response