Cloud Four Blog

Archive for the ‘Blogs and Social Media’ Category

Getting all JavaScript into the Footer in WordPress? Not so fast, Buster!

Thursday, September 17th, 2009


Warning: Technical WordPress post ahead!

Overview: Really Getting JavaScript Into the Footer

Quoth the WordPress Version 2.8 feature list:

– Improvements to the script loader: allows plugins to queue scripts for the front end head and footer, adds hooks for server side caching of compressed scripts, adds support for ENFORCE_GZIP constant (deflate is used by default since it’s faster)

At the time, I thought Wow, cool. When I have time, I’ll investigate that and then immediately forgot about it for a few months. During RSS-coffee-breaks I read Lester Chan’s post about how to put JavaScript in the footer (sounds easy enough!) and Andrew Ozz’s post, which left me coated with an intense and foolish optimism about compression.

What I aim to do in this post — part one of a series of three if I find time to investigate the second and third pieces — is explain why it’s not as easy as you’d expect to get some scripts (specifically scripts that come with WordPress by default) into the footer, and how you can make it happen.

If you’re in a hurry, you can skip to the Summary section at the end of the post.

WP JavaScript Inclusion: Header vs. Footer

What I read had me believe that it is falling-off-log easy to put all WordPress JavaScript in the footer. If you are trying to include a piece of JavaScript, say, in a plugin, that WP has not previously known about, it is that easy. But woe if you try this on certain scripts that come packaged with WordPress.

Let’s take a look at the functions that are involved in letting WordPress know that you want to use a given piece of JavaScript.

These are taken from wp-includes/functions.wp-scripts.php.

wp_register_script() and wp_enqueue_script()

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * Register new JavaScript file.
 *
 * @since r16
 * @see WP_Dependencies::add() For parameter information.
 */
function wp_register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
	global $wp_scripts;
	if ( !is_a($wp_scripts, 'WP_Scripts') )
		$wp_scripts = new WP_Scripts();
 
	$wp_scripts->add( $handle, $src, $deps, $ver );
	if ( $in_footer )
		$wp_scripts->add_data( $handle, 'group', 1 );
}
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
 * Enqueues script.
 *
 * Registers the script if src provided (does NOT overwrite) and enqueues.
 *
 * @since r16
 * @see WP_Script::add(), WP_Script::enqueue()
*/
function wp_enqueue_script( $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
	global $wp_scripts;
	if ( !is_a($wp_scripts, 'WP_Scripts') )
		$wp_scripts = new WP_Scripts();
 
	if ( $src ) {
		$_handle = explode('?', $handle);
		$wp_scripts->add( $_handle[0], $src, $deps, $ver );
		if ( $in_footer )
			$wp_scripts->add_data( $_handle[0], 'group', 1 );
	}
	$wp_scripts->enqueue( $handle );
}

As a theme developer, you may only ever have encounters with wp_enqueue_script(). But let’s look at what each of these functions does so I can then explain the issue.

wp_register_script() tells WordPress about a script, but does not actually cause it to be included in a given page request. By default, WordPress registers a gripload of scripts that are then available to you, the theme hacker, when or if you should need them. A list of these can be found in wp-includes/script-loader.php in the wp_defalut_scripts() function. Highlights include jQuery, prototype, scriptaculous, etc., as well as extensions to those frameworks (e.g. jQuery UI). WordPress (via the WP_Scripts class, itself extended from WP_Dependencies) handles dependencies and makes sure that jQuery UI doesn’t get included without its necessary jQuery, if you should forget to enqueue jQuery itself or it gets enqueued after jQuery UI.

wp_enqueue_script() tells WordPress, hey, I actually need this script, in this request. You may have seen or done something like this:

wp_enqueue_script('jquery');

This spools up jQuery and spits out a script tag to include the requested script.

