diff --git a/README.md b/README.md index a9d7d9b..81479a5 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,13 @@ will be a previous entry in the browser's history stack, and will emit `'navigate'` and `'render'`. Similar to [history.popState](http://devdocs.io/dom_events/popstate). +### `'DOMTitleChange'`|`state.events.DOMTITLECHANGE` +This event should be emitted whenever the `document.title` needs to be updated. +It will set both `document.title` and `state.title`. This value can be used +when server rendering to accurately include a `` tag in the header. +This is derived from the +[DOMTitleChanged event](https://developer.mozilla.org/en-US/docs/Web/Events/DOMTitleChanged). + ## State Choo comes with a shared state object. This object can be mutated freely, and is passed into the view functions whenever `'render'` is emitted. The state @@ -232,6 +239,9 @@ An object containing the current queryString. `/foo?bin=baz` becomes `{ bin: ### `state.route` The current name of the route used in the router (e.g. `/foo/:bar`). +### `state.title` +The current page title. Can be set using the `DOMTitleChange` event. + ## Routing Choo is an application level framework. This means that it takes care of everything related to routing and pathnames for you. diff --git a/index.js b/index.js index d89a875..ddb335a 100644 --- a/index.js +++ b/index.js @@ -20,9 +20,12 @@ function Choo (opts) { assert.equal(typeof opts, 'object', 'choo: opts should be type object') + var self = this + // define events used by choo this._events = { DOMCONTENTLOADED: 'DOMContentLoaded', + DOMTITLECHANGE: 'DOMTitleChange', REPLACESTATE: 'replaceState', PUSHSTATE: 'pushState', NAVIGATE: 'navigate', @@ -33,6 +36,7 @@ function Choo (opts) { // properties for internal use only this._historyEnabled = opts.history === undefined ? true : opts.history this._hrefEnabled = opts.href === undefined ? true : opts.href + this._hasWindow = typeof window !== 'undefined' this._createLocation = nanolocation this._tree = null @@ -40,6 +44,13 @@ function Choo (opts) { this.router = nanorouter({ curry: true }) this.emitter = nanobus('choo.emit') this.state = { events: this._events } + + // listen for title changes; available even when calling .toString() + this.emitter.prependListener(this._events.DOMTITLECHANGE, function (title) { + assert.equal(typeof title, 'string', 'events.DOMTitleChange: title should be type string') + self.state.title = title + if (self._hasWindow) document.title = title + }) } Choo.prototype.route = function (route, handler) { @@ -72,7 +83,6 @@ Choo.prototype.start = function () { assert.equal(typeof window, 'object', 'choo.start: window was not found. .start() must be called in a browser, use .toString() if running in Node') var self = this - if (this._historyEnabled) { this.emitter.prependListener(this._events.NAVIGATE, function () { self.emitter.emit(self._events.RENDER) @@ -133,6 +143,7 @@ Choo.prototype.start = function () { })) documentReady(function () { + self.state.title = document.title self.emitter.emit(self._events.DOMCONTENTLOADED) })