DevinVinson / WordPress-Plugin-Boilerplate

[WordPress] A foundation for WordPress Plugin Development that aims to provide a clear and consistent guide for building your plugins.
http://wppb.io
7.67k stars 2.25k forks source link

[CSS Enqueue Issue] Enqueue CSS to the header, but only on pages where my short-code is called. #469

Open Terminal-Access opened 6 years ago

Terminal-Access commented 6 years ago

Hey Guys,

I've written a Carousel slider that grabs products from WooCommerce and displays them via short-code on any page/widget or post that you require them on (It's visually based on the Woo Carousel available in the Avada Theme).

With the following options: Carousel Layout: Title & price on Rollover / Rollover alt Image (no title or price) Carousel Autoplay: On/Off Scroll Items: 1-5 Product Order Sorting: Ascending order / Randomized Order (default is random) Category Black List: self-explanatory. Number of Products: Total number of products to be pulled. Show Out of Stock Products: Yes / No

Responsive Design: yes Items displayed at one time: Mobile: 1 Tablet: 3 Desktop: 5

I'm getting on really well with my little plugin now, its working 98% the way I had planned, kicking the bugs out of my code and learning as I go, always nice when I learn something new!

I have stumbled into a problem that I've been working hard to overcome for the past 24 hours but just can't seem to crack.

Brief Description: My CSS is being enqueued in the footer, after my carousel code, rather than the header, causing a momentary graphical bug where the carousel loads and displays its data prior to the website reading and applying my CSS style.

Detailed Description:

As I've recently come to understand by default WordPress-Plugin-Boilerplate enqueues all CSS and JavaScript by using the standard WordPress "enqueue_script / enqueue_style" functions.

Further to this I have recently discovered that by default design WordPress enqueue's files globally site-wide rather than on an "as needed basis", this seems backwards and a very bad practice to me but that just my humble opinion.

By following a few tutorials found via google, I have managed to alter the plugin so that the Styles and JavaScript are now loaded on an "as needed basis" however this has lead to the problem briefly described above.

My CSS is being enqueued in the footer, after my carousel code, rather than the header, causing a momentary graphical bug where the carousel loads and displays its data prior to the website reading and applying my CSS style.

From doing a little research I've come to the understanding that this is caused due to a fairly recent change in the way WordPress handles enqueueing files after the initial page load has begun, Cheers WordPress thanks for making it just a bit harder!

I'm looking for any insight into how I can go about having my CSS be placed into the header without falling back to globally enqueueing that particular file.

Any and all assistance is as always greatly appreciated.

With the kindest of regards Lee.

slushman commented 6 years ago

Are you using wp_enqueue_style() to enqueue the stylesheet? What is it hooked to in your main plugin file?

Do you have a repo we can look at?

Terminal-Access commented 6 years ago

I do have a private repo set up on gitlab, that I can grant access to if you want to see the entirety of the code.

Below should be the relevant changes.

First I made the change to register the files instead fo enqueueing them

Changes made from stock: includes\class-woo-randomizer.php

private function define_public_hooks() 
{
    $plugin_public = new Woo_Randomizer_Public( $this->get_plugin_name(), $this->get_version() );
    $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'register_styles' );
    $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'register_scripts' );
    $this->loader->add_action( 'init', $plugin_public, 'register_shortcodes' );
}

Next, I moved the enqueuing directly to when the shortcode is called, that has the desired effect however as I said this leave the CSS being placed in the footer instead of the header.

public\class-woo-randomizer-public.php