In a lot of cases, it seems easy and straightforward to do this simple enqueue request. jQuery comes packaged with WP and is automatically registered for you, so you just have to hand the wp_enqueue_script() function one argument: $handle.

As a clever theme hacker, you may have noticed that the signature for wp_enqueue_script() and wp_register_script() changed in version 2.8 and this seems exciting. An $in_footer parameter was added:

wp_enqueue_script( $handle, $src = false, $deps = array(), $ver = false, $in_footer = false );

Totally psyched, you update your theme and use:

wp_enqueue_script('jquery','','','',true);

And wait for the magic. RELOAD! Wait, maybe something’s cached. RELOAD! Wait, what? RELOADRELOADRELOAD.

jQuery is still in the head.

So you get irritable and you quit trying, or like me you spend a few hours deactivating all of your plugins and trying to figure out where the problem is. You notice that the same thing happens with other scripts that WP already knows about (that is, the scripts in wp_default_scripts()). Stubbornly, they won’t get out of your page’s head element.

Here’s why.

Why WordPress Default JavaScripts Won’t Move to the Footer

The crux is: if the script you are enqueueing has anything defined as a dependency in WordPress’ wp_default_scripts() function, it will ignore your request to put it in the footer (I’ll provide a workaround shortly).

The reason for this is twofold.

Dependency Handling and “Groups”

Part of WordPress’ dependency handling for scripts involves “groups”, the ins and outs of which I’ll leave as an exercise for an intrigued reader. In an oversimplification, it put scripts with dependencies (e.g. jQuery, Scriptaculous) in groups[0]. It explicitly puts scripts that depend on other scripts in groups[1]. The default behavior of both wp_register_script() is to put a script in groups[0] if the $in_footer argument != true.

How Groups Handling does Something Possibly Confusing

OK, now check out this snippet from class.wp-scripts.php (do_item() method):

85
86
87
88
if ( 0 === $group && $this->groups[$handle] > 0 ) {
	$this->in_footer[] = $handle;
	return false;
}

This piece of logic puts groups > 0 into the footer. So that’s how WP decides to move things into the footer–it’s based on where the script is in the $wp_scripts->groups Array.

But here’s a potential pitfall in the wp_enqueue_script() function:

	if ( $src ) {
		$_handle = explode('?', $handle);
		$wp_scripts->add( $_handle[0], $src, $deps, $ver );
		if ( $in_footer )
			$wp_scripts->add_data( $_handle[0], 'group', 1 );
	}
	$wp_scripts->enqueue( $handle );

If you don’t give wp_enqueue_script() a $src (which you don’t have to provide do if the script has already been registered) for your $handle, it is going to enqueue it in whatever group it’s already registered in. So, when you do your little simple enqueue:

wp_enqueue_script('jquery','','','',true);

The entire part of the function where it would put it in the footer doesn’t execute because the if($src) test fails. And jQuery has already been registered—in groups[0].

Possible Workaround

I don’t like this but it works:

wp_enqueue_script('jquery','/wp-includes/js/jquery/jquery.js','','',true);

jQuery is now in the footer.

This does still appear to handle dependencies correctly, but I haven’t deeply tested. Also, there are some valid reasons to have some scripts, and possibly jQuery, in the head. This was just a demo.

Getting JS into the Footer: Quick Summary

// This will NOT put jquery in the footer
wp_enqueue_script('jquery','','','',true); 
 
// But this will, if inelegant and circumventing abstraction
wp_enqueue_script('jquery','/wp-includes/js/jquery/jquery.js','','',true);

For My Next Trick…Compression

Really, this post was a red herring. I tripped into this oddity while investigating the second part of the WordPress 2.8 feature claim: adds hooks for server side caching of compressed scripts, adds support for ENFORCE_GZIP constant (deflate is used by default since it’s faster). We’ll talk about that next time.

WordPress: Taking the Hack out of Multiple Custom Loops

Monday, September 14th, 2009

Overview

