Skip to content

Commit bb2892f

Browse files
committed
jQuery 3 compatibility for history navigation
jQuery 3 doesn't support the `selector` property on jQuery objects anymore. Instead, this refactors pjax options handling to always expect that `container` is explicitly specified as a string selector.
1 parent db26150 commit bb2892f

File tree

3 files changed

+46
-112
lines changed

3 files changed

+46
-112
lines changed

jquery.pjax.js

Lines changed: 35 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
// pjax specific options:
2020
//
2121
//
22-
// container - Where to stick the response body. Usually a String selector.
23-
// $(container).html(xhr.responseBody)
24-
// (default: current jquery context)
22+
// container - String selector for the element where to place the response body.
2523
// push - Whether to pushState the URL. Defaults to true (of course).
2624
// replace - Want to use replaceState instead? That's cool.
2725
//
@@ -30,11 +28,13 @@
3028
//
3129
// Returns the jQuery object
3230
function fnPjax(selector, container, options) {
33-
var context = this
31+
options = optionsFor(container, options)
3432
return this.on('click.pjax', selector, function(event) {
35-
var opts = $.extend({}, optionsFor(container, options))
36-
if (!opts.container)
37-
opts.container = $(this).attr('data-pjax') || context
33+
var opts = options
34+
if (!opts.container) {
35+
opts = $.extend({}, options)
36+
opts.container = $(this).attr('data-pjax')
37+
}
3838
handleClick(event, opts)
3939
})
4040
}
@@ -52,11 +52,6 @@ function fnPjax(selector, container, options) {
5252
// // is the same as
5353
// $(document).pjax('a')
5454
//
55-
// $(document).on('click', 'a', function(event) {
56-
// var container = $(this).closest('[data-pjax-container]')
57-
// $.pjax.click(event, container)
58-
// })
59-
//
6055
// Returns nothing.
6156
function handleClick(event, container, options) {
6257
options = optionsFor(container, options)
@@ -110,8 +105,7 @@ function handleClick(event, container, options) {
110105
// Examples
111106
//
112107
// $(document).on('submit', 'form', function(event) {
113-
// var container = $(this).closest('[data-pjax-container]')
114-
// $.pjax.submit(event, container)
108+
// $.pjax.submit(event, '[data-pjax-container]')
115109
// })
116110
//
117111
// Returns nothing.
@@ -158,8 +152,7 @@ function handleSubmit(event, container, options) {
158152
//
159153
// Accepts these extra keys:
160154
//
161-
// container - Where to stick the response body.
162-
// $(container).html(xhr.responseBody)
155+
// container - String selector for where to stick the response body.
163156
// push - Whether to pushState the URL. Defaults to true (of course).
164157
// replace - Want to use replaceState instead? That's cool.
165158
//
@@ -176,26 +169,31 @@ function pjax(options) {
176169
options.url = options.url()
177170
}
178171

179-
var target = options.target
180-
181172
var hash = parseURL(options.url).hash
182173

183-
var context = options.context = findContainerFor(options.container)
174+
var containerType = $.type(options.container)
175+
if (containerType !== 'string') {
176+
throw "expected string value for 'container' option; got " + containerType
177+
}
178+
var context = options.context = $(options.container)
179+
if (!context.length) {
180+
throw "the container selector '" + options.container + "' did not match anything"
181+
}
184182

185183
// We want the browser to maintain two separate internal caches: one
186184
// for pjax'd partial page loads and one for normal page loads.
187185
// Without adding this secret parameter, some browsers will often
188186
// confuse the two.
189187
if (!options.data) options.data = {}
190188
if ($.isArray(options.data)) {
191-
options.data.push({name: '_pjax', value: context.selector})
189+
options.data.push({name: '_pjax', value: options.container})
192190
} else {
193-
options.data._pjax = context.selector
191+
options.data._pjax = options.container
194192
}
195193

196194
function fire(type, args, props) {
197195
if (!props) props = {}
198-
props.relatedTarget = target
196+
props.relatedTarget = options.target
199197
var event = $.Event(type, props)
200198
context.trigger(event, args)
201199
return !event.isDefaultPrevented()
@@ -211,7 +209,7 @@ function pjax(options) {
211209
}
212210

213211
xhr.setRequestHeader('X-PJAX', 'true')
214-
xhr.setRequestHeader('X-PJAX-Container', context.selector)
212+
xhr.setRequestHeader('X-PJAX-Container', options.container)
215213

216214
if (!fire('pjax:beforeSend', [xhr, settings]))
217215
return false
@@ -284,7 +282,7 @@ function pjax(options) {
284282
id: options.id || uniqueId(),
285283
url: container.url,
286284
title: container.title,
287-
container: context.selector,
285+
container: options.container,
288286
fragment: options.fragment,
289287
timeout: options.timeout
290288
}
@@ -347,7 +345,7 @@ function pjax(options) {
347345
id: uniqueId(),
348346
url: window.location.href,
349347
title: document.title,
350-
container: context.selector,
348+
container: options.container,
351349
fragment: options.fragment,
352350
timeout: options.timeout
353351
}
@@ -363,7 +361,7 @@ function pjax(options) {
363361
if (xhr.readyState > 0) {
364362
if (options.push && !options.replace) {
365363
// Cache current container element before replacing it
366-
cachePush(pjax.state.id, cloneContents(context))
364+
cachePush(pjax.state.id, [options.container, cloneContents(context)])
367365

368366
window.history.pushState(null, "", options.requestUrl)
369367
}
@@ -448,13 +446,14 @@ function onPjaxPopstate(event) {
448446
}
449447

450448
var cache = cacheMapping[state.id] || []
451-
var container = $(cache[0] || state.container), contents = cache[1]
449+
var containerSelector = cache[0] || state.container
450+
var container = $(containerSelector), contents = cache[1]
452451

453452
if (container.length) {
454453
if (previousState) {
455454
// Cache current container before replacement and inform the
456455
// cache which direction the history shifted.
457-
cachePop(direction, previousState.id, cloneContents(container))
456+
cachePop(direction, previousState.id, [containerSelector, cloneContents(container)])
458457
}
459458

460459
var popstateEvent = $.Event('pjax:popstate', {
@@ -466,7 +465,7 @@ function onPjaxPopstate(event) {
466465
var options = {
467466
id: state.id,
468467
url: state.url,
469-
container: container,
468+
container: containerSelector,
470469
push: false,
471470
fragment: state.fragment,
472471
timeout: state.timeout,
@@ -568,7 +567,7 @@ function cloneContents(container) {
568567
cloned.find('script').each(function(){
569568
if (!this.src) jQuery._data(this, 'globalEval', false)
570569
})
571-
return [container.selector, cloned.contents()]
570+
return cloned.contents()
572571
}
573572

574573
// Internal: Strip internal query params from parsed URL.
@@ -618,49 +617,14 @@ function stripHash(location) {
618617
//
619618
// Returns options Object.
620619
function optionsFor(container, options) {
621-
// Both container and options
622-
if ( container && options )
620+
if (container && options) {
621+
options = $.extend({}, options)
623622
options.container = container
624-
625-
// First argument is options Object
626-
else if ( $.isPlainObject(container) )
627-
options = container
628-
629-
// Only container
630-
else
631-
options = {container: container}
632-
633-
// Find and validate container
634-
if (options.container)
635-
options.container = findContainerFor(options.container)
636-
637-
return options
638-
}
639-
640-
// Internal: Find container element for a variety of inputs.
641-
//
642-
// Because we can't persist elements using the history API, we must be
643-
// able to find a String selector that will consistently find the Element.
644-
//
645-
// container - A selector String or jQuery object.
646-
//
647-
// Returns a jQuery object whose context is `document` and has a selector.
648-
function findContainerFor(container) {
649-
var formatedContainer
650-
651-
if (jQuery.type(container) === 'string') {
652-
formatedContainer = $(container)
653-
formatedContainer.selector = container
654-
} else {
655-
formatedContainer = container
656-
}
657-
658-
if (!formatedContainer.length) {
659-
throw "no pjax container for " + container
660-
} else if (!formatedContainer.selector) {
661-
throw "cant get selector for pjax container"
623+
return options
624+
} else if ($.isPlainObject(container)) {
625+
return container
662626
} else {
663-
return formatedContainer
627+
return {container: container}
664628
}
665629
}
666630

test/unit/fn_pjax.js

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if ($.support.pjax) {
1919
asyncTest("pushes new url", function() {
2020
var frame = this.frame
2121

22-
frame.$("#main").pjax("a").on("pjax:end", function() {
22+
frame.$("#main").pjax("a", "#main").on("pjax:end", function() {
2323
equal(frame.location.pathname, "/dinosaurs.html")
2424
start()
2525
})
@@ -30,7 +30,7 @@ if ($.support.pjax) {
3030
asyncTest("replaces container html from response data", function() {
3131
var frame = this.frame
3232

33-
frame.$("#main").pjax("a").on("pjax:end", function() {
33+
frame.$("#main").pjax("a", "#main").on("pjax:end", function() {
3434
equal(frame.$("iframe").attr('title'), "YouTube video player")
3535
start()
3636
})
@@ -41,30 +41,18 @@ if ($.support.pjax) {
4141
asyncTest("sets title to response title tag", function() {
4242
var frame = this.frame
4343

44-
frame.$("#main").pjax("a").on("pjax:end", function() {
44+
frame.$("#main").pjax("a", "#main").on("pjax:end", function() {
4545
equal(frame.document.title, "dinosaurs")
4646
start()
4747
})
4848

4949
frame.$("a[href='/dinosaurs.html']").trigger('click')
5050
})
5151

52-
53-
asyncTest("uses second argument as container", function() {
54-
var frame = this.frame
55-
56-
frame.$("body").pjax("a", "#main").on("pjax:end", "#main", function() {
57-
equal(frame.location.pathname, "/dinosaurs.html")
58-
start()
59-
})
60-
61-
frame.$("a[href='/dinosaurs.html']").click()
62-
})
63-
6452
asyncTest("uses second argument as options", function() {
6553
var frame = this.frame
6654

67-
frame.$("#main").pjax("a", {push: true}).on("pjax:end", function() {
55+
frame.$("#main").pjax("a", {container: "#main", push: true}).on("pjax:end", function() {
6856
equal(frame.location.pathname, "/dinosaurs.html")
6957
start()
7058
})
@@ -102,7 +90,7 @@ if ($.support.pjax) {
10290
asyncTest("sets relatedTarget to clicked element", function() {
10391
var frame = this.frame
10492

105-
frame.$("#main").pjax("a")
93+
frame.$("#main").pjax("a", "#main")
10694

10795
var link = frame.$("a[href='/dinosaurs.html']")[0]
10896

@@ -118,7 +106,7 @@ if ($.support.pjax) {
118106
asyncTest("doesn't ignore left click", function() {
119107
var frame = this.frame
120108

121-
frame.$("#main").pjax("a")
109+
frame.$("#main").pjax("a", "#main")
122110

123111
var event = frame.$.Event('click')
124112
event.which = 0
@@ -131,7 +119,7 @@ if ($.support.pjax) {
131119
asyncTest("ignores middle clicks", function() {
132120
var frame = this.frame
133121

134-
frame.$("#main").pjax("a")
122+
frame.$("#main").pjax("a", "#main")
135123

136124
var event = frame.$.Event('click')
137125
event.which = 3
@@ -233,9 +221,9 @@ if ($.support.pjax) {
233221
asyncTest("triggers pjax:click event from link", function() {
234222
var frame = this.frame
235223

236-
frame.$("#main").pjax("a").on("pjax:click", function(event, options) {
224+
frame.$("#main").pjax("a", "#main").on("pjax:click", function(event, options) {
237225
ok(event)
238-
ok(options.container.is('#main'))
226+
equal(options.container, "#main")
239227
ok(options.url.match("/dinosaurs.html"))
240228
start()
241229
})
@@ -246,9 +234,9 @@ if ($.support.pjax) {
246234
asyncTest("triggers pjax:clicked event from link", function() {
247235
var frame = this.frame
248236

249-
frame.$("#main").pjax("a").on("pjax:clicked", function(event, options) {
237+
frame.$("#main").pjax("a", "#main").on("pjax:clicked", function(event, options) {
250238
ok(event)
251-
ok(options.container.is('#main'))
239+
equal(options.container, "#main")
252240
ok(options.url.match("/dinosaurs.html"))
253241
start()
254242
})

test/unit/pjax.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,24 +108,6 @@ if ($.support.pjax) {
108108
})
109109
})
110110

111-
asyncTest("container option accepts jQuery object", 1, function() {
112-
var container = this.frame.$("#main")
113-
114-
navigate(this.frame)
115-
.pjax({ url: "hello.html", container: container }, function(frame) {
116-
equal(frame.$("#main > p").html().trim(), "Hello!")
117-
})
118-
})
119-
120-
asyncTest("container option accepts DOM element with ID", 1, function() {
121-
var container = this.frame.document.getElementById("main")
122-
123-
navigate(this.frame)
124-
.pjax({ url: "hello.html", container: container }, function(frame) {
125-
equal(frame.$("#main > p").html().trim(), "Hello!")
126-
})
127-
})
128-
129111
asyncTest("url option accepts function", 2, function() {
130112
var numCalls = 0
131113
var url = function() {

0 commit comments

Comments
 (0)