阶梯导航(一个 jQuery 插件?)

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 提供适用于您旅程各个阶段的云产品。 立即开始使用 价值 200 美元的免费积分!

前几天我突发奇想,想要实现一个导航的想法。 它只是一个视觉效果,其中悬停(或活动)的导航项将成为最高的“阶梯”,而它之前的和之后的其他项则向下延伸。 它很简单,但并不常见。 可能是因为一个主要原因:您无法在 CSS 中选择“上一个”元素。

stairwaynav
查看演示

您可以在 CSS 中选择“下一个”元素。 您将使用 通用兄弟组合器 来获取所有下一个元素,或者使用 相邻兄弟组合器 来获取下一个元素(可以进行链接)。 这些都无法让您获取上一个元素,正如您从上面的图片中看到的那样,这对于实现这种效果至关重要。

在伪代码中,我们试图做到这一点

/* Not Real Code */

a:hover { /* top stair stuff */ }

a:hover-1,
a:hover+1 { /* second stair stuff *}

a:hover-2,
a:hover-2 { /* third stair stuff *}

与其在 CSS 中变得过于复杂,不如依靠一项能够选择上一个元素的技术:jQuery。 jQuery 有一个 .prev() 函数(以及一些其他相关函数),我们可以用它来获取我们需要的元素。 我们的伪代码将变得更像这样真实的代码

$("nav a").hover(function() {
  $(this)
   .addClass("stair-1")
   .prev()
     .addClass("stair-2")
     .prev()
       .addClass("stair-3")
       .end()
     .end()
   .next()
     .addClass("stair-2")
     .next()
       .addClass("stair-3");  
});

可以想象,我们也会在 mouseleave 事件中清除所有导航元素上的所有类。 这意味着,为了提高效率,我们已经拥有指向所有这些元素的指针。

var navEls = $("nav a");

navEls
  .on("mouseenter", function() {
     // add classes as above
  })
  .on("mouseleave", function() {
     navsEls.removeClass("stair-1 stair-2 stair-3");
  })

现在我们有了集合,我们可以进一步提高效率。 使用 .next().prev() 意味着很多 jQuery 会重新访问 DOM 以确定要选择的内容(我认为,如果我错了请纠正我)。 与其这样做,我们可以根据集合中元素的位置来选择元素。 .index() 函数可以帮助我们确定位置,而 .eq() 可以让我们根据索引获取元素。

navEls
  .mouseenter(function() {

    $(this).addClass("stair-1");

    var index = $(this).index();
    
    allLinks.eq(index+1).addClass("stair-2");
    allLinks.eq(index-1).addClass("stair-2");

    allLinks.eq(index+2).addClass("stair-3");
    allLinks.eq(index-2).addClass("stair-3");

  })

这样就行了。

CSS 完成设计工作

请注意,所有 jQuery 做的只是添加和删除类。 这正是 UI 和 JavaScript 大部分时间应该保持的联系方式。 JavaScript 负责了解和改变状态,而 CSS 负责使页面看起来不同。

现在,当我们根据这些类应用样式时,整个“阶梯”视觉效果就出现了。

.stair-1 {
  transform:
    scale(1.10)
    translateX(24px)
  box-shadow: 0 0 10px rgba(black, 0.75);
  z-index: 3;
}
.stair-2 {
  transform:
    scale(1.07)
    translateX(12px)
  box-shadow: 0 0 10px rgba(black, 0.5);
  z-index: 2;
}
.stair-3 {
  transform:
    scale(1.04)
    translateX(4px)
  z-index: 1;
}

最上面的“阶梯”(stair-1)会放大、向右移动并带有深色阴影。 每个后续的阶梯都会在这三方面都略微减少一些。 您也可以在此处更改颜色,或执行其他适合您应用程序的操作。

一个 jQuery 插件?

我在这篇文章的标题中加上了这些词,因为我认为这是一个有趣的领域。

这种事情“应该”被插件化吗? 首先,这严重依赖于 CSS。 将其称为“阶梯导航”插件并不能描述实际的 jQuery 代码正在做什么。 它也没有利用 jQuery 为此构建的任何内置功能,例如它使事物动画化的能力,而是将此留给了 CSS。

无论如何,我们将把它插件化,因为它会让事情更有趣。

插件选项

我们将使它成为尽可能简单的选项集:您想要多少个阶梯下降? 您将在一个仅包含锚链接的导航元素上调用它

$(".main-nav").stairwayNav({
  stairs: 2
});

然后在插件中,我们确保可以访问一个“stairs”变量,该变量是传递的值或某个默认值。

$.fn.stairwayNav = function(options) {
  
  var defaults = {
     stairs: 3
  };
  this.options = $.extend({}, defaults, options);
  var stairs = this.options.stairs;

我喜欢这种模式。 这意味着我们不必进行任何花哨的检查,以查看对象是否包含某些键,并确保它们不为空,等等。 如果您传递了一个“stairs”的值,那么该值最终将出现在 options 对象中。 如果您没有传递,它将获得一个默认值。 太酷了。

循环

为了遵守该选项,我们现在只需运行一个小的 for 循环,循环次数与阶梯数量相同。 我们在每次迭代中调整索引值,但永远不会选择负值。

navEls
  .mouseenter(function() {
    $(this).addClass("stair-1");
    var index = $(this).index(), i, bef, aft;
    for(i = 1; i < stairs; i++) {
      
      bef = index - i;
      aft = index + i;
     
      allLinks.eq(aft).addClass("stair-" + (i+1));
      if (bef > 0) {
        allLinks.eq(bef).addClass("stair-" + (i+1));
      }
    }   
  })

阶梯导航演示

这是 CodePen 上的演示