Consider this a looking-forward-to WordCamp Portland post! WordCamp is this weekend in Portland, Sept. 19-20. It is utterly sold out!

This post is aimed at those comfortable with WordPress hackery and PHP programming.

Making things work for our customers often–nay, all the time–ends up with the need for multiple WordPress loops on pages

On a given day, one of our enterprise or otherwise-crafty-and-complex clients may request, say, the three most recent posts about foo in the right sidebar, “upcoming events” (e.g. date-sensitive posts in some events category) in another box, a couple of embedded and editable WordPress page components, a random post or two, and then the “traditional” post loop itself. Whew.

I have long tried to find the best way to handle custom loops. For sake of definition, custom loop here means querying and displaying posts and/or pages within a page that are not that page’s “natural” queried posts (that is, the landing page of a WordPress blog by default “naturally” queries the most recent posts, and a category page “naturally” queries for posts in a given category).

There are a number of plugins and even entire themes keyed toward custom-loop-heavy development and modular formatting of posts in loops, including the Carrington JAM theme. But I find that most solutions either are a bit too baked–the Carrington theme feels like it would require a lifestyle change and an adoption of a new outlook (not that I’m ragging on the Carrington stuff; I’ve actually heard good stuff about it)–or a little bit too under-baked and soggy in the middle (“Here’s a two-line code snippet to call query_posts! I think? I mean, this should work, probably.”).

I’m Tired of Solving these things Ad Nauseum

Problems I keep running into:

  • The dreadful tendency to forget to restore the “natural”, appropriate global post and wp_query objects after one is done messing about. Those are sort of inviolate and should not be sullied, but it’s wretchedly easy to lose track.
  • Yet, if one does not pollute the global namespace with posts in the custom loop, one cannot access certain template tags and the like.
  • The mishmash of logic and formatting that often happens when one munges together a custom query in a page or post template is totally aesthetically unpleasing and betrays a certain lack of character/mettle.
  • Duplicated markup across a site that would better be modular and re-used.

It occurred to me recently that there is a straightforward way to deal with this. By creating a custom function (in a given theme’s functions.php file) of relatively low complexity, one can solve some of these problems once and for all.

The function: lyzadotcom_custom_loop($args)

It’s prefaced with lyzadotcom because I initially wrote it for my own site, but you can call it whatever.

This is what it looks like, and we’ll talk about it more below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * To use when a loop is needed in a page. Use $args to call query_posts and then 
 * use the template indicated to render the posts. The template gets included once
 * per post.
 * 
 * @param Array $args               Wordpress-style arguments; passed on to query_posts 
 *                                  'template' => name of post template to use for posts
 * @return Array of WP $post objs   Matching posts, if you should need them.
 */
