Day 24 - Sticky Nav

前言

JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。

另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。


本日目标

今天我们要做的是置顶导览列,当使用者拉下卷轴而视窗顶部碰到导览列顶部时,若往後继续下拉卷轴则将导览列始终固定在最上方。


解析程序码

HTML 部分

header是网页上方的标题。

#main是网页的导览列,包含.logo(初始隐藏)还有其他五个项目。

.site-wrap放的是一大堆假文还有图片。

<header>
    <h1>A story about getting lost.</h1>
</header>

<nav id="main">
    <ul>
      <li class="logo"><a href="#">LOST.</a></li>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Images</a></li>
      <li><a href="#">Locations</a></li>
      <li><a href="#">Maps</a></li>
    </ul>
</nav>

<div class="site-wrap">
    <!--以下是假文还有图片,为不占空间所以省略-->
</div>

JS 部分

宣告常数nav取得网页导览列。
宣告常数topOfNav取得导览列顶部离视窗最上方的距离。

const nav = document.querySelector('#main');
const topOfNav = nav.offsetTop;

我们置顶导览列的机制主要是透过观测卷轴的"卷动量"来决定是否置顶卷轴。为此首先要在window注册scroll event listener在卷轴卷动时不断触发,然後用fixNav()进行事件处理。

fixedNav()里面,我们可以判断现在视窗的 y 方向卷动量(window.scrollY)是否超过导览列的顶部(topOfNav),如果超过的话就在body上添加fixed-nav这个 class,反之若没有超过则移除fixed-nav

那为什麽我们还要特别设定bodypadding-top呢? 因为在导览列(#main)的 CSS 设定中,我们使用了position: fixed;,这样就会造成导览列(#main)原本占的空间突然空出来,之後下方的.site-wrap看上面还有空间就会挤上来,但挤上来的速度过快,因此在视觉上就会出现诡异的弹跳。

为避免这种状况发生,我们可以在置顶导览列的同时,指定bodypadding-top: nav.ofsetHeight;,让原本导览列占有的空间,被padding-top补上。不置顶导览列的同时,理所当然我们就要将bodypadding-top改回 0。

function fixNav(){
    console.log(topOfNav,window.scrollY);
    if(window.scrollY >= topOfNav){
      document.body.style.paddingTop = nav.offsetHeight + 'px';
      document.body.classList.add('fixed-nav');
    }else{
      document.body.style.paddingTop = 0;
      document.body.classList.remove('fixed-nav');
    }
}

window.addEventListener('scroll',fixNav);

CSS 部分

下面是在body上有.fixed-nav这个 class 时的特殊设定。

设定导览列position: fixed,在预设上是固定在left: 0;top: 0;的位置(视窗左上角),然後加上一点阴影。

.fixed-nav nav{
  position: fixed;
  box-shadow: 0 5px rgba(0,0,0,0.1);
}

把原本在导览列隐藏的logo显示出来,设定max-width(最大宽度)为500px,这里原本也可以用width: 500px;就好,但是设定width会让transition产生的动画效果失效。

.fixed-nav li.logo{
  max-width: 500px; /*use width can't show animation effect*/
}

在置顶导览列的同时,把下方的文章区块略为放大。

.fixed-nav .site-wrap{
  transform: scale(1);
}
补充资料:

HTMLElement.offsetTop
HTMLElement.offsetHeight
Element.classList
CSS Transform
Element: scroll event
Window.scrollY

范例网页请按此


<<:  Day9 针对 ICS 攻击的骇客集团(1)

>>:  第 08 天 再接再厉坚持不懈( leetcode 300 347 )

D27 第十四周 (回忆篇)

支线任务:共笔部落格切版 礼拜一的时候终於把留言版做完了,接着是弄共笔部落格的文章列表样板,花了一两...

Day24 Vue 认识Porps(3)

以物件做props的传递 我们先来看看一个例子! 在这里我们先用props把外层元件的data里的i...

[Day30]颁发和注销访问token

安装passport套件 安装套件cmd执行以下 composer require laravel/...

[Day22]C# 鸡础观念- 物件导向(oop)~物件(Object)

在程序语言中万物皆物件, 就如同真实世界中, 所有物质接由元素组成一般 物件基本原理 C#通过new...

WSL2, VM, Dual Boot, Proxmox怎麽选?

更多会员限定文章可以到patreon观看 WSL2, VM, Dual Boot, Proxmox怎...