Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Better Ajaxify | 26 | 2 years ago | 2 | November 18, 2020 | 1 | mit | JavaScript | |||
Ajax websites engine for better-dom | ||||||||||
Pjax | 20 | 2 months ago | 20 | October 22, 2021 | 10 | mit | TypeScript | |||
Easily enable fast AJAX navigation (Fetch + pushState) | ||||||||||
React Native Wx | 16 | a year ago | 16 | JavaScript | ||||||
模仿微信界面的RN | ||||||||||
Demopro | 10 | 4 years ago | JavaScript | |||||||
React Native开源Demo | ||||||||||
React Native Template Wx | 10 | 4 years ago | JavaScript | |||||||
wx的react-native脚手架,自动集成mobx,react-navigation,完成fetch封装,全局loading,全局toast | ||||||||||
Rnapp | 6 | 10 months ago | TypeScript | |||||||
react-native练习demo | ||||||||||
Guangdiuapp | 2 | 5 years ago | JavaScript | |||||||
一款使用RN开发的优惠资讯汇总APP | ||||||||||
Angulargetbible | 2 | 8 years ago | mit | JavaScript | ||||||
Display Bible verses with AngularJS | ||||||||||
Govuk Content Navigation | 2 | 6 years ago | 1 | mit | HTML | |||||
Prototype for new GOV.UK navigation | ||||||||||
Health Record Maintainance And Tracking Application | 1 | 9 months ago | JavaScript | |||||||
The Application Which Stores Medical Records of the patients and used to track their past Medical Records |
Easily enable fast AJAX navigation (Fetch + pushState).
Pjax aims to deliver app-like browsing experiences. No more full page reloads, no more multiple HTTP requests. It does not rely on other libraries, like jQuery or similar. Written in pure TS, transpiled by Babel and Rollup.
A maintained, modern, and smaller version of MoOx/pjax.
Jump to Usage, Options, Status, Q&A, or Contributing Guide.
Visit https://www.jsdelivr.com/package/npm/@sliphua/pjax.
Install package @sliphua/pjax:
npm install @sliphua/pjax
Clone this repo and install:
git clone https://github.com/PaperStrike/Pjax.git
cd Pjax
npm install
dist
folderEach script file has a related .map
file, known as the source map, for debugging. Browsers won't fetch them without DevTools opened, so it won't affect your users' experiences. For more information, click the link to find out.
Link to pjax.js
or pjax.min.js
in a separate <script>
tag as:
<script src="./dist/pjax.js"></script>
Import the default value from pjax.esm.js
or pjax.esm.min.js
as:
import Pjax from './dist/pjax.esm.js';
In short, ONE fetch with a pushState
call.
Pjax fetches the new content, updates the URL, switches parts of your page, executes newly added scripts and scroll to the right position without refreshing the whole thing.
fetch
.pushState
.DOMParser
.Just designate the elements that differs between navigations. Pjax handles all the rest things.
Consider the following page:
<!DOCTYPE html>
<html lang="">
<head>
<title>My Cool Blog</title>
<meta name="description" content="Welcome to My Cool Blog">
<link href="/styles.css" rel="stylesheet">
</head>
<body>
<header class="header">
<nav>
<a href="/" class="is-active">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<section class="content">
<h1>My Cool Blog</h1>
<p>
Thanks for stopping by!
<a href="/about">Click Here to find out more about me.</a>
</p>
</section>
<aside class="sidebar">
<h3>Recent Posts</h3>
<!-- sidebar content -->
</aside>
<footer class="footer">
© My Cool Blog
</footer>
<script src="onDomReady.js"></script>
</body>
</html>
We want Pjax to intercept the URL /about
, and replace .content
with the resulting content of the request.
Besides, we would like to replace the <nav>
to show the active /about
link, as well as update our page meta and the <aside>
sidebar.
So all in all we want to update the page title and meta, header, content area, and sidebar, without reloading styles or scripts.
We can easily achieve this by telling Pjax to use such CSS selectors:
const pjax = new Pjax({
selectors: [
'title',
'meta[name=description]',
'.header',
'.content',
'.sidebar',
],
});
Now, when someone in a compatible browser clicks a link, the content selected above will switch to the specific content pieces found in the next page.
Magic! For real! Nothing server-side!
Browser | Supported versions | Release date |
---|---|---|
Chrome | 66+ | Apr 17, 2018 |
Edge | 79+ | Jan 15, 2020 |
Firefox | 60+ | May 9, 2018 |
Opera | 53+ | May 10, 2018 |
Safari | 12.2+ | Jul 22, 2019 |
Method | Parameters | Return Value |
---|---|---|
Pjax.constructor | options?: Partial<Options> | Pjax |
load | requestInfo: RequestInfo, overrideOptions?: Partial<Options> | Promise<void> |
weakLoad | requestInfo: RequestInfo, overrideOptions?: Partial<Options> | Promise<void> |
switchDOM | requestInfo: RequestInfo, overrideOptions?: Partial<Options> | Promise<void> |
preparePage | switchesResult: SwitchesResult | null, overrideOptions?: Partial<Options> | Promise<void> |
Pjax.reload | / | void |
The most basic way to get started.
When instantiating Pjax
, you can pass options into the constructor as an object:
const pjax = new Pjax({
selectors: [
'title',
'.header',
'.content',
'.sidebar',
],
});
This will enable Pjax on all links and forms, and designate the part to replace using CSS selectors 'title'
, '.header'
, '.content'
, and '.sidebar'
.
To disable the default Pjax trigger, set defaultTrigger
option to false
.
A call to this method aborts the current Pjax action (if any), and navigates to the target resource in Pjax way.
Any error other than AbortError
leads to the normal navigation (by window.location.assign
). Note that AbortError
happens on fetch timeout, too.
const pjax = new Pjax();
// use case 1
pjax.load('/your-url').catch(() => {});
// use case 2 (with options override)
pjax.load('/your-url', { timeout: 200 }).catch(() => {});
// use case 3 (with further action)
pjax.load('/your-url')
.then(() => {
onSuccess();
})
.catch(() => {
onAbort();
});
// use case 4 (with formed Request object)
const requestToSend = new Request('/your-url', {
method: 'POST',
body: 'example',
});
pjax.load(requestToSend);
// use case X, mix brakets above together
This method behaves almost the same as load
, except that it throws regardless of the error's type.
Useful when you need to handle all the errors on your own.
const pjax = new Pjax();
// use case
pjax.weakLoad('/your-url')
.then(() => {
onSuccess();
})
.catch((e) => {
onError(e);
});
This method accepts the URL string or the Request object to fetch. The response should contain the target document.
It returns a promise that resolves when all the following steps have done:
pushState
to update the URL.selectors
with switches defined in switches
.true
if previous step has cleared the page focus, otherwise, false
.preparePage
with a new SwitchesResult that contains focusCleared.This method accepts a nullable SwitchesResult.
It returns a promise that resolves when all the following steps have done:
autofocus
if focusCleared
(in the given switches result) evaluates to true
.scrollTo
option.interface SwitchesResult {
focusCleared: boolean
}
A helper shortcut for window.location.reload
, a static member of Pjax.
Pjax.reload();
Name | Type | Default Value |
---|---|---|
defaultTrigger | boolean | TriggerOptions | true |
selectors | string[] | ['title', '.pjax'] |
switches | Record<string, Switch> | {} |
scripts | string | script[data-pjax] |
scrollTo | number | [number, number] | boolean | true |
scrollRestoration | boolean | true |
cache | RequestCache | 'default' |
timeout | number | 0 |
hooks | Hooks | {} |
When set to false
or an object with enable: false
, disable the default Pjax trigger.
The default trigger alters these redirections:
<a>
and <area>
that targets a link within the same origin.<form>
that redirects to a link within the same origin.Disable when you only need Pjax in some specific moments. E.g.,
// Set `defaultTrigger` to `false`.
const pjax = new Pjax({ defaultTrigger: false });
// Use `load` on your demand.
document.addEventListener('example', (event) => {
if (!needsPjax) return;
event.preventDefault();
pjax.load('/bingo');
});
Use the exclude
sub-option to disable the trigger only for specific elements:
const pjax = new Pjax({
defaultTrigger: {
exclude: 'a[data-no-pjax]',
},
});
interface TriggerOptions {
enable?: boolean,
exclude?: string,
}
CSS selector list used to target contents to replace. E.g.,
const pjax = new Pjax({
selectors: [
'title',
'.content',
],
});
If a query returns multiples items, it will just keep the index.
Every selector, in the current page and new page, must select the same amount of DOM elements. Otherwise, Pjax will fall back into normal page load.
This contains callbacks (of type Switch) used to switch old elements with new elements.
The object keys should match one of the defined selectors (from the selectors
option).
Examples:
const pjax = new Pjax({
selectors: ['title', '.Navbar', '.pjax'],
switches: {
// default behavior
'title': Pjax.switches.default,
'.content': async (oldEle, newEle) => {
// How you deal with the two elements.
},
'.pjax': Pjax.switches.innerText,
},
});
type Switch<T extends Element = Element> = (oldEle: T, newEle: T) => (Promise<void> | void);
When it returns a promise, Pjax recognizes when the switch has done. Newly added scripts execute and labeled scripts re-execute after all switches finishes.
Pjax.switches.default
The default behavior, same as Pjax.switches.replaceWith
.Pjax.switches.innerHTML
Replace HTML contents by using Element.innerHTML
.Pjax.switches.textContent
Replace all text by using Node.textContent
.Pjax.switches.innerText
Replace readable text by using HTMLElement.innerText
.Pjax.switches.attributes
Rewrite all attributes, leaving inner HTML untouched.Pjax.switches.replaceWith
Replace elements by using ChildNode.replaceWith
.Your callback function can do whatever you want. But remember to keep the amount of the elements selected by the selectors
option remain the same.
In the example below, .current
class marks the only switching element, so that the switch elements' amount won't change. Before the returned promise resolves, Pjax will neither execute the script elements nor scroll the page.
const pjax = new Pjax({
selectors: ['.sidebar.current'],
});
const customSwitch = (oldEle, newEle) => {
oldEle.classList.remove('current');
newEle.classList.add('current');
oldEle.after(newEle);
return new Promise((resolve) => {
// Assume there's animations start right after inserting to DOM.
newEle.addEventListener('animationend', () => {
oldEle.remove();
resolve();
}, { once: true });
});
};
NOTE: Pjax waits for the switches, but moves on to the next navigation, and forgets the previous one, on matter whether finished or not. Workarounds that try to block user actions may not work as well as hoped, because the user can always use the "back" button, and Pjax always reacts to it.
CSS selector used to target extra <script>
to execute after a page switch. For multiple selectors, separate them by a comma. Use the empty string to target nothing. Like:
// Single selector
const pjax = new Pjax({
scripts: 'script.pjax',
});
// Multiple selectors
const pjax = new Pjax({
scripts: 'script.pjax, script.analytics',
});
// Only execute new scripts
const pjax = new Pjax({
scripts: '',
});
NOTE: Pjax always executes the newly added scripts in a page switch. You don't have to mark them here.
When set to a number, this represents the vertical value (in px from the top of the page) to scroll to after a page switch.
When set to an array of 2 numbers ([x, y]), this contains the horizontal and vertical values to scroll to after a page switch.
Set this to true
to make Pjax decide the scroll position. Pjax will try to act as the browsers' default behavior. For example, scroll the element into view when hash changing to its id, scroll to page left top when navigating to a new page without a valid hash.
Set this to false
to disable all scrolling by Pjax.
NOTE: This does not affect the scroll restoration defined below.
When set to true
, Pjax attempts to restore the page scroll status when navigating backward or forward.
This contains the cache mode of Pjax requests, which shares the same available values with Request.cache
.
The time in milliseconds to abort the fetch requests. Set to 0
to disable.
This option defines hooks (each of type Hook) for modifying the request sent, the response received, the document parsed and the switchResult created in Pjax. An example for adding a custom header for Pjax requests:
const pjax = new Pjax({
hooks: {
request: (request) => {
request.headers.set('My-Custom-Header', 'ready');
},
},
});
A function that returns undefined
, the hooked input, or a promise resolving to one of them.
type Hook<T> = (input: T) => T | void | Promise<T | void>;
interface Hooks {
request?: Hook<Request>;
response?: Hook<Response>;
document?: Hook<Document>;
switchesResult?: Hook<SwitchesResult>;
}
Accessible by calling on the Pjax instance.
Name | Type | Default Value |
---|---|---|
location | URL | new URL(window.location.href) |
abortController | AbortController | null | null |
The last location recognized by Pjax.
The abort controller that can abort the Pjax handling navigation. When Pjax handles nothing, null
.
For example, to abort Pjax on certain events:
const pjax = new Pjax();
document.addEventListener('example', () => {
pjax.abortController?.abort();
});
When calling Pjax, Pjax may fire a number of events.
All events fire from the document, not the clicked anchor nor the caller function. You can get more detail of the event via event.detail
.
An ordered list showing the types of these events, and the moment they happen:
pjax:send
event when Pjax sends the request.pjax:receive
event when Pjax receives the response.switchDOM
method for details.pjax:error
event if any error happens to previous steps.pjax:complete
event when previous steps finish.pjax:success
event if previous steps finish without any error.If you use a loading indicator (e.g. topbar), a pair of send
and complete
events may suit you well.
document.addEventListener('pjax:send', topbar.show);
document.addEventListener('pjax:complete', topbar.hide);
Pjax uses several custom headers when it sends HTTP requests.
X-Requested-With: Fetch
X-PJAX: true
X-PJAX-Selectors
A serialized JSON array of selectors, taken from selectors
. You can use this to send back only the elements that Pjax will use to switch, instead of sending the whole page. Note that you may need to deserialize this on the server (Such as by using JSON.parse
)Most of the time, you will have code related to the current DOM that you only execute when the DOM became ready.
Since Pjax doesn't trigger the standard DOM ready events, you'll need to add code to re-trigger the DOM ready code. For example:
function whenDOMReady() {
// do your stuff
}
document.addEventListener('DOMContentLoaded', whenDOMReady);
const pjax = new Pjax();
document.addEventListener('pjax:success', whenDOMReady);
NOTE: Don't instantiate Pjax in the whenDOMReady
function.