function lyzadotcom_custom_loop($args)
{
    global $wp_query;
    global $post;
    $post_template_dir          = 'post_templates';
    /* The 'template' element should be the name of the PHP template file
       to use for rendering the matching posts. It should be the name of file,
       without path and without '.php' extension. e.g. the default value 'default'
       is $post_template_dir/default.php
    */
    $defaults                   = Array('template' => 'default' ); 
 
    $opts = wp_parse_args($args, $defaults);
 
    // Bring arguments into local scope, vars prefixed with $loop_
    extract($opts, EXTR_PREFIX_ALL, 'loop');
 
    // Preserve the current query object and the current global post before messing around.
    $temp_query = clone $wp_query;                                  
    $temp_post  = clone $post;
 
    $template_path = sprintf('%s/%s/%s.php', dirname(__FILE__), $post_template_dir, $loop_template);
 
    if(!file_exists($template_path))
    {
        printf ('<p class="error">Sorry, the template you are trying to use ("%s")
            in %s() does not exist (%s).',
            $template, 
            __FUNCTION__, 
            __FILE__);
        return false;
    }
    /* Allow for display of posts in order passed in post__in array
       [as the 'orderby' arg doesn't seem to work consistently without giving it some help]
       If 'post__in' is in args and 'orderby' is set to 'none', just grab those posts,
       in the order provided in the 'post__in' array.
    */
    if($loop_orderby && $loop_orderby == 'none' && $loop_post__in)     
    {
        foreach($loop_post__in as $post_id)
            $loop_posts[] = get_post($post_id);
    }
    else
        $loop_posts = query_posts($args);
 
    /* Utility vars for the loop; in scope in included template */
    $loop_count             = 0; 
    $loop_odd               = false;
    $loop_even              = false;
    $loop_first             = true;
    $loop_last              = false;
    $loop_css_class         = '';                               // For convenience
    $loop_size = sizeof($loop_posts);
    $loop_owner = $temp_post;       /* The context from within this loop is called
                                       the global $post before we query */
 
    foreach($loop_posts as $post)
    {
        $loop_count += 1;
        ($loop_count % 2 == 0) ? $loop_even = true : $loop_even = false;
        ($loop_count % 2 == 1) ? $loop_odd  = true : $loop_odd  = false;
        ($loop_count == 1) ?     $loop_first = true : $loop_first = false;
        ($loop_count == $loop_size) ? $loop_last = true : $loop_last = false;
        ($loop_even) ? $loop_css_class = 'even' : $loop_class = 'odd';
        setup_postdata($post);
        include($template_path);
    }
    $wp_query = clone $temp_query;  // Put the displaced query and post back into global scope
    $post = clone $temp_post;       // And set up the post for use.
    setup_postdata($post);
    return $loop_posts;
}

Loop Templates

  • By default, this function will look for loop templates in a directory called ‘post_templates’ in your theme’s template directory. Also by default, it will look for a file called ‘default.php’ in this directory. You can change the directory for templates if this bugs you.
  • Templates are PHP files and you can do what you will in them. All template tags are available.
  • You can create as many template files as you want and use them for different chunks of content.

Example Loop Template File

For example, this might be your default.php template file.

    <div class="post <?php echo $loop_css_class ?>" id="post-<?php the_ID(); ?>">
        <h4><?php echo $loop_count; ?><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>">
            <?php the_title(); ?>
        </a></h4>
        <em><?php the_date(); ?></em><br />
        <?php the_excerpt(); ?>        
    </div>

You can see that, in addition to standard “>WordPress template tags, I’ve used a couple of the batching variables made available by the function.

Batching Variables Available to Loop Templates

Available variables in loop templates are:

$loop_count                         // The count of the current post within the loop
$loop_odd                           // True if we're on an odd item (e.g. $loop_count = 1, 3, 5...)
$loop_even                          // True if we're on an even item (e.g. $loop_count = 2, 4, 6...)
$loop_first                         // True if this is the first post/page in the loop
$loop_last                          // True if this is the last post/page in the loop
$loop_css_class                     // 'even' if $loop_even, 'odd' if $loop_odd
$loop_size = sizeof($loop_posts);   // Total number of posts/pages in loop
$loop_owner = $temp_post;           // The post in global scope before this loop was entered

How to Use this Thing

Requirements

  • The use of clone means you need to have PHP5.
  • I have not tested this on any versions of WordPress 2.8. I imagine it would be OK, but YMMV.

Usage

  1. Edit your themes functions.php file and stick this function in it.
  2. Create at least one loop template file. By default (unless you tell it otherwise, which likely you will) this function looks for a template in
    <template_directory>/post_templates/default.php

    We’ll talk about templates shortly.

  3. Call the function from any WordPress template file you might need it from. You talk to it with WordPress query-string-style arguments. This function takes any argument that the query_posts() function takes.
  4. It also takes an additional argument, ‘template’. This is how you tell it which loop template to use.

Usage Examples

