From cb0a7cd850cc26d5100e6ee1ed295f0fbf59ebe0 Mon Sep 17 00:00:00 2001 From: Behind The Math Date: Sat, 20 Jan 2018 22:06:03 -0500 Subject: [PATCH] Move all code that executes after the switches to a new function If any switches are async, the subsequent code will execute before the switches are finished. This commit moves all that code to a new function, and debounces the calls to onSwitch() so it only executes once, after all the switches finish. Fizes #72. --- index.js | 122 ++++++++++++++++++++-------------- lib/switches-selectors.js | 3 + tests/lib/switch-selectors.js | 3 +- 3 files changed, 77 insertions(+), 51 deletions(-) diff --git a/index.js b/index.js index da4aa8f..9000766 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,11 @@ var defaultSwitches = require("./lib/switches") var Pjax = function(options) { this.firstrun = true + this.state = { + numPendingSwitches: 0, + href: null, + options: null + } var parseOptions = require("./lib/proto/parse-options.js"); parseOptions.apply(this,[options]) @@ -83,8 +88,12 @@ Pjax.prototype = { }, onSwitch: function() { - this.parseDOM(document) - trigger(window, "resize scroll") + this.state.numPendingSwitches-- + + // debounce calls, so we only run this once after all switches are finished. + if (this.state.numPendingSwitches === 0) { + this.afterAllSwitches() + } }, loadContent: function(html, options) { @@ -125,22 +134,6 @@ Pjax.prototype = { // try { this.switchSelectors(this.options.selectors, tmpEl, document, options) - // FF bug: Won’t autofocus fields that are inserted via JS. - // This behavior is incorrect. So if theres no current focus, autofocus - // the last field. - // - // http://www.w3.org/html/wg/drafts/html/master/forms.html - var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop() - if (autofocusEl && document.activeElement !== autofocusEl) { - autofocusEl.focus(); - } - - // execute scripts when DOM have been completely updated - this.options.selectors.forEach(function(selector) { - forEachEls(document.querySelectorAll(selector), function(el) { - executeScripts(el) - }) - }) // } // catch(e) { // if (this.options.debug) { @@ -181,6 +174,8 @@ Pjax.prototype = { else if (request.getResponseHeader("X-XHR-Redirected-To")) { href = request.getResponseHeader("X-XHR-Redirected-To") } + this.state.href = href + this.state.options = options try { this.loadContent(html, options) @@ -197,49 +192,76 @@ Pjax.prototype = { throw e } } + }.bind(this)) + }, - if (options.history) { - if (this.firstrun) { - this.lastUid = this.maxUid = newUid() - this.firstrun = false - window.history.replaceState({ + afterAllSwitches: function() { + trigger(window, "resize scroll") + + // FF bug: Won’t autofocus fields that are inserted via JS. + // This behavior is incorrect. So if theres no current focus, autofocus + // the last field. + // + // http://www.w3.org/html/wg/drafts/html/master/forms.html + var autofocusEl = Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop() + if (autofocusEl && document.activeElement !== autofocusEl) { + autofocusEl.focus(); + } + + // execute scripts when DOM have been completely updated + this.options.selectors.forEach(function(selector) { + forEachEls(document.querySelectorAll(selector), function(el) { + executeScripts(el) + }) + }) + + if (this.state.options.history) { + if (this.firstrun) { + this.lastUid = this.maxUid = newUid() + this.firstrun = false + window.history.replaceState({ url: window.location.href, title: document.title, uid: this.maxUid }, document.title) - } + } - // Update browser history - this.lastUid = this.maxUid = newUid() - window.history.pushState({ - url: href, - title: options.title, + // Update browser history + this.lastUid = this.maxUid = newUid() + window.history.pushState({ + url: this.state.href, + title: this.state.options.title, uid: this.maxUid }, - options.title, - href) + this.state.options.title, + this.state.href) + } + + this.forEachSelectors(function(el) { + this.parseDOM(el) + }, this) + + // Fire Events + trigger(document,"pjax:complete pjax:success", this.state.options) + + this.state.options.analytics() + + // Scroll page to top on new page load + if (this.state.options.scrollTo !== false) { + if (this.state.options.scrollTo.length > 1) { + window.scrollTo(this.state.options.scrollTo[0], this.state.options.scrollTo[1]) } - - this.forEachSelectors(function(el) { - this.parseDOM(el) - }, this) - - // Fire Events - trigger(document,"pjax:complete pjax:success", options) - - options.analytics() - - // Scroll page to top on new page load - if (options.scrollTo !== false) { - if (options.scrollTo.length > 1) { - window.scrollTo(options.scrollTo[0], options.scrollTo[1]) - } - else { - window.scrollTo(0, options.scrollTo) - } + else { + window.scrollTo(0, this.state.options.scrollTo) } - }.bind(this)) + } + + this.state = { + numPendingSwitches: 0, + href: null, + options: null + } } } diff --git a/lib/switches-selectors.js b/lib/switches-selectors.js index 5ae919f..289e234 100644 --- a/lib/switches-selectors.js +++ b/lib/switches-selectors.js @@ -24,6 +24,9 @@ module.exports = function(switches, switchesOptions, selectors, fromEl, toEl, op if (this.log) { this.log("newEl", newEl, "oldEl", oldEl) } + + this.state.numPendingSwitches++ + if (switches[selector]) { switches[selector].bind(this)(oldEl, newEl, options, switchesOptions[selector]) } diff --git a/tests/lib/switch-selectors.js b/tests/lib/switch-selectors.js index 8653227..1e32e04 100644 --- a/tests/lib/switch-selectors.js +++ b/tests/lib/switch-selectors.js @@ -9,7 +9,8 @@ tape("test switchesSelectors", function(t) { var pjax = { onSwitch: function() { console.log("Switched") - } + }, + state: {} } var tmpEl = document.implementation.createHTMLDocument()