Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions jquery.pjax.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,7 @@ function pjax(options) {
}

// Cancel the current request if we're already pjaxing
var xhr = pjax.xhr
if ( xhr && xhr.readyState < 4) {
xhr.onreadystatechange = $.noop
xhr.abort()
}
abortXHR(pjax.xhr)

pjax.options = options
var xhr = pjax.xhr = $.ajax(options)
Expand Down Expand Up @@ -402,6 +398,12 @@ if ('state' in window.history) {
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
function onPjaxPopstate(event) {

// Hitting back or forward should override any pending PJAX request.
if (!initialPop) {
abortXHR(pjax.xhr)
}

var state = event.state

if (state && state.container) {
Expand Down Expand Up @@ -504,6 +506,15 @@ function fallbackPjax(options) {
form.submit()
}

// Internal: Abort an XmlHttpRequest if it hasn't been completed,
// also removing its event handlers.
function abortXHR(xhr) {
if ( xhr && xhr.readyState < 4) {
xhr.onreadystatechange = $.noop
xhr.abort()
}
}

// Internal: Generate unique id for state object.
//
// Use a timestamp instead of a counter since ids should still be
Expand Down
40 changes: 40 additions & 0 deletions test/unit/pjax.js
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,46 @@ if ($.support.pjax) {
}, 0)
}

asyncTest("clicking back while loading cancels XHR", function() {
var frame = this.frame

frame.$('#main').on('pjax:timeout', function(event) {
event.preventDefault()
})

frame.$("#main").one('pjax:send', function() {

// Check that our request is aborted (need to check
// how robust this is across browsers)
frame.$("#main").one('pjax:complete', function(e, xhr, textStatus) {
equal(xhr.status, 0)
equal(textStatus, 'abort')
})

setTimeout(function() {
frame.history.back()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would only do back() after a some timeout, 500ms for example, and not immediately on pjax:send. This simulates how an actual user would do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

}, 250)

// Make sure the URL and content remain the same after the
// XHR would have arrived (delay on timeout.html is 1s)
setTimeout(function() {
var afterBackLocation = frame.location.pathname
var afterBackTitle = frame.document.title

setTimeout(function() {
equal(frame.location.pathname, afterBackLocation)
equal(frame.document.title, afterBackTitle)
start()
}, 1000)
}, 500)
})

frame.$.pjax({
url: "timeout.html",
container: "#main"
})
})

asyncTest("popstate going back to page", function() {
var frame = this.frame

Expand Down