Embed an editable Page into Content

  1. In the desired WordPress template:
        <?php
            $args = Array('pagename'         => 'embed-this-page',
                          'template'         => 'embed');
        ?>  
        <?php lyzadotcom_custom_loop($args); ?>
  2. This is going to look for the template ‘embed.php’ in your $post_templates directory. This template might look like this:
        <div class="embedded" id="embedded-page-<?php the_ID(); ?>">
            <strong><?php the_title(); ?></strong><br />
            <?php the_content(); ?>
            <?php edit_post_link(); ?>
        </div>

Display 5 Most Recent Posts from Several Categories, Titles Only

  1. In the desired WordPress template:
        <ul>
        <?php 
            // This assumes you have category IDs. 
            // Getting them is beyond the scope of this pos
            $args = Array('category__in'        => Array(5,6,4), 
                          'showposts'           => 5
                          'template'            => 'list_with_link');
            lyzadotcom_custom_loop($args);
        ?>
        </ul>
  2. list_with_link.php might look like:
        <li id="link-<?php the_ID(); ?>" class="<?php echo $list_css_class>">
            <a href="<?php the_permalink()" title="<?php the_title(); ?>"><?php the_title(); ?></a>
        </li>

Display Your Posts Tagged ‘Banana’ from March of 2009

  1. In the desired WordPress template:
        <h3>March's Bananas</h3>
        <?php
            $args = ('tag'              => 'banana',
                     'showposts'        => -1, //show all posts
                     'monthnum'         => 3,
                     'year'             => 2009,
                     'template'         => 'month_archive_list');
            $banana_posts = lyzadotcom_custom_loop($args);
            /* Hint: You might used the returned $banana_posts to
               populate another call to lyzadotcom_custom_loop(), 
               e.g. 'post__not_in' => [ids from $banana_posts] 
               to avoid duplicating posts!
            */
        ?>
  2. month_archive_list.php might look like:
        <div class="archive_post" id="archive_post-<?php the_ID(); ?>">
            <?php echo $loop_count; ?> of <?php echo $loop_size; ?> Bananas:
            <strong><?php the_title(); ?></strong><br />
            <?php the_content(); ?>
            <?php edit_post_link(); ?>
        </div>

FAQ (In Which I Anticipate Questions)

Will this work with pages as well as posts?
Yes. Sure. Just fine! Anything that query_posts() can do, I can do too!
Why isn’t this a plugin?
It took me hours just to write this post; I’ve got stuff to do! If there’s a lot of interest, maybe I’ll consider it.
I found a bug/horrible security hole/idiocy?
Please let me know! Gently! Comments to this post is a fine method to do so.
How is this bad boy licensed?
GPL. Do with it what you will!
OMG! Don’t you know about XYZ plugin? It totally does this, but better!
Nope! But I’d like to!

Download, with Examples

I’ve gone ahead and zipped up some stuff for you:

  • a functions.php file with the function in it (add it to yours)
  • Several example files in a post_templates directory
  • Example.php with several calls to the function

Download ZIP

Welcome, Megan!

Monday, July 6th, 2009

We’re excited to welcome Megan Notarte (@megnotarte) to Cloud Four. Megan has extensive experience as both a project manager and developer, most recently as the IT Manager at Oregon Real Estate Forms.

Megan will be helping us mostly in a project management role, but we will be putting her development skills through the paces, too.

We feel fortunate to be growing during these challenging economic times–and we feel particularly lucky to have someone of Megan’s caliber joining us. We think she’s going to contribute a lot to our company right away, but we also expect her to help shape what comes next for Cloud Four.

Cloud Five?

During the hiring process, the most common question we got asked was whether or not we would change our name to Cloud Five.

The simple answer is no.

We picked Cloud Four not because of the four founders, but instead because we wanted a name that represented our values and our roots.

We wanted a name that conveyed transparency and openness. A cloud has those characteristics while also being something with substance. And as Portlanders, we see our fair share of clouds.

