Barba.js(v2)を使ってネイティブ風な動きを実装する

非SPAのサイトでネイティブ風な動きを実装したいと思い、Barba.jsを使ったので、その時に対応したことなどメモ。

# 完成したもの

gifなので、速度は結構早いですが、、、

![barba動き.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214546/20c3c5fe-584e-f194-e3d8-714dcc12e1e0.gif)


# 行った手順

## Barba.jsのインストール

```console
$ npm i @barba/core 
```

参考 : https://barba.js.org/docs/getstarted/install/

### polyfill対応

IEにもBarba.jsを対応させる場合、polyfillが必要となるので、

```html
<script
  crossorigin="anonymous"
  src="https://polyfill.io/v3/polyfill.min.js?features=default%2CArray.prototype.find%2CIntersectionObserver"
></script>
```

を追記する。

参考 : https://barba.js.org/docs/getstarted/browser-support/

## 基本的な遷移まで

```js
import barba from '@barba/core'
barba.init()
```

```html
<div data-barba='wrapper'>
  <!-- headerなど -->
  <div data-barba='container' data-barba-namespace='pageName'>
    <!-- ここに変更するコンテンツを配置する -->
  </div>
  <!-- footerなど -->
</div>
```

参考 : https://barba.js.org/docs/getstarted/markup/

## ページ遷移時に簡単なアニメーションを実装する

https://liginc.co.jp/476913 のコード参考

```console
$ npm i @barba/css
```

```js
import barba from '@barba/core'
import barbaCss from '@barba/css' // 追記

barba.use(barbaCss) // 追記
barba.init()
```

```css
.barba-leave-active,
.barba-enter-active {
	transition: opacity 0.4s ease;
}

.barba-leave {
	opacity: 1;
}

.barba-enter {
	opacity: 0;
}

.barba-leave-to {
	opacity: 0;
}

.barba-enter-to {
	opacity: 1;
}
```

公式 : https://barba.js.org/docs/plugins/css/

## prefetch導入

```console
$ npm i @barba/prefetch
```

```js
import barba from '@barba/core'
import barbaCss from '@barba/css'
import barbaPrefetch from '@barba/prefetch' // 追記

barba.use(barbaCss)
barba.use(barbaPrefetch) // 追記
barba.init()
```

参考 : https://barba.js.org/docs/plugins/prefetch/

### polyfill追記

自分の環境だと公式のpolyfillだけではエラーになったため、以下のコードで対応。(既存のものから`NodeList.prototype.forEach`を追加しただけです。)

```html
<script crossorigin='anonymous' src='https://polyfill.io/v3/polyfill.min.js?features=default%2CArray.prototype.find%2CIntersectionObserver%2CNodeList.prototype.forEach'></script>
```

polyfillについては https://polyfill.io/v3/url-builder/ を参考。

## 同じurlの場合、ページ遷移をさせない

同じリンク上だとページの読み込みが行われてしまうため。

参考 : https://gist.github.com/mhige/a5fe953de730afdd353e2432ace909b0#file-barba-custom-js-L3-L12

```js
import barba from '@barba/core'
import barbaCss from '@barba/css'
import barbaPrefetch from '@barba/prefetch'

barba.use(barbaCss)
barba.use(barbaPrefetch)
barba.init()

// ここから追記

const eventDelete = e => {
  if (e.currentTarget.href === window.location.href) {
    e.preventDefault()
    e.stopPropagation()
    return
  }
}

const links = [...document.querySelectorAll('a[href]')]
links.forEach(link => {
  link.addEventListener('click', e => {
    eventDelete(e)
  }, false)
})
```

## `title`タグ以外のmetaタグの情報の書き換えを行う

公式のままだと`title`タグは変更させますが、`description`タグなどについては更新されないため。

参考 : https://gist.github.com/mhige/a5fe953de730afdd353e2432ace909b0#file-barba-custom-js-L22-L45

`barba.init`以外は変更していないため省略。

```js
// 省略(barba.use(barbaPrefetch)より上)

const replaceHeadTags = target => {
  const head = document.head
  const targetHead = target.html.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]
  const newPageHead = document.createElement('head')
  newPageHead.innerHTML = targetHead
  const removeHeadTags = [
    "meta[name='keywords']",
    "meta[name='description']",
    "meta[property^='fb']",
    "meta[property^='og']",
    "meta[name^='twitter']",
    "meta[name='robots']",
    'meta[itemprop]',
    'link[itemprop]',
    "link[rel='prev']",
    "link[rel='next']",
    "link[rel='canonical']",
  ].join(',')
  const headTags = [...head.querySelectorAll(removeHeadTags)]
  headTags.forEach(item => {
    head.removeChild(item)
  })
  const newHeadTags = [...newPageHead.querySelectorAll(removeHeadTags)]
  newHeadTags.forEach(item => {
    head.appendChild(item)
  })
}

barba.init({
  transitions: [
    {
      beforeEnter({ next }) {
        replaceHeadTags(next)
      }
    }
  ]
})
//省略(const eventDelete = e => {より下)
```

### 上記のコードで変更が行えない場合

```javascript
barba.init({
  transitions: [
    {
      beforeEnter({ next }) {
        replaceHeadTags(next)
      }
    }
  ]
})
```

の部分を

```javascript
barba.hooks.beforeEnter(({ next }) => {
  replaceHeadTags(next)
})
```

に変更。

参考: https://github.com/barbajs/barba/issues/410

### transitionのオプションについて

| 名前 | 説明 |
| --- | ---|
| before |最初 |
| beforeLeave | 現在のページを離れる直前 |
| leave | 現在のページを離れる時 |
| afterLeave | 現在のページを離れた直後 |
| beforeEnter | 次のページを表示する直前 |
| enter | 次のページを表示する時 |
| afterEnter | 次のページが表示された直後 |
| after |最後 |

その他オプションについては https://barba.js.org/docs/advanced/hooks/ 参考。

## ページ遷移時にGoogle Analyticsに情報を送信

参考 : https://gist.github.com/mhige/a5fe953de730afdd353e2432ace909b0#file-barba-custom-js-L47-L48

```js
// いろいろ省略
const gaPush = pagename => {
  ga('send', 'pageview', pagename)
}
barba.init({
  transitions: [
    {
      beforeEnter({ next }) {
        // replaceHeadTags(next)の処理
        gaPush(location.pathname)
      }
    }
  ]
})
```

## ページ遷移時にページの一番上まで行く。

ページをスクロール → リンククリック → ページ遷移だとスクロールの値が保存されていたので、それを0にする。

```js
// いろいろ省略
barba.hooks.enter(() => {
  window.scrollTo(0, 0);
});
```

参考: https://barba.js.org/docs/advanced/hooks/#Global-hooks



## Barbaの範囲外も適応させたい

現状だと `header` や `footer`などは一切動かないため動きを追加する。(簡単なクラスの付け替えなど)


```js
barba.init({
  transitions: [
    beforeEnter({ next }) {
      const header = document.querySelector('.l-header') // header要素取得
      header.classList.toggle('is-active') // クラスの付け替え
      // その他いろいろ
    }
  ]
})
```

# 参考リンク一覧

- https://barba.js.org/
- https://liginc.co.jp/476913
- https://notes.sharesl.net/articles/50/
- https://www.evoworx.co.jp/blog/barbajs-transition/
- https://www.willstyle.co.jp/blog/1722/