Give
Conditionally Load Give Styles and Scripts
Some developers put a lot of attention into which styles and scripts are loaded on each page of their site. This article explains how to conditionally load the Give styles and scripts and explains a little why we’ve chosen not to do this by default.
Pros and Cons of Conditionally Loading Styles and Scripts
While in theory it sounds very beneficial to only load Give styles and scripts when they are actually used on a page or post, in practice this often ends up creating more trouble than benefit. We have chosen to load all our Give styles and scripts minified and concatenated into one file each and have that load throughout the site.
The main reason we’ve chosen to do that is because very many WordPress sites use some sort of minification and/or caching plugin to concatenate and minify all the scripts and styles of the entire site into one large file or perhaps up to 4 smaller files each. When different scripts and styles are loaded conditionally on different pages/posts the results are often very mixed whether the conditionally loaded scripts will be minified correctly and applied correctly when needed.
Nevertheless, we wanted to provide a method for developers to conditionally load scripts with Give regardless.
/**
* GiveWP: stop core + add-on frontend assets loading on every page.
*
* Give_Scripts::public_enqueue_scripts()/public_enqueue_styles() (in the
* give plugin's includes/class-give-scripts.php) unconditionally enqueue
* the 'give' script and 'give-styles' stylesheet on every non-admin page
* load via wp_enqueue_scripts, regardless of whether the page has any
* Give content on it. GiveWP's newer block/campaign embeds
* (givewp/donation-form, givewp/campaign-form, etc.) already load their
* own dedicated bundles only when the block itself renders (see
* BlockRenderController::loadEmbedScript()), so this snippet doesn't need
* to touch those - they're already conditional.
*
* The following GiveWP add-ons do the exact same unconditional thing on
* every page load, independent of the 'give'/'give-styles' handles above -
* confirmed by reading each plugin's enqueue callback directly:
* - give-fee-recovery (script+style handle 'give-fee-recovery' -
* its script hard-depends on 'give', which is
* why dequeuing only 'give' throws a
* "was enqueued with dependencies that are
* not registered: give" notice on WP 6.9.1+
* instead of actually fixing anything)
* - give-recurring ('give_recurring_script' / 'give_recurring_css')
* - give-tributes ('give-tributes', 'give-tributes-common')
* - give-funds ('give-funds-script-frontend' / '...-style-frontend')
* - give-salesforce ('give-salesforce-script-frontend' / '...-style-frontend')
* - give-currency-switcher ('give-currency-switcher' / '...-style')
* - give core Stripe ('give-stripe-js', 'give-stripe-onpage-js',
* 'give-stripe-sepa', 'give-stripe-becs' -
* gated only by "is Stripe configured", not
* by page content)
* - give core Entities API ('givewp-entities-public')
*
* This lets Give (and every add-on above) register+enqueue as normal at
* their default wp_enqueue_scripts priority (10), then dequeues the whole
* handle set at priority 20 unless the request is one of the places
* legacy Give content can live:
* - a single give_forms post, or a give_forms_category/give_forms_tag archive
* - any of Give's shortcodes in the queried post's post_content
* - any of Give's Gutenberg blocks in the queried post's post_content
* - a classic (non-Elementor) GiveWP sidebar widget active anywhere
* - a GiveWP Elementor widget saved in the page's Elementor JSON
* (Elementor widgets don't live in post_content, so has_shortcode()/
* has_block() can't see them - the raw _elementor_data meta is
* checked for the widget's registered slug instead)
*
* Known limitation: this cannot see Give content injected by a theme
* template, a menu, an ACF field, or a global Elementor template/popup.
* Use GIVE_CONDITIONAL_ASSETS_ALLOWED_POST_IDS or the
* give_conditional_assets_should_load filter for any page like that,
* rather than extending the detection logic.
*
* If you run a full-page cache (this site has LiteSpeed Cache installed),
* purge it after installing/changing this snippet and after any content
* change that adds/removes Give content from a page - otherwise you'll be
* looking at stale cached HTML instead of what this snippet actually did.
*
* QA before relying on this: click through every page template that can
* carry a donation form/campaign/widget (home, a single give_forms page,
* a campaign page, any Elementor-built donate page, any page with a
* sidebar donation widget, the donation confirmation/receipt flow) and
* confirm styling and the donate flow both still work, then check a plain
* content page shows none of the handles below in dev tools.
*/
const GIVE_CONDITIONAL_ASSETS_ALLOWED_POST_IDS = [];
const GIVE_CONDITIONAL_ASSETS_SCRIPT_HANDLES = [
'give',
'give-fee-recovery',
'give_recurring_script',
'give-tributes',
'give-tributes-common',
'give-funds-script-frontend',
'give-salesforce-script-frontend',
'give-currency-switcher',
'give-stripe-js',
'give-stripe-onpage-js',
'give-stripe-sepa',
'give-stripe-becs',
'givewp-entities-public',
];
const GIVE_CONDITIONAL_ASSETS_STYLE_HANDLES = [
'give-styles',
'give-fee-recovery',
'give_recurring_css',
'give-tributes',
'give-funds-style-frontend',
'give-salesforce-style-frontend',
'give-currency-switcher-style',
'give-authorize-css',
'givewp-design-system-foundation',
'givewp-campaign-blocks-fonts',
];
const GIVE_CONDITIONAL_ASSETS_SHORTCODES = [
'donation_history',
'give_form',
'give_goal',
'give_login',
'give_register',
'give_receipt',
'give_profile_editor',
'give_totals',
'give_form_grid',
'give_donor_wall',
'give_donor_dashboard',
'give_multi_form_goal',
'givewp_campaign',
'givewp_campaign_grid',
'givewp_campaign_form',
'givewp_campaign_comments',
'givewp_campaign_donors',
'givewp_campaign_donations',
'givewp_campaign_stats',
'givewp_campaign_goal',
];
const GIVE_CONDITIONAL_ASSETS_BLOCKS = [
'givewp/donation-form',
'givewp/campaign-block',
'givewp/campaign-cover-block',
'givewp/campaign-title',
'givewp/campaign-stats-block',
'givewp/campaign-donors',
'givewp/campaign-form',
'givewp/campaign-donations',
'givewp/campaign-grid',
'givewp/campaign-goal',
'givewp/campaign-donate-button',
'givewp/campaign-comments-block',
'givewp/event-tickets',
'give/donor-dashboard',
'give/multi-form-goal',
'give/progress-bar',
];
// get_name() slugs from give/src/ThirdPartySupport/Elementor/Widgets/{V1,V2}/*.
const GIVE_CONDITIONAL_ASSETS_ELEMENTOR_WIDGETS = [
'Give Form',
'Give Goal',
'Give Login',
'Give Register',
'Give Totals',
'Give_Profile_Editor',
'Give_Donor_Wall',
'GiveWP_Form_Grid',
'GiveWP Subscriptions',
'Give Multi Form Goal',
'Donation History',
'Donation Receipt',
'givewp_donation_form',
'givewp_donation_form_grid',
'givewp_donor_wall',
'givewp_campaign',
'givewp_campaign_grid',
'givewp_campaign_form',
'givewp_campaign_comments',
'givewp_campaign_donors',
'givewp_campaign_donations',
'givewp_campaign_stats',
'givewp_campaign_goal',
];
const GIVE_CONDITIONAL_ASSETS_WIDGET_ID_BASE = 'give_forms_widget';
add_action( 'wp_enqueue_scripts', 'give_conditional_assets_maybe_dequeue', 20 );
function give_conditional_assets_maybe_dequeue() {
if ( is_admin() || give_conditional_assets_should_load() ) {
return;
}
foreach ( GIVE_CONDITIONAL_ASSETS_SCRIPT_HANDLES as $handle ) {
wp_dequeue_script( $handle );
wp_deregister_script( $handle );
}
foreach ( GIVE_CONDITIONAL_ASSETS_STYLE_HANDLES as $handle ) {
wp_dequeue_style( $handle );
wp_deregister_style( $handle );
}
}
function give_conditional_assets_should_load(): bool {
$post = get_queried_object();
$post = $post instanceof WP_Post ? $post : null;
$shouldLoad = give_conditional_assets_post_needs_assets( $post )
|| give_conditional_assets_classic_widget_active()
|| ( $post && in_array( $post->ID, GIVE_CONDITIONAL_ASSETS_ALLOWED_POST_IDS, true ) );
/**
* Final override for whether GiveWP's frontend assets should load on
* this request. Use this rather than editing detection logic above for
* one-off pages (e.g. a global Elementor template or popup that embeds
* a Give widget on every page).
*
* @param bool $shouldLoad
* @param WP_Post|null $post
*/
return (bool) apply_filters( 'give_conditional_assets_should_load', $shouldLoad, $post );
}
function give_conditional_assets_post_needs_assets( ?WP_Post $post ): bool {
if ( is_singular( 'give_forms' ) || is_tax( 'give_forms_category' ) || is_tax( 'give_forms_tag' ) ) {
return true;
}
if ( ! $post ) {
return false;
}
foreach ( GIVE_CONDITIONAL_ASSETS_SHORTCODES as $shortcode ) {
if ( has_shortcode( $post->post_content, $shortcode ) ) {
return true;
}
}
foreach ( GIVE_CONDITIONAL_ASSETS_BLOCKS as $block ) {
if ( has_block( $block, $post ) ) {
return true;
}
}
return give_conditional_assets_elementor_content_has_give_widget( $post->ID );
}
function give_conditional_assets_elementor_content_has_give_widget( int $postId ): bool {
$elementorData = get_post_meta( $postId, '_elementor_data', true );
if ( ! is_string( $elementorData ) || '' === $elementorData ) {
return false;
}
foreach ( GIVE_CONDITIONAL_ASSETS_ELEMENTOR_WIDGETS as $widget ) {
if ( false !== strpos( $elementorData, '"widgetType":"' . $widget . '"' ) ) {
return true;
}
}
return false;
}
/**
* Note: this only knows the widget is active *somewhere*. If your theme
* shows that sidebar on every page (e.g. a global footer widget area), this
* will force Give's assets to load site-wide and you'll get little/no
* benefit from this snippet - in that case, replace the donation form
* widget with the givewp/donation-form block or the [give_form] shortcode
* placed only where it's actually needed.
*/
function give_conditional_assets_classic_widget_active(): bool {
return (bool) is_active_widget( false, false, GIVE_CONDITIONAL_ASSETS_WIDGET_ID_BASE );
}
A Note on Debugging
If you have WP_DEBUG set to true, then Give will load all of its scripts individually throughout your site and the above function will not work at all. This only applies to the minified and concatenated style and script: give.min.css and give.all.min.js which are only loaded when WP_DEBUG set to false.