Those values and characteristics are not changing as we grow. The qualities of the name that originally appealed to us remain true no matter how many people we have in the company.

We’re excited about what comes next and we’re very happy to have Megan as part of our team.

Make Your Blog Faster & Help Save the Environment

Thursday, June 5th, 2008

For those in the Portland area, I’m going to be leading an informal session at this week’s Beer and Blog on how to optimize your blog for performance and why doing so is good for the environment.

More on how performance is connected to the environment in a moment, but first, the details on when and where:

Beer and Blog – Make Your Blog Load Fast & Save the Environment
Green Dragon Bistro & Brewpub
928 SE 9th Ave.
Portland, Oregon 97214
RSVP | Add to Calendar | Map

The Impact of Data Centers on the Environment

HP Efficient Computing at SNWEveryone is aware that our ability to reduce our energy consumption is a key component to reducing the impact we have on the environment.

However, it wasn’t until I was asked to speak to the Storage Networking Industry Association (SNIA) at Storage Networking World last fall that I realized how important this is for those who run data centers.

At the time, I observed:

Several businesses are now being told that they cannot bring any more power into their data centers. The power company is simply refusing to provide them with more capacity.

In many cases, the cost of powering and cooling a data center exceed the costs of the hardware within two years.

The people in the storage industry get it. The SNIA has started the Green Storage Initiative to foster innovative ways of reducing power consumption in data centers.

The question is are those of us who are web developers doing our part? Sadly, no.

Web Site Gluttons

Web developers live insulated lives. We have broadband at our homes and our offices and it shows. We’ve stopped paying attention to the size of web pages.

Andrew King recently highlighted research showing that the average size of web pages has doubled since 2003. “Longer term statistics show that since 1995 the size of the average web page has increased by 22 times, and the number of objects per page has grown by 21.7 times.”

Growth of Average Web Page Size and Number of Objects

Source: http://www.websiteoptimization.com/speed/tweak/average-web-page/

In addition to the size of web pages, few sites are optimized to ensure that browsers cache the files and don’t request the same files repeatedly.

Steve Souders recently conducted a thought experiment on how much CO2 would be saved by optimizing the Wikipedia home page. He used the CO2stats.com estimate that “three minutes on a Web site generates three grams of CO2 – roughly equal to the amount one person generates by breathing for 4.5 minutes.”

Steve estimated that the optimization could save “5,000 kilowatt-hours per year or approximately 500-1000 pounds of CO2 emissions.”

Why Now?

I’ve been interested in web optimization for ever since it saved my prior company tens of thousands of dollars a few years ago. I’ve been speaking on the topic whenever possible and trying to help web developers understand how much benefit they can get by optimizing their pages and how easy it is to do.

A couple of weeks ago, I finally watched An Inconvenient Truth. At the conclusion of the film I started thinking about what more I could do. I’m getting a bike ready to use for my commute to work. I’m reducing the energy I use. I’m contemplating a vegetarian diet.

And I’ve redoubled my commitment to getting web developers to take site performance seriously. It saves money, improves the experience for your customers, and it’s easy to do.

Most importantly, if you care about global warming, optimizing your site is a moral imperative.

So come join us tomorrow at the Green Dragon to make your blog faster and reduce the energy it consumes.

Akismet Numbers Paint Picture of Comment Spam

Thursday, November 15th, 2007

I knew comment spam on blogs was a problem, but I didn’t know quite how much of one until I happened across Akismet’s Zeitgeist page recently.

Akismet is a comment spam protection service that is free to use for non-commercial use and integrated with popular blog software (e.g. WordPress, which we use at Cloud Four).

But I never knew the true numbers before: of the 3.67 billion comments Akismet has seen, only about 295 million are legitimate. That means only about 8% of all blog comments filtered through Akismet are legitimate. That’s kind of grim.

It makes me realize that having Akismet activated for my blog isn’t just a nice-to-have, but a must.