构建自定义JavaScript Scrollspy导航

The content of an HTML document can be very long and difficult to access only through the scroll. Because of this arduous task, developers often use internal links (page jumps) as an alternative mode of transport around the page. This useful technique has been improved with the help of Javascript to offer a better experience, primarily by offering soft jumps, and then introducing the so-called Scrollspy scripts.

HTML文档的内容可能很长,并且仅通过滚动条很难访问。 由于这项艰巨的任务,开发人员经常使用内部链接 (页面跳转)作为围绕页面的另一种传输方式。 借助于Javascript,对这种有用的技术进行了改进,以提供更好的体验,主要是通过提供软跳转,然后引入所谓的Scrollspy脚本。

A Scrollspy is used to automatically update links in a navigation list based on scroll position.

Scrollspy用于根据滚动位置自动更新导航列表中的链接。

Through this tutorial we'll be building a custom Scrollspy component. See exactly what we are going to build below:

通过本教程,我们将构建一个自定义Scrollspy组件。 确切地看到我们将在下面构建的内容:

Custom Scrollspy

Also, you can take a look at the working DEMO.

另外,您可以看一下正在运行的DEMO

To accomplish this custom Scrollspy we will be using:

要完成此自定义Scrollspy,我们将使用:

  • Gumshoe: Simple, framework-agnostic scrollspy script.

    Gumshoe :简单的,与框架无关的scrollspy脚本。
  • Smooth Scroll: Lightweight script to animate scrolling to anchor links.

    平滑滚动 :轻量级脚本,可动画化滚动以锚定链接。
  • Anime.js: Flexible yet lightweight Javascript animation library.

    Anime.js :灵活而轻巧的Javascript动画库。

Along the tutorial we'll be explaining some features we use of these libraries, but it's a good idea to check the Github repositories, for basic understanding.

在本教程中,我们将解释我们使用这些库的一些功能,但是最好检查一下Github存储库,以了解基本知识。

标记 ( Markup )

Let's start with the HTML structure we'll be using, describing the key elements in the comments:

让我们从将要使用HTML结构开始,描述注释中的关键元素:

<section>
    <!-- Fixed header -->
    <!-- The [data-gumshoe-header] attribute tell Gumshoe that automatically offset it's calculations based on the header's height -->
    <!-- The [data-scroll-header] attribute do the same thing but for Smooth Scroll calculations -->
    <header class="page-header" data-gumshoe-header data-scroll-header>
        <div class="page-nav">
            <!-- Nav and links -->
            <!-- The [data-gumshoe] attribute indicates the navigation list that Gumshoe should watch -->
            <nav data-gumshoe>
                <!-- Turn anchor links into Smooth Scroll links by adding the [data-scroll] data attribute -->
                <a data-scroll href="#eenie">Eenie</a>
                <a data-scroll href="#meanie">Meanie</a>
                <a data-scroll href="#minnie">Minnie</a>
                <a data-scroll href="#moe">Moe</a>
            </nav>
            <!-- Arrows -->
            <a class="nav-arrow nav-arrow-left"><svg class="icon"><use xlink:href="#arrow-up"/></svg></a>
            <a class="nav-arrow nav-arrow-right"><svg class="icon"><use xlink:href="#arrow-down"/></svg></a>
        </div>
    </header>
    <!-- Page content -->
    <main class="page-content">
        <section>
            <h2 id="eenie"><a data-scroll href="#eenie">Eenie</a></h2>
            <p>Lorem ipsum dolor sit amet, has dico eligendi ut.</p>
            <!-- MORE CONTENT HERE -->
        </section>
    </main>
</section>

添加样式 ( Adding style )

With the HTML ready, we are all set to add some style. Let's see the key style pieces commented briefly:

在准备好HTML之后,我们都准备添加一些样式。 让我们看一下简短评论的关键样式:

h2 {
  /* This is to solve the headbutting/padding issue. Read more: https://css-tricks.com/hash-tag-links-padding/ */
  /* 110px = 80px (fixed header) + 30px (additional margin) */
  &:before {
    display: block;
    content: " ";
    margin-top: -110px;
    height: 110px;
    visibility: hidden;
  }
}

/* Fixed header */
.page-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px; /* The height of fixed header */
  background-color: #2D353F;
  text-align: center;
  z-index: 2;
}

/* Content container */
.page-content {
  display: inline-block; /* This is for clearing purpose. */
  margin: 80px 50px 30px; /* Margin top = 80px because of fixed header */
}

/* Nav container */
.page-nav {
  display: inline-block;
  position: relative;
  margin-top: 20px;
  height: 40px; /* This is the same height of each link */
  width: 400px;
  max-width: 100%; /* Responsive behavior */
  overflow: hidden; /* Only current link visible */
  background-color: #427BAB;
}

