ラインが追従するナビゲーションを実装する

ラインが追従するナビゲーションを実装する

今回はcssとjavascriptを使って、次のようなナビゲーションを実装する方法を紹介します。

  • ナビゲーションアイテム要素のcurrentクラスが付いている要素の下に最初はラインがある。
  • ナビゲーションアイテム要素にホバーすると、ラインが移動する。
  • ホバーアウトすると、currentクラスのナビゲーションアイテム要素にラインが戻る。

実装したのがこちら

See the Pen navigation line by takblog (@blanks-site) on CodePen.

コード(html,css,javascript)

html

コードをクリップボードにコピー
<nav class="nav" id="nav">
  <i class="nav-line" id="nav-line"></i>
  <ul class="glnav" id="glnav">
    <li class="glnav-item"><a class="glnav-link" href="/">HOME</a></li>
    <li class="glnav-item current"><a class="glnav-link" href="/about/">ABOUT</a></li>
    <li class="glnav-item"><a class="glnav-link" href="/products/">PRODUCTS</a></li>
    <li class="glnav-item"><a class="glnav-link" href="/news/">NEWS</a></li>
    <li class="glnav-item"><a class="glnav-link" href="/faq/">FAQ</a></li>
    <li class="glnav-item"><a class="glnav-link" href="/contact/">CONTACT</a></li>
  </ul>
</nav>

css

コードをクリップボードにコピー
.nav {
  position: relative;
}
.nav-line {
  display: block;
  width: 0;
  height: 2px;
  position: absolute;
  bottom: 0;
  background-color: #ff0000;
  transition: .2s all ease;
}

cssは最低限のものしか載せていません。

javascript

コードをクリップボードにコピー
// 変数・定数定義
const glnav = document.getElementById("glnav");
const navItems = document.getElementsByClassName("glnav-item");
const navLine = document.getElementById("nav-line");
let navLineInitLeft;
let navLineInitWidth;

// 関数定義
const initNavline = () => {
  const navCurrent = glnav.getElementsByClassName("current")[0];
  const navLeft = navCurrent.getBoundingClientRect().left
  const navWidth = navCurrent.clientWidth;
  navLine.style.left = `${navLeft}px`;
  navLine.style.width = `${navWidth}px`;
  navLineInitLeft = `${navLeft}px`;
  navLineInitWidth = `${navWidth}px`;
}

const navMouseOver = (target) =>{
  const left = target.getBoundingClientRect().left;
  const width = target.clientWidth;
  navLine.style.left = `${left}px`;
  navLine.style.width = `${width}px`;
}

const navMouseOut = () =>{
  navLine.style.left = navLineInitLeft;
  navLine.style.width = navLineInitWidth;
}


// 関数実行
initNavline();
for( let i = 0;i < navItems.length; i++ ){
  navItems[i].addEventListener("mouseover",(e)=>{
    navMouseOver(e.currentTarget);
  });
  navItems[i].addEventListener("mouseout",()=>{
    navMouseOut();
  });
}

window.addEventListener('resize', ()=>{
  initNavline();
});

IE11等に適用させるときは、トランスパイルツールでトランスパイルしてから使用してください。

私は以下のツールでトランスパイルしています。

javascriptの説明

コードをポップアップで見る

1~6行目:変数定義

5行目、navLineInitLeftは画面の左端からcurrentクラスが付いたglnav-item要素の左端までの距離を入れます。

6行目、navLineInitWidthはcurrentクラスが付いたglnav-item要素のwidthを入れます。

9~17行目:initNavline関数

nav-line要素の初期位置(currentクラスが付いたglnav-item要素の下にくる位置)を設定する関数です。

10行目では、currentクラスが付いたglnav-item要素を取得しています。
glnav要素内にはcurrentクラスが付いた要素は1つしかない前提なので、

glnav.getElementsByClassName("current")[0]

で取得しています。

11、12行目ではcurrentクラスが付いたglnav-item要素の左端からの距離とwidthを取得しています。

今回の場合、nav要素はwidth:100%;なので

left = navCurrent.getBoundingClientRect().left;

になっています。

nav要素にmax-widthがあって、margin: 0 autoで真ん中寄せの場合

const nav = document.getElementById("nav");
left = navCurrent.getBoundingClientRect().left - nav.getBoundingClientRect().left;

として、leftの位置を調整します。

15、16行目でnavLineInitLeft、navLineInitWidthに11、12行目の値を入れています。

19~24行目:navMouseOver関数

glnav-item要素にマウスオーバーした時に発火する関数です。

内容はほぼinitNavline関数と同じです。

こちらもnav要素にmax-widthがあって、margin: 0 autoで真ん中寄せの場合

const nav = document.getElementById("nav");
left = target.getBoundingClientRect().left - nav.getBoundingClientRect().left;

として、leftの位置を調整します。

26~29行目:navMouseOut関数

glnav-item要素にマウスアウトした時に発火する関数です。

nav-line要素のleftとwidthにnavLineInitLeft、navLineInitWidthを入れています。

32行目以降:関数実行

関数を実行しています。

ウィンドウ幅が変わると、nav-line要素の位置と幅は変わるので、43行目ではresizeしたときに、initNavline関数が発火するようにしています。

arrow_circle_up