Inspired by PerformanceObserver but for observing history on browsers.
- ES2018+ (This module uses RegExp named capture groups for route matching.)
# Install via NPM
$ npm install url-observer
import 'url-observer';
const observer = new URLObserver((list, observer) => {
for (const entry of list.getEntries()) {
/** Process entry for each URL update */
}
});
const routes = [
/^\/test$\//i,
/^\/test\/(?<test>[^\/]+)$/i,
];
const options = {
dwellTime: 2e3, /** Default dwellTime. Set -1 to always push new URL */
debug: false, /** Set to enable debug mode. This exposes hidden `routes` property. */
matcherCallback() {
/**
* Override how route matching works internally.
* By default, ES2018's RegExp named capture groups are used.
*/
},
};
/** Call .observe() to start observing history */
observe.observe(routes, options);
/** Call .add() to add new route or before route handler to existing registered route */
observer.add({
handleEvent: () => {
/** Do anything before route changes. Return true to navigate to new route. */
return true;
}
pathRegExp: routes[0],
/**
* A scoped route handler enables multiple before route handler to be registered to the
* same route. E.g.
*
* A .data-scope property or `data-scope` attribute can be set in an anchor tag so that URLObserver
* knows which before route handler it needs to trigger before navigating to a new URL.
*
* When .data-scope (or `data-scope`) is an empty string, it defaults to ':default', which is the
* default scope value when registering a route unless specified.
*
* 1. <a href="/test/123">/test/456</a>
* - No before route handler will be triggered on link click as it is not a scoped link.
*
* 2. <a href="/test/123" data-scope>/test/123</a>
* - Only before route handler registered to ':default' scope will be triggered.
*
* 3. <a href="/test/123" data-scope="456">/test/456</a>
* - Only before route handler registered to '456' scope will be triggered.
*
*/
scope: '',
});
/** Dynamically add new route without before route handler */
observer.add({ pathRegExp: /^\/test2$/i });
/** Call .disconnect() to stop observing history */
observer.disconnect();
/** Call .match() to determine if current URL is being observed by URLObserver */
const {
/** Return true for a matched route */
found,
/**
* Return URL parameters after matching the route RegExp with current URL. E.g.
*
* 1. /^\/test/i
* - This does not output any matches
* 2. /^\/test\/(?<test>[^\/]+)$/i
* - This matches URL like '/test/123' and returns { test: 123 }. However, this requires
* ES2018's RegExp named capture groups to work as expected.
*/
params,
} = observer.match();
/** Remove a route from the observer */
observer.remove(routes[0]);
/** Remove a before route handler from an observing route */
observer.remove(routes[1], '456');
/** Return the history entries */
observer.takeRecords();
/** Async-ly call .updateHistory to manually update to new URL */
await observer.updateHistory('/test/789');
/**
* Async-ly call .updateHistory to manually update to new URL and trigger before route handler
* with defined scope value.
*/
await observer.updateHistory('/test/456', '456');
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
MIT License © Rong Sen Ng