Skip to content

Commit 3efcc3c

Browse files
committed
pjax
0 parents  commit 3efcc3c

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed

LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) Chris Wanstrath
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
Software), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
## pushState + ajax = pjax
2+
3+
.--.
4+
/ \
5+
## a a
6+
( '._)
7+
|'-- |
8+
_.\___/_ ___pjax___
9+
."\> \Y/|<'. '._.-'
10+
/ \ \_\/ / '-' /
11+
| --'\_/|/ | _/
12+
|___.-' | |`'`
13+
| | |
14+
| / './
15+
/__./` | |
16+
\ | |
17+
\ | |
18+
; | |
19+
/ | |
20+
jgs |___\_.\_
21+
`-"--'---'
22+
23+
24+
## what is it?
25+
26+
pjax loads content from your server into an existing page
27+
without reloading that entire page while ensuring permalinks,
28+
titles, and the back button work as expected. It enhances the
29+
browsing experience of your users - nothing more.
30+
31+
32+
## three ways to pjax on the client side:
33+
34+
1. Functionally obtrusive, loading the href with ajax into data-pjax:
35+
36+
<a href='/explore' data-pjax='#main'>Explore</a>
37+
38+
$('a[data-pjax]').pjax()
39+
40+
41+
2. Slightly obtrusive, passing a container and jQuery ajax options:
42+
43+
<a href='/explore' class='js-pjax'>Explore</a>
44+
45+
$('a.js-pjax').pjax('#main', { error: function(){
46+
$('.error').text('Something went wrong!')
47+
})
48+
49+
50+
3. Unobtrusive, using the pjax 'loading' callback:
51+
52+
<div id='main'>
53+
<div class='loader' style='display:none'><img src='spin.gif'></div>
54+
<div class='tabs'>
55+
<a href='/explore'>Explore</a>
56+
<a href='/help'>Help</a>
57+
</div>
58+
</div>
59+
60+
$('a').pjax('#main', { loading: function(){
61+
this == the target container == #main
62+
$(this).find('.loader').show()
63+
})
64+
65+
66+
## $(link).pjax( container, options )
67+
68+
The $(link).pjax() function accepts a container, an options object,
69+
or both. The options are the same as jQuery's ajax options with the
70+
following additions:
71+
72+
container - The selector of the container to load the reponse body into, or
73+
the container itself.
74+
push - Whether to pushState the URL. Defaults to true (of course).
75+
loading - A callback to fire after it's been too many ms and
76+
you want to ease the user's pain with a loading indicator.
77+
You can also bind to the 'loading.pjax' event on container.
78+
79+
80+
## $.pjax( options )
81+
82+
You can also just call $.pjax directly. It acts much like $.ajax, even
83+
returning the same thing and accepting the same options.
84+
85+
The pjax-specific keys listed in the $(link).pjax() section work here
86+
as well.
87+
88+
This pjax call:
89+
90+
$.pjax({
91+
url: '/authors',
92+
container: '#main'
93+
})
94+
95+
Roughly translates into this ajax call:
96+
97+
$.ajax({
98+
url: '/authors?pjax=true',
99+
dataType: 'html',
100+
success: function(data){
101+
$('#main').html( data )
102+
history.pushState( null, $(data).filter('title').text(), '/authors' )
103+
})
104+
})
105+
106+
107+
## install it
108+
109+
$ cd path/to/js
110+
$ curl -O https://github.com/defunkt/jquery-pjax/raw/master/jquery.pjax.js
111+
112+
Then, in your HTML:
113+
114+
<script src="path/to/js/jquery.pjax.js"></script>
115+
116+
Replace 'path/to/js' with the path to your JavaScript directory,
117+
e.g. 'public/javascripts'.

jquery.pjax.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// When called on a link, fetches the href with ajax into the
2+
// container specified as the first parameter or with the data-pjax
3+
// attribute on the link itself.
4+
//
5+
// Tries to make sure the back button and ctrl+click work the way
6+
// you'd expect.
7+
//
8+
// Accepts a jQuery ajax options object that may include these
9+
// pjax specific options:
10+
//
11+
// container - Where to stick the response body. Usually a String selector.
12+
// $(container).html(xhr.responseBody)
13+
// loading - A callback to fire after it's been too many ms and
14+
// you want to ease the user's pain with a loading indicator.
15+
// You can also bind to the 'loading.pjax' event on container.
16+
// push - Whether to pushState the URL. Defaults to true (of course).
17+
//
18+
// For convenience the first parameter can be either the container or
19+
// the options object.
20+
//
21+
// Returns the jQuery object
22+
jQuery.fn.pjax = function( container, options ) {
23+
var $ = jQuery
24+
25+
if ( options )
26+
options.container = container
27+
else
28+
options = container
29+
30+
$(this).live('click', function(){
31+
// Middle click, cmd click, and ctrl click should open
32+
// links in a new tab as normal.
33+
if ( event.which == 2 || event.metaKey )
34+
return true
35+
36+
var defaults = {
37+
url: this.href,
38+
container: $(this).attr('data-pjax')
39+
}
40+
41+
$.pjax( $.extend({}, defaults, options) )
42+
43+
return false
44+
})
45+
}
46+
47+
48+
// Loads a URL with ajax, puts the response body inside a container,
49+
// then pushState()'s the loaded URL. Also ensures the back button
50+
// works the way you'd expect.
51+
//
52+
// Works just like $.ajax in that it accepts a jQuery ajax
53+
// settings object (with keys like url, type, data, etc).
54+
//
55+
// Accepts these extra keys:
56+
//
57+
// container - Where to stick the response body.
58+
// $(container).html(xhr.responseBody)
59+
// loading - A callback to fire after it's been too many ms and
60+
// you want to ease the user's pain with a loading indicator.
61+
// You can also bind to the 'loading.pjax' event on container.
62+
// push - Whether to pushState the URL. Defaults to true (of course).
63+
//
64+
// Use it just like $.ajax:
65+
//
66+
// var xhr = $.pjax({ url: this.href, container: '#main' })
67+
// console.log( xhr.readyState )
68+
//
69+
// Returns whatever $.ajax returns.
70+
jQuery.pjax = function( options ) {
71+
// Helper
72+
var $ = jQuery
73+
74+
var defaults = {
75+
data: { pjax: true },
76+
type: 'GET',
77+
dataType: 'html',
78+
error: function(){ window.location = options.url },
79+
success: function(data){
80+
// If we got no data or an entire web page, go directly
81+
// to the page and let normal error handling happen.
82+
if ( !$.trim(data) || /<html/i.test(data) )
83+
return window.location = options.url
84+
85+
// If there's a <title> tag in the response, use it as
86+
// the page's title.
87+
var title = $.trim( $(data).filter('title').remove().text() )
88+
if ( title ) document.title = title
89+
90+
// Make it happen.
91+
$(options.container).html( data )
92+
93+
// If they didn't explicitly disable `push`, call pushState()
94+
if ( options.push !== false ) {
95+
window.history.pushState( { pjax: options.container },
96+
document.title, options.url )
97+
}
98+
99+
// Invoke their success handler if they gave us one.
100+
success.apply( this, arguments )
101+
}
102+
}
103+
104+
// We don't want to let anyone override our success handler.
105+
var success = options.success || $.noop
106+
delete options.success
107+
108+
options = $.extend( true, {}, defaults, options )
109+
var xhr = $.ajax( options )
110+
111+
// If we haven't found what we're looking for after a buncha ms
112+
// you might want to show a 'Loading...' indicator.
113+
setTimeout(function(){
114+
if ( xhr.readyState == 4 ) return
115+
$(options.container).trigger('loading.pjax')
116+
if ( options.loading ) options.loading.call( options.container )
117+
}, 350)
118+
119+
$(document).trigger('pjax', xhr, options)
120+
return xhr
121+
}
122+
123+
// Bind our popstate handler which takes care of the back and
124+
// forward buttons, but only once we've called pjax()
125+
jQuery(document).one('pjax', function(){
126+
jQuery(window).bind('popstate', function(event){
127+
var state = event.state
128+
if ( state && state.pjax )
129+
jQuery.pjax({ url: location.href, container: state.pjax, push: false })
130+
else
131+
window.location = location.href
132+
})
133+
})
134+
135+
// Add the state property to jQuery's event object so we can use it in
136+
// $(window).bind('popstate')
137+
jQuery.event.props.push('state')
138+
139+
// Fall back to normalcy for older browsers.
140+
if ( !window.history || !window.history.pushState ) {
141+
jQuery.pjax = jQuery.fn.pjax = $.noop
142+
}

0 commit comments

Comments
 (0)