Skip to content

Commit

Permalink
fix(useTitle): prevent title from being updated infinitely
Browse files Browse the repository at this point in the history
  • Loading branch information
huynl-96 committed Aug 5, 2022
1 parent 058628b commit f582db1
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 9 deletions.
27 changes: 22 additions & 5 deletions packages/core/useTitle/index.md
Expand Up @@ -7,7 +7,8 @@ category: Browser
Reactive document title.

::: tip
When using with Nuxt 3, this functions will **NOT** be auto imported in favor of Nuxt's built-in `useTitle()`. Use explicit import if you want to use the function from VueUse.
When using with Nuxt 3, this functions will **NOT** be auto imported in favor of Nuxt's built-in `useTitle()`.
Use explicit import if you want to use the function from VueUse.
:::

## Usage
Expand All @@ -20,13 +21,13 @@ console.log(title.value) // print current title
title.value = 'Hello' // change current title
```

Set initial title immediately
Set initial title immediately:

```js
const title = useTitle('New Title')
```

Pass a `ref` and the title will be updated when the source ref changes
Pass a `ref` and the title will be updated when the source ref changes:

```js
import { useTitle } from '@vueuse/core'
Expand All @@ -40,9 +41,25 @@ const title = computed(() => {
useTitle(title) // document title will match with the ref "title"
```

Pass an optional template tag [Vue Meta Title Template](https://vue-meta.nuxtjs.org/guide/metainfo.html)
to update the title to be injected into this template:
Pass an optional template tag [Vue Meta Title Template](https://vue-meta.nuxtjs.org/guide/metainfo.html) to update the title to be injected into this template:

```js
const title = useTitle('New Title', { titleTemplate: '%s | My Awesome Website' })
```

::: warning
When setting `observe` to `true`, the `titleTemplate` must return the exact same value as the input title.
Otherwise, the document title will not be updated.
:::

```js
// this will work
const title = useTitle('New Title', { observe: true, titleTemplate: '%s' }) // default value
// this will work
const title = useTitle('New Title', { observe: true, titleTemplate: title => title })

// this won't work
const title = useTitle('New Title', { observe: true, titleTemplate: '%s - %s' })
// this won't work
const title = useTitle('New Title', { observe: true, titleTemplate: title => `${title} - modified` })
```
14 changes: 10 additions & 4 deletions packages/core/useTitle/index.ts
Expand Up @@ -50,23 +50,29 @@ export function useTitle(

const title: WritableComputedRef<string | null | undefined> = resolveRef(newTitle ?? document?.title ?? null)
const isReadonly = newTitle && isFunction(newTitle)
const hasModifiedTitle = isFunction(titleTemplate) ? titleTemplate('') !== '' : titleTemplate !== '%s'

function format(t: string) {
return isFunction(titleTemplate)
? titleTemplate(t)
: unref(titleTemplate).replace('%s', t)
: unref(titleTemplate).replace(/%s/g, t)
}

watch(
title,
(t, o) => {
if (isString(t) && t !== o && document)
document.title = format(t)
if (t !== o && document)
document.title = format(isString(t) ? t : '')
},
{ immediate: true },
)

if (observe && document && !isReadonly) {
/*
`titleTemplate` that returns the modified input string will make
the `document.title` to be different from the `title.value`, causing the title to update infinitely.
therefore, `observe` should be ignored in this case.
*/
if (observe && document && !isReadonly && !hasModifiedTitle) {
useMutationObserver(
document.head?.querySelector('title'),
() => {
Expand Down

0 comments on commit f582db1

Please sign in to comment.