Help us understand the problem. What is going on with this article?

言うなればスライディングフォーカスを簡単に実現するライブラリ

マウスオーバーするとボーダーがひゅーんっと移動するメニューを時々見かけますが、これを実現するライブラリ(というか一個の関数)を作りました。こういうやつです。
flying-box.gif
…ちょっとフレームレートが低くてわかりにくいので 動くデモをご覧ください

私の探し方が悪いのか、この UI の呼び方がわからないので、とりあえずスライディングフォーカスと呼ぶことにします。
英語では Sliding Border Navigation Menu とか呼ばれることもあるようですが、正確にはボックスを移動していること、ナビゲーションやメニューに限定する必要はないことから、 Sliding Focus です。
そして今回作ったライブラリの名前は Flying Box JS です。紛らわしいですね。

動作確認環境

  • Mac の Chrome ・ Firefox ・ Safari
  • iPhone 5s の Safari
  • Windows の Edge

説明

シソーラス

  • アイテム要素
    メニューのアイテムに相当します。
  • フォーカス要素
    そのまま。
  • ホーム要素
    選択中のアイテム要素。現在のページなどに相当します。 正確には [data-is-home="true"] 属性が設定されたアイテム要素です。

何をしているのか

基本的には次の二点だけです。

  • アイテム要素がマウスオーバーされたらフォーカス要素の位置とサイズをアイテム要素に合わせる
  • クリックされたアイテム要素をホーム要素にする

ホーム要素と、ホーム要素に移動したフォーカス要素には、デフォルトで [data-is-home="true"] が設定されます。
後は CSS で上手く飾り付けてください。

最低限のサンプル

最低限のデモ のコードを掲載しておきます。
使い方としては flying-box.js を読み込み、 flyingBox() 関数を実行するだけです。
GitHubはこちら

<style>
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
  .flying-box {
    display: flex;
    justify-content: space-between;
    position: relative;
  }
  .flying-box a {
    display: block;
    flex-grow: 1;
    padding: 8px 0;
    text-align: center;
  }
  .flying-box a[data-is-home="true"] {
    color: #f00000;
  }
  .flying-box .flying-box__focus {
    background-color: rgba(0, 128, 240, 0.1);
    pointer-events: none;
    position: absolute;
    transition: all 100ms ease-out;
  }
  .flying-box .flying-box__focus[data-is-home="true"] {
    background-color: rgba(240, 0, 0, 0.1);
  }
</style>
<menu class="flying-box">
  <div class="flying-box__focus"></div>
  <a data-is-home="true">Home</a>
  <a>About</a>
  <a>Services</a>
  <a>Help</a>
</menu>
<script src="./flying-box.js"></script>
<script>
  flyingBox()
</script>

fliying-box.js はこうなっています。

const flyingBox = (option = {
  itemQuery: '.flying-box a',
  focusQuery: '.flying-box .flying-box__focus',
  homeAttr: 'data-is-home'
}) => {
  const focus = document.querySelector(option.focusQuery)
  const moveFocus = (item) => {
    if (!item) return
    focus.style['top'] = `${item.offsetTop}px`
    focus.style['left'] = `${item.offsetLeft}px`
    focus.style['width'] = `${item.clientWidth}px`
    focus.style['height'] = `${item.clientHeight}px`
    focus.setAttribute(option.homeAttr, item === queryHome())
  }
  const queryHome = () => document.querySelector(`${option.itemQuery}[${option.homeAttr}=true]`)
  moveFocus(queryHome())
  const items = document.querySelectorAll(option.itemQuery)
  items && items.forEach((item) => {
    item.addEventListener('mouseenter', () => { moveFocus(item) })
    item.addEventListener('mouseleave', () => { moveFocus(queryHome()) })
    item.addEventListener('click', () => {
      const home = queryHome()
      home && home.removeAttribute(option.homeAttr)
      item.setAttribute(option.homeAttr, true)
      focus.setAttribute(option.homeAttr, true)
    })
  })
}

ホーム要素が動的に切り替わることを考慮した結果、ちょっと冗長になりました。
タッチデバイスでは mouseenter とか click をタッチイベントに変えた方が良いかもしれません。

注意点

  • トランスパイルされる前提で書きました。
  • 「マウスオーバーではスライドせず、クリックした時だけスライドする」ような挙動の実装は…お任せします。
  • このスライディングフォーカス、実は CSS だけでも実装できますが、「各アイテムのサイズを決め打ちにしなければならない」「ラジオボタンなどを使わなければクリックで固定することができない」などの苦行が待ち受けているのでおすすめできません。 CSS だけで何とかしようとするのはやめましょう。

おわりに

UI の名前がわからない問題、どうにかしたい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした