spa-history

A HTML5 history library for single-page application.

Usage no npm install needed!

<script type="module">
  import spaHistory from 'https://cdn.skypack.dev/spa-history';
</script>

README

spa-history

A HTML5 history library for single-page application.

Install

npm install spa-history

Constructor

PathHistory

import { PathHistory } from 'spa-history'

const history = new PathHistory({
  // path of base directory. defaults to ''
  // If you want the root path doesn't contain ending slash,
  // you can set the base without ending slash, like '/app'
  base: '/app/',
  
  /*
    beforeChange() will be called before location change.

    Arguments:
      to: A normalized location object. The location will be changed to.
      from: A normalized location object. The current location.
      action: String. What action triggered the history change.
        push: history.push() is called.
        replace: history.replace() is called.
        pop: user clicked the back or forward button, or history.go(), history.back(), history.forward() is called, or hash changed.
        init: "to" is the initial page, at this stage, "from" is null.
        dispatch: history.dispatch() is called.

    Returns:
      true | undefined: The navigation is confirmed.
      false: Prevent the navigation.
      null: Do nothing.
      path | location object: Redirect to this location.
        You can override the history manipulate action by providing the `action` property, values are: 'push', 'replace', 'dispatch'.

    Return value can be a Promise.
  */
  beforeChange(to, from, action) {
  },

  /*
    afterChange() will be called after the location changed.

    Arguments:
      to: Location object. The location changed to.
  */
  afterChange(to, from, action) {
  }
})

history.start()

HashHistory

import { HashHistory } from 'spa-history'

const history = new HashHistory({
  beforeChange(to, from, action) {
  },

  afterChange(to, from, action) {
  }
})

history.start()

HashHistory has no base option.

Location object

A location object is used for changing the current address. It can be used in history.start(location), history.push(location), history.replace(location), history.dispatch(location), etc.

A string URL can be converted to a location object by history.normalize(). to and from parameter of beforeChange and afterChange hook are normalized location objects.

And a location object can be converted to a URL string by history.url().

{
  path,
  external,
  query,
  hash,
  fullPath,
  url,
  state,
  hidden,
  appearPath
}

path

String

SPA Internal path, which has stripped the protocol, host, and base path.

external

Boolean

If path is started with protocal, or external is true, path is treated as an external path, and will be converted to an internal path.

query

Object | String | Array | URLSearchParams | StringCaster<URLSearchParams>

query accepts the same parameter types as URLSearchParams constructor. Or it can be a StringCaster object that wraps a URLSearchParams object.

hash

String

A string containing a # followed by the fragment identifier of the URL. If HashHistory is used, the fragment identifier is followed by the second # mark.

fullPath

String. Read-only.

path + query string + hash

url

String. Read-only.

An external relative URL which can be used as href attribute of <a>. It is the same as history.url(location).

  • PathHistory: base + path + query string + hash
  • HashHistory: # + path + query string + hash

state

Object

The state object is a JavaScript object which is associated with the history entry. See state parameter of history.pushState() for details.

hidden

Boolean

Indicate whether it is a hidden history entry. see history.push() for detail.

appearPath

String

If hidden is true and appearPath is set, the location bar will show this address instead.

APIs

history.current

Location object

The current location. See location object.

history.start()

history.start(URL string | location)

Starts to handle the history.

In browser, if URL/location is not given, the default value is the current address.

history.normalize()

history.normalize(URL string | location)

Converts a URL string or an unnormalized location object to a normalized object.

If URL/location.path is started with protocal, or location.external is true, location.path is treated as an external path, and will be converted to an internal path.

// PathHistory with base '/foo/bar/'
history.normalize('http://www.example.com/foo/bar/home?a=1#b')
/* ->
  {
    path: '/home',
    query: new StringCaster(new URLSearchParams('a=1')),
    hash: '#b',
    fullPath: '/home?a=1#b',
    url: '/foo/bar/home?a=1#b',
    state: {}
  }
*/

// same result as above
history.normalize({
  path: '/foo/bar/home?a=1#b',
  external: true
})

// same result as above
history.normalize('/home?a=1#b')

// same result as above
history.normalize({
  path: '/home',
  query: {
    a: 1
  },
  hash: '#b'
})

// HashHistory
history.normalize('http://www.example.com/app/#/home?a=1#b')
/* ->
  {
    path: '/home',
    query: new StringCaster(new URLSearchParams('a=1')),
    hash: '#b',
    fullPath: '/home?a=1#b',
    url: '#/home?a=1#b',
    state: {}
  }
*/

history.url()

history.url(URL string | location)

Converts a internal URL string or a location object to an external URL which can be used as href of <a>.

history.url({
  path: '/home',
  query: {
    a: 1
  },
  hash: '#b'
})

// or
history.url('/home?a=1#b')

/*
  result:
  HashHistory: #/home?a=1#b
  PathHistory(with base: '/foo/bar/'): /foo/bar/home?a=1#b
*/

history.push()

history.push(URL string | location)

Pushs the location onto the history stack. beforeChange will be called.

history.push('/home?a=1#b')

history.push({
  path: '/home',
  query: {
    a: 1
  },
  hash: '#b'
})

// PathHistory, complete URL
history.push('http://www.example.com/foo/bar/home?a=1#b')

// HashHistory, complete URL
history.push('http://www.example.com/#/home?a=1#b')

You can push a location with state.

history.push({
  path: '/home',
  state: {
    foo: 1,
    bar: 2
  }
})

And you can push a hidden location, which will not change the value of browser's address bar. the hidden location is stored in window.history.state.

history.push({
  path: '/login',
  state: {
    foo: 1
  },

  // '/login' won't show in the location bar
  hidden: true,

  // optional. if set, the location bar will show this address instead
  appearPath: '/buy'
})

history.replace()

history.replace(URL string | location)

Replaces the current history entry with the provided URL/location.

history.dispatch()

history.dispatch(URL string | location)

Sets the current location to the provided URL/location without changing the history session. That is, the location of browser's address bar won't change. beforeChange will be called.

history.setState()

history.setState(state)

Sets state of the current location. The state will be merged into history.current.state

history.go()

history.go(position, { silent = false, state = null } = {})

Counterpart of window.history.go(). Returns a promise which will be resolved when popstate event fired.

silent: If true, beforeChange won't be called.

state: If set, the state object will be merged into the state object of the destination location.

history.back()

history.back(options)

Same as history.go(-1, options).

history.forward()

history.forward(options)

Same as history.go(1, options).

history.captureLinkClickEvent()

history.captureLinkClickEvent(event)

Prevents the navigation when clicking the <a> element in the container and the href is an in-app address, history.push() will be called instead.

document.body.addEventListener('click', e => history.captureLinkClickEvent(e))

Dependencies

You can use @babel/polyfill and dom4 to meet the requirements.

Or use the polyfill.io service:

<script src="https://polyfill.io/v3/polyfill.min.js"></script>

License

MIT