public function register_styles()
 {
    wp_register_style($this->plugin_name.'owl-carousel', plugin_dir_url( __FILE__ ) . 'css/owl.carousel.min.css', array(), null, 'all');
    wp_register_style($this->plugin_name.'owl-carousel-theme', plugin_dir_url( __FILE__ ) . 'css/owl.theme.default.min.css', array(), null, 'all');
    wp_register_style($this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/'.$this->plugin_name.'-public.css', array(), null, 'all');
 }
public function enqueue_styles()
{
    wp_enqueue_style($this->plugin_name.'owl-carousel');
    wp_enqueue_style($this->plugin_name.'owl-carousel-theme');
    wp_enqueue_style($this->plugin_name);
}

 public function register_scripts()
{
    wp_register_script('jquery', plugin_dir_url( __FILE__ ) . 'js/jquery.min.js', array(), null, true);
    wp_register_script($this->plugin_name.'owl-carousel', plugin_dir_url( __FILE__ ) . 'js/owl.carousel.min.js', array(), null, true);
 }
public function enqueue_scripts()
{
    wp_deregister_script('jquery');
    wp_enqueue_script('jquery');
    wp_enqueue_script($this->plugin_name.'owl-carousel');
}

public function woo_randomizer_shortcode( $atts = array() )
{
    ob_start();
    $this->enqueue_styles();
    $this->enqueue_scripts();
    $defaults['page'] = 1;
    $args = shortcode_atts($defaults, $atts, 'woo-randomizer' );

    include_once( 'partials/'.$this->plugin_name.'-public-display.php' );
    $output = ob_get_contents();

    ob_end_clean();

    return $output;
} //woo_randomizer_shortcode
slushman commented 6 years ago

Check out this response: https://github.com/ThemeFuse/Unyson/issues/153#issuecomment-69721101

Since the enqueue calls happen after the head is already generated, the styles don't get applied immediately.

Maybe method #4 from this post would work? https://wordpress.stackexchange.com/questions/165754/enqueue-scripts-styles-when-shortcode-is-present

Basically, check the $post object content to see if it contains the shortcode, then enqueue like normal in the head.

Terminal-Access commented 6 years ago

Thanks, I'll check both of those out!

The help is greatly appreciated :D

Terminal-Access commented 6 years ago

Option 4 Worked perfectly.

Below are the changes in case anyone else comes looking for a solution to this.

## Changes Made: From the code above.

includes\class-woo-randomizer.php

private function define_public_hooks()
{
    $plugin_public = new Woo_Randomizer_Public( $this->get_plugin_name(), $this->get_version() );
    $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' );
    $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'register_scripts' );
    $this->loader->add_action( 'init', $plugin_public, 'register_shortcodes' );
}

public\class-woo-randomizer-public.php

//Removed register_styles() as this function is no longer required.

public function enqueue_styles()
 {
    global $post;
    $postType = $post->post_type;
     if($postType == 'post' || $postType == 'page'  && has_shortcode($post->post_content, 'woo-randomizer'))
    {
        wp_enqueue_style($this->plugin_name.'owl-carousel', plugin_dir_url( __FILE__ ) . 'css/owl.carousel.min.css', array(), null, 'all');
        wp_enqueue_style($this->plugin_name.'owl-carousel-theme', plugin_dir_url( __FILE__ ) . 'css/owl.theme.default.min.css', array(), null, 'all');
        wp_enqueue_style($this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/'.$this->plugin_name.'-public.css', array(), null, 'all');
    }
}

public function woo_randomizer_shortcode( $atts = array() )
{
    ob_start();
    _//$this->enqueue_styles(); - no longer called here, returned to the main class file._
    $this->enqueue_scripts();
    $defaults['page'] = 1;
    $args = shortcode_atts($defaults, $atts, 'woo-randomizer' );

    include_once( 'partials/'.$this->plugin_name.'-public-display.php' );
    $output = ob_get_contents();

    ob_end_clean();

    return $output;
} //woo_randomizer_shortcode
AlchemyUnited commented 6 years ago

Pardon me if I didn't read in detail, and I realize this is an old issue and likely since resolved but...Can't you just get the post object, look at the content, see if it contains your shortcode, and then use a conditional?

Maybe it's too early? Maybe I need more coffee, sorry?

p.s. And I thought there was a hook (filter?) that can tell you if a particular shortcode is being used (for just this reason), yes?

And how much CSS are we talking about? Isn't this only really an issue if the first page visited doesn't have the shortcode? That is, after that the browser has it cached. So if there's a second page, and it uses the shortcode, then the CSS is already there (in the browser cache), yes?