
Chris Coyier has made a wonderful screencast about AJAXing WordPress theme. To AJAXify WordPress theme, he uses jQuery to make any internal link on the site will load into the main content area without requiring a page refresh, including search when the link is clicked. Althought the script works fine, it has 2 disadvantages: the internal links’ href property are changed (hashized) and the back/forward buttons are not supported. So I decided to improve the script to make the AJAX functionality better.
I use a blank Thematic child theme as my test theme. Just download Thematic theme and put it in theme folder (you don’t need to activate it). It contains a folder named thematicsamplechildtheme which is a sample blank child theme for Thematic. Copy it into themes folder and rename it to thematic-child, this is the theme that we will work on.
Step 1. Create needed javascript files
To support back/forward buttons while using AJAX, I’ll use the jQuery hashchange event plugin by Ben Alman, you can grab it here. After downloading, create a folder js inside theme folder (which is thematic-child/js in my test) and move the javascript file into it.
We will use a custom javascript code, so make a blank javascript file called ajax.js in the folder thematic-child/js. Our AJAX code will be put in this file.
Step 2. Register scripts in WordPress theme
To ajaxify WordPress theme, first we need to include javascript files above into our theme by using function wp_enqueue_script(). Put the following code into the functions.php file:
if (!is_admin()) {
wp_enqueue_script('hash-change', get_stylesheet_directory_uri() . '/js/jquery.ba-hashchange.min.js', array('jquery'), false, true);
wp_enqueue_script('ajax-theme', get_stylesheet_directory_uri() . '/js/ajax.js', array('jquery', 'hash-change'), false, true);
}
The first line will register the hash-change script, which needs jQuery library. The second line will register our AJAX script (which I call ajax-theme), it needs jQuery and hash-change scripts. Both scripts are located in the footer for better performance.
For more information about wp_enqueue_script(), please read the Codex.
Step 3. Implement AJAX code
3.1. Get site url and place holder
Now open the ajax.js file inside thematic-child/js folder. We start our code with:
jQuery(document).ready(function($) {
var $mainContent = $("#container"),
siteUrl = "http://" + top.location.host.toString(),
url = '';
// more code here
}); Note that jQuery need to be used in noConflict mode, so I pass the $ symbol as a function’s argument to make sure it can be used inside function (for ease, of course).
We get the place holder of our main content, which is #container in Thematic. It might be different if you use another theme. To know this section, you can use Firebug to find as in the following screenshot:
To get internal links, we need to know our site url, which is stored in siteUrl variable.
3.2. Add event listener to internal links’ click
After that, we will add an event to internal links’ click, which will change the url’s hash:
$(document).delegate("a[href^='"+siteUrl+"']:not([href*=/wp-admin/]):not([href*=/wp-login.php]):not([href$=/feed/])", "click", function() {
location.hash = this.pathname;
return false;
});
The selector looks so complicated, doesn’t it? Don’t worry, it has the following parts:
a[href^='"+siteUrl+"']: which means all links that begin with our site’s url (internal links):not([href*=/wp-admin/]): the links don’t contain /wp-admin/ string (the links in admin dashboard). This might be the Edit post link or link to user’s profile in comment form:not([href*=/wp-login.php]): the login/logout link, of course we don’t want to load content via ajax from these pages:not([href$=/feed/]): and the last is feed links. This maybe the content feed link, comment feed link of whole blog or particular post. We don’t want to load content via ajax from these pages, too
So, when we click on an internal link, which is not admin link, login link or feed link, we’ll change the url’s hash to link’s pathname. For example, if our link is http://localhost/wp/a-paginated-post/, then when it’s clicked, the location will be http://localhost/wp/#/wp/a-paginated-post/.
The return function prevent browser to go to original link.
In this script, I use the delegate() function to add event listener to click event. This method is implemented in jQuery 1.4 and is recommended instead of using live() function. Unfortunately jQuery 1.4 is included only since WordPress 3.0, so if you’re using WordPress 2.9.x and ealier, use the live() method instead of:
$("a[href^='"+siteUrl+"']:not([href*=/wp-admin/]):not([href*=/wp-login.php]):not([href$=/feed/])").live("click", function() {
location.hash = this.pathname;
return false;
});Note that the href attribute of internal links are not modified. This solves the first problem in Chris Coyer’s code.
3.3. Add event listener to search form
Adding event listener to search form when it’s submitted is very similar to adding event listener to internal link’s click:
$("#searchform").submit(function(e) {
location.hash = '?s=' + $("#s").val();
e.preventDefault();
});
When search form is submitted, we put the search parameter and search query into url’s hash. Then prevent the default action to not redirect to search result page.
3.4. Load content when hash changed
When url’s hash is changed, we will get the new content and put it in the #container section:
$(window).bind('hashchange', function(){
url = window.location.hash.substring(1);
if (!url) {
return;
}
url = url + " #content";
$mainContent.animate({opacity: "0.1"}).html('<p>Please wait...</>').load(url, function() {
$mainContent.animate({opacity: "1"});
});
});
First, we get the current hash. If it’s empty, then do nothing. Else, we create the needed url to get content. The url needs to be added #content because we don’t want to get all page content, but only the #content section. The #content section in Thematic is the child element of #container (the only child), so when we get it, we can immediately put it inside #container.
If you use another theme, check the id of the content section with Firebug as I mentioned above and change #content to proper value.
We use the opacity animation to fade out the content, then put a “Please wait…” string to tell users that the page’s being loaded. When the page has been loaded, we just animate the opacity to 1 to show the content.
This code is simple, but it works beautifully. Because we use the hash-change library, the script works well with back/forward buttons. This solved the second problem in Chris Coyer’s code.
3.5. Wait! When page first loads
When the blog first loads, if the location is the home page (for example http://localhost/wp/), then our code works fine. But after some times you navigate through the blog and get a location of a blog post: http://localhost/wp/#/wp/a-paginated-post/, you paste it into a new tab and run. What happens? The browser still loads the homepage, not the specified page we want!
To solve this problem, just use the following code:
$(window).trigger('hashchange');This will check the url’s hash. If it’s changed (i.e. different from default value), then the hash-change event is fired, and we get exactly the content we want.
So, our final javascript code is:
jQuery(document).ready(function($) {
var $mainContent = $("#container"),
siteUrl = "http://" + top.location.host.toString(),
url = '';
$(document).delegate("a[href^='"+siteUrl+"']:not([href*=/wp-admin/]):not([href*=/wp-login.php]):not([href$=/feed/])", "click", function() {
location.hash = this.pathname;
return false;
});
$("#searchform").submit(function(e) {
location.hash = '?s=' + $("#s").val();
e.preventDefault();
});
$(window).bind('hashchange', function(){
url = window.location.hash.substring(1);
if (!url) {
return;
}
url = url + " #content";
$mainContent.animate({opacity: "0.1"}).html('<p>Please wait...</>').load(url, function() {
$mainContent.animate({opacity: "1"});
});
});
$(window).trigger('hashchange');
}); Save it into ajax.js file inside thematic-child/js folder. Refresh the browser to see how it works.
You can check the demo version or get the source code of javascript file in the following links:
The script can be changed easily to fit your WordPress theme, and now you already have an AJAXed WordPress theme. I hope this tutorial is useful for you when implement AJAX functionality in your WordPress blog. If you have some questions or suggestions, please leave me a comment.



40 comments
Thanks for this code. I’ve tried an it works fine…except it breaks my Lytebox-like popup images :-(
Oh I think you should change the jQuery selector for links that shouldn’t be ajax (like your popup images). This is different on each blog so I can’t tell you exactly how to do it.
But could you give an example? This might help all of the people on here that are asking the same question. I.e with a link that is image here how would you add a selector or whatever to allow the lightbox script to popup?
Oups I forgot this line :(
Thanks!
I use fancy box for my images…it works when you first load any page, but if you then navigation and load a new page, it no longer works.
cant get this to work.. :S
atm the url changes correctly but it isnt loaded into the div, im quite sure those names are correct tho. am i supposed to have a loop inside the div where content is placed or should that be empty ?
works well on WP3.0, on WP3.2.1 doesn’t work :/
thanks, this is great.
however, i got two problems: nextgengallery doesn’t work and all audio players don’t work. what i tried: excluding mp3 and jpg links via :not([href*=.mp3]):not([href*=.jpg]). this helps only in that jpgs now open in the same browser window, but the thickbox of nextgengallery doesn’t work, and mp3 links or [audio] embeddings still don’t work.
also only delegating links from my navigation bar via “$(document).delegate(“#menubar a”, “click”, function() {” does NOT help with the gallery and audio player, although they obviously occur in a different div, not in #menubar.
any idea what i could do?
i found out, that i case of the mediaplayer the flash somehow isn’t loaded. so it’s not an issue concerning the link (because it isn’t clicked yet) but something else. any idea anyone?
Your links (demo and source) is broken :-(
Yes, I haven’t used that domain any more. I’ll try to set up a subdomain for demos.
can anybody confirm whether the script works in WP 3.2.1 or not?
I have this script working in WordPress 3.3
I would love to see a demo of this but the link doesn’t work.
[...] been following this tutorial, but clicking a link only reloads the frontpage. It has the nice fade-in animation, but after the [...]
The demo link isn’t working!
it works great, thank you very much for taking some time and setting this up, it might need some changes for different theme but the concept is beautiful, keep up the good work!!!
Does not work with 3.2.1
Will this be updated? Actually I liked what I saw but it was not loading when clicking the category links in the sidebar and not when using the “Back Home Button”. I am usin “Wostok”, yes with a W and not with a V.
Hi guys, me again.
I love this script and tutorial. It works beautifully where it works but somehow there is something within the code that at least works differently with wp 3.2.1. Here is what I tracked down.
Landing page is ok when loaded.
I can click a post and it fades in.
I can click a different page on the menu and it fades in.
If I click the “Home Button” (which is linked to the landing page), the text “Loading…” appears but nothing is loading.
Same happens if I click to a category that is listed in the sidebar (which is linked to the archive.
I noticed this:
The tutorial says that the link (created with the hashchange plugin) should look like this.
http://localhost/wp/#/wp/a-paginated-post/
but my link looks like this
http://localhost/wp/#wp/a-paginated-post/
Can you see the difference? The slash between # and wp is missing. as soon as I add this slash to the address, the content gets loaded.
Does anybody know what I could change on the script to make it work properly? I would appreciate your help very much because this here is the best ajax approach for wp that I found on the net.
I need your help please.
Trying to get this working in my theme, I copied over the js directory with both scripts into my theme. Added the if statement to my functions.php.
Now no of the links work. I do see the hash in the url.
I’m a n00b at coding for WordPress and I don’t know php. Only C and QuakeC.
I would appreciate any help. Thank you in advance, have a great weekend.
oh… i need your help to. would like to see the demo file… i´m working on my site but without much success… take a look: http:www.thousandrottenkings.com
I think u apply this technique in this comment box also. But the mail feature I want is not detected here. If a user does not fill Name and Email field, wordpress redirects to a new error page. I want to get rid of this redirection… It happens here too.
Probably a dim question but how can we add an ! after the hash eg
http://localhost/wp/#!/wp/a-paginated-post/
is true that on WP3.2.1 doesn’t work???
Doesn’t work on 3.2.1 for me… :(
I take that back, it does work just fine.
Mine works fine! Just, widgets doesn’t load… Any help on that?
Tnx…
Thanks for the tutorial. It works fine with wp 3.2.1 too. Is there a way to use this with multiple menus and containers? I tried to by changing $(document).delegate to the id of each menu, changing the variable mainContent in every version and container and content id but it shows me the please wait… in the right container but not the loaded conten is shown in the 1st container. Any idea how it will work?
Thanks.
Great it works for the most part. There is a problem when users click the submit button for Bbpress and contact forms.
The the ajax call keeps on loading loading loading.
Why did you delete my comment?
Hi Chris, I didn’t. I just haven’t approved it to show.
Hi! I have a problem with the hashtags and navigating to frontpage. I don’t use this method at the frontpage, I use it on subpage to show my work. Navigation to Work page works nice, because of this -> :not([href*=/work/]). But I cannot go to frontpage anymore, because I don’t know what to write to event listeners. Any thoughts for this?
Thanks!
Hi, all smartly works! )) But there was a problem! after receipt of the contents of scrolling does not work! how to make it work?
Thank you!
Hello,
Thank you for this tutorial. Is there a way to make it run for Internet Explorer 8 ?
That’s a great tutorial! You did a great job explaining each detail and I followed along with ease even though I’ve never tried AJAX before.
I was able to get it working, and even write a further conditional to only target pages that begin with a specific word. However, no matter what I have tried, the javascript and jQuery script do not execute on the page that’s loaded into the div container, I’ve tried placing the scripts in the actual container.
Any tips?
That would be great to maintain the scripts working. I haven’t tried yet, but will only use it if there’s a way…
:§
Anyone mirror this code?
Would like to give it a whirl in 3.3
Thanks
Works great for Chrome and Firefox but seems to be an issue with IE 9. The # is missing the trailing slash when loading it in IE, would anybody happen to know how to fix this issue? Thanks.
Actually figured out a simple fix for IE, just change the permalink in WordPress to custom and add a another / in the front. So mine is: /%/%postname%/ .
Hey. Thanks for a great tutorial.
I’m having a little problem. The first link in my menu keeps having the class “current_page_item” even though it is another pages content that is being shown.
Have I missed something?