Skip to content
Browse files

pjax

  • Loading branch information...
0 parents commit 3efcc3c968c18c4deeb27a1bab1b7306ca4b6e99 @defunkt committed
Showing with 279 additions and 0 deletions.
  1. +20 −0 LICENSE
  2. +117 −0 README
  3. +142 −0 jquery.pjax.js
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) Chris Wanstrath
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+Software), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
117 README
@@ -0,0 +1,117 @@
+## pushState + ajax = pjax
+
+ .--.
+ / \
+ ## a a
+ ( '._)
+ |'-- |
+ _.\___/_ ___pjax___
+ ."\> \Y/|<'. '._.-'
+ / \ \_\/ / '-' /
+ | --'\_/|/ | _/
+ |___.-' | |`'`
+ | | |
+ | / './
+ /__./` | |
+ \ | |
+ \ | |
+ ; | |
+ / | |
+ jgs |___\_.\_
+ `-"--'---'
+
+
+## what is it?
+
+pjax loads content from your server into an existing page
+without reloading that entire page while ensuring permalinks,
+titles, and the back button work as expected. It enhances the
+browsing experience of your users - nothing more.
+
+
+## three ways to pjax on the client side:
+
+1. Functionally obtrusive, loading the href with ajax into data-pjax:
+
+ <a href='/explore' data-pjax='#main'>Explore</a>
+
+ $('a[data-pjax]').pjax()
+
+
+2. Slightly obtrusive, passing a container and jQuery ajax options:
+
+ <a href='/explore' class='js-pjax'>Explore</a>
+
+ $('a.js-pjax').pjax('#main', { error: function(){
+ $('.error').text('Something went wrong!')
+ })
+
+
+3. Unobtrusive, using the pjax 'loading' callback:
+
+ <div id='main'>
+ <div class='loader' style='display:none'><img src='spin.gif'></div>
+ <div class='tabs'>
+ <a href='/explore'>Explore</a>
+ <a href='/help'>Help</a>
+ </div>
+ </div>
+
+ $('a').pjax('#main', { loading: function(){
+ this == the target container == #main
+ $(this).find('.loader').show()
+ })
+
+
+## $(link).pjax( container, options )
+
+The $(link).pjax() function accepts a container, an options object,
+or both. The options are the same as jQuery's ajax options with the
+following additions:
+
+container - The selector of the container to load the reponse body into, or
+ the container itself.
+ push - Whether to pushState the URL. Defaults to true (of course).
+ loading - A callback to fire after it's been too many ms and
+ you want to ease the user's pain with a loading indicator.
+ You can also bind to the 'loading.pjax' event on container.
+
+
+## $.pjax( options )
+
+You can also just call $.pjax directly. It acts much like $.ajax, even
+returning the same thing and accepting the same options.
+
+The pjax-specific keys listed in the $(link).pjax() section work here
+as well.
+
+This pjax call:
+
+ $.pjax({
+ url: '/authors',
+ container: '#main'
+ })
+
+Roughly translates into this ajax call:
+
+ $.ajax({
+ url: '/authors?pjax=true',
+ dataType: 'html',
+ success: function(data){
+ $('#main').html( data )
+ history.pushState( null, $(data).filter('title').text(), '/authors' )
+ })
+ })
+
+
+## install it
+
+ $ cd path/to/js
+ $ curl -O https://github.com/defunkt/jquery-pjax/raw/master/jquery.pjax.js
+
+Then, in your HTML:
+
+ <script src="path/to/js/jquery.pjax.js"></script>
+
+Replace 'path/to/js' with the path to your JavaScript directory,
+e.g. 'public/javascripts'.
142 jquery.pjax.js
@@ -0,0 +1,142 @@
+// When called on a link, fetches the href with ajax into the
+// container specified as the first parameter or with the data-pjax
+// attribute on the link itself.
+//
+// Tries to make sure the back button and ctrl+click work the way
+// you'd expect.
+//
+// Accepts a jQuery ajax options object that may include these
+// pjax specific options:
+//
+// container - Where to stick the response body. Usually a String selector.
+// $(container).html(xhr.responseBody)
+// loading - A callback to fire after it's been too many ms and
+// you want to ease the user's pain with a loading indicator.
+// You can also bind to the 'loading.pjax' event on container.
+// push - Whether to pushState the URL. Defaults to true (of course).
+//
+// For convenience the first parameter can be either the container or
+// the options object.
+//
+// Returns the jQuery object
+jQuery.fn.pjax = function( container, options ) {
+ var $ = jQuery
+
+ if ( options )
+ options.container = container
+ else
+ options = container
+
+ $(this).live('click', function(){
+ // Middle click, cmd click, and ctrl click should open
+ // links in a new tab as normal.
+ if ( event.which == 2 || event.metaKey )
+ return true
+
+ var defaults = {
+ url: this.href,
+ container: $(this).attr('data-pjax')
+ }
+
+ $.pjax( $.extend({}, defaults, options) )
+
+ return false
+ })
+}
+
+
+// Loads a URL with ajax, puts the response body inside a container,
+// then pushState()'s the loaded URL. Also ensures the back button
+// works the way you'd expect.
+//
+// Works just like $.ajax in that it accepts a jQuery ajax
+// settings object (with keys like url, type, data, etc).
+//
+// Accepts these extra keys:
+//
+// container - Where to stick the response body.
+// $(container).html(xhr.responseBody)
+// loading - A callback to fire after it's been too many ms and
+// you want to ease the user's pain with a loading indicator.
+// You can also bind to the 'loading.pjax' event on container.
+// push - Whether to pushState the URL. Defaults to true (of course).
+//
+// Use it just like $.ajax:
+//
+// var xhr = $.pjax({ url: this.href, container: '#main' })
+// console.log( xhr.readyState )
+//
+// Returns whatever $.ajax returns.
+jQuery.pjax = function( options ) {
+ // Helper
+ var $ = jQuery
+
+ var defaults = {
+ data: { pjax: true },
+ type: 'GET',
+ dataType: 'html',
+ error: function(){ window.location = options.url },
+ success: function(data){
+ // If we got no data or an entire web page, go directly
+ // to the page and let normal error handling happen.
+ if ( !$.trim(data) || /<html/i.test(data) )
+ return window.location = options.url
+
+ // If there's a <title> tag in the response, use it as
+ // the page's title.
+ var title = $.trim( $(data).filter('title').remove().text() )
+ if ( title ) document.title = title
+
+ // Make it happen.
+ $(options.container).html( data )
+
+ // If they didn't explicitly disable `push`, call pushState()
+ if ( options.push !== false ) {
+ window.history.pushState( { pjax: options.container },
+ document.title, options.url )
+ }
+
+ // Invoke their success handler if they gave us one.
+ success.apply( this, arguments )
+ }
+ }
+
+ // We don't want to let anyone override our success handler.
+ var success = options.success || $.noop
+ delete options.success
+
+ options = $.extend( true, {}, defaults, options )
+ var xhr = $.ajax( options )
+
+ // If we haven't found what we're looking for after a buncha ms
+ // you might want to show a 'Loading...' indicator.
+ setTimeout(function(){
+ if ( xhr.readyState == 4 ) return
+ $(options.container).trigger('loading.pjax')
+ if ( options.loading ) options.loading.call( options.container )
+ }, 350)
+
+ $(document).trigger('pjax', xhr, options)
+ return xhr
+}
+
+// Bind our popstate handler which takes care of the back and
+// forward buttons, but only once we've called pjax()
+jQuery(document).one('pjax', function(){
+ jQuery(window).bind('popstate', function(event){
+ var state = event.state
+ if ( state && state.pjax )
+ jQuery.pjax({ url: location.href, container: state.pjax, push: false })
+ else
+ window.location = location.href
+ })
+})
+
+// Add the state property to jQuery's event object so we can use it in
+// $(window).bind('popstate')
+jQuery.event.props.push('state')
@wjlroe
wjlroe added a note

This line doesn't work when using Zepto (instead of jQuery). How difficult do you think it would be to make PJAX compatible with Zepto?
https://github.com/madrobby/zepto
Cheers

@defunkt Owner
defunkt added a note

pjax is still pretty experimental. I'd wait a few months until it gets more stable before porting it to different frameworks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+// Fall back to normalcy for older browsers.
+if ( !window.history || !window.history.pushState ) {
+ jQuery.pjax = jQuery.fn.pjax = $.noop
+}

0 comments on commit 3efcc3c

Please sign in to comment.
Something went wrong with that request. Please try again.