/* Nav and links */
nav {
  position: relative;
  width: 100%;
  line-height: 40px;
  text-align: center;
  background-color: rgba(0, 0, 0, 0.05);

  a {
    display: block;
    font-size: 18px;
    color: #fff;
    outline: none;
  }
}

添加功能和动画 ( Adding functionality and animations )

As we will be working closely with the DOM, we need to get all the elements we need first. Also we will declare the additional variables we will be using.

当我们将与DOM紧密合作时,我们需要首先获取我们需要的所有元素。 我们还将声明将要使用的其他变量。

// Init variables
var navOpen = false;
var pageNav = document.querySelector('.page-nav');
var navEl = document.querySelector('.page-nav nav');
var navLinks = document.querySelectorAll('.page-nav nav a');
var arrowLeft = document.querySelector('.nav-arrow-left');
var arrowRight = document.querySelector('.nav-arrow-right');
var navHeight = 40;
var activeIndex, activeDistance, activeItem, navAnimation, navItemsAnimation;

The following is a key part of the puzzle. This function translate the nav element to show only the selected link, using the activeIndex value.

以下是难题的关键部分。 此函数使用activeIndex值将nav元素转换为仅显示所选链接。

// This translate the nav element to show the selected item
function translateNav(item) {
    // If animation is defined, pause it
    if (navItemsAnimation) navItemsAnimation.pause();
    // Animate the `translateY` of `nav` to show only the current link
    navItemsAnimation = anime({
        targets: navEl,
        translateY: (item ? -activeIndex * navHeight : 0) + 'px',
        easing: 'easeOutCubic',
        duration: 500
    });
    // Update link on arrows, and disable/enable accordingly if first or last link
    updateArrows();
}

Then, we need a way to open and close the nav. The open state should let us see all the links and allow us to select one of them directly. The close state is the default one, letting see only the selected link.

然后,我们需要一种打开和关闭nav 。 打开状态应让我们看到所有链接,并允许我们直接选择其中之一。 关闭状态是默认状态,仅显示选定的链接。

// Open the nav, showing all the links
function openNav() {
    // Updating states
    navOpen = !navOpen;
    pageNav.classList.add('nav-open');
    // Moving the nav just like first link is active
    translateNav();
    // Animate the `height` of the nav, letting see all the links
    navAnimation = anime({
        targets: pageNav,
        height: navLinks.length * navHeight + 'px',
        easing: 'easeOutCubic',
        duration: 500
    });
}

// Close the nav, showing only the selected link
function closeNav() {
    // Updating states
    navOpen = !navOpen;
    pageNav.classList.remove('nav-open');
    // Moving the nav showing only the active link
    translateNav(activeItem);
    // Animate the `height` of the nav, letting see just the active link
    navAnimation = anime({
        targets: pageNav,
        height: navHeight + 'px',
        easing: 'easeOutCubic',
        duration: 500
    });
}

Now let's see how we handle the events. We need handlers to open or close the nav accordingly.

现在让我们看看我们如何处理事件。 我们需要处理程序来相应地打开或关闭nav

// Init click events for each nav link
for (var i = 0; i < navLinks.length; i++) {
    navLinks[i].addEventListener('click', function (e) {
        if (navOpen) {
            // Just close the `nav`
            closeNav();
        } else {
            // Prevent scrolling to the active link and instead open the `nav`
            e.preventDefault();
            e.stopPropagation();
            openNav();
        }
    });
}

// Detect click outside, and close the `nav`
// From: http://stackoverflow.com/a/28432139/4908989
document.addEventListener('click', function (e) {
    if (navOpen) {
        var isClickInside = pageNav.contains(e.target);
        if (!isClickInside) {
            closeNav();
        }
    }
});

We are ready to let Gumshoe and Smooth Scroll do the magic. See how we are initializing them:

我们准备让Gumshoe和Smooth Scroll做魔术。 看看我们如何初始化它们:

// Init Smooth Scroll
smoothScroll.init({
    // This `offset` is the `height` of fixed header
    offset: -80
});

// Init Gumshoe
gumshoe.init({
    // The callback is triggered after setting the active link, to show it as active in the `nav`
    callback: function (nav) {
        // Check if active link has changed
        if (activeDistance !== nav.distance) {
            // Update states
            activeDistance = nav.distance;
            activeItem = nav.nav;
            activeIndex = getIndex(activeItem);
            // Translate `nav` to show the active link, or close it
            if (navOpen) {
                closeNav();
            } else {
                translateNav(activeItem);
            }
        }
    }
});

And we are done! You can see it working here.

我们完成了! 您可以看到它在这里工作

For the sake of clarity, we have commented only the most important parts of the code. But you can get it all from this Github repo.

为了清楚起见,我们仅注释了代码中最重要的部分。 但是您可以从此Github存储库中获得所有信息。

We really hope you have enjoyed it and found it useful!

我们真的希望您喜欢它并发现它有用!

翻译自: https://scotch.io/tutorials/build-a-custom-javascript-scrollspy-navigation

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值