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

您可以在 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 上的演示。
太棒了! 类似于 stroll.js 在 :hover 上 :D
第一个演示链接错误。 有点关于熊的东西……
确实很有趣! 最后的示例似乎在 FF 16 中的前一两个元素有点跳跃(如果我将鼠标悬停在第二个链接上,第一个链接没有像第二个链接那样“阶梯状”。 不知道是 FF 的问题还是其他问题。 期待尽快使用它。
我可以确认跳跃行为(将鼠标放在导航元素上文字开始的地方,处于未扩展状态,或者应该说未阶梯化? - 状态)以及第一个元素未被选择(阶梯化、添加类或其他操作) 当第二个元素被选中时。 我不是巫师,但我认为如果您更改第 21 行(在 CodePen 演示中)
if (bef > 0) {到
if (bef >= 0) {那么应该可以解决问题。
我真的很喜欢这个插件,我也必须称赞 CodePen,我花了将近 5 分钟的时间试图以一种可以轻松测试和编辑的方式获取源代码,然后才意识到我可以在 CodePen 上直接修改它。
希望对您有所帮助!
我认为在古老的 Flash 时代,它被称为“海啸”,而不是“阶梯”,对吧?
这绝对是我见过的最酷的事情之一!!!
Chris,非常棒的代码。
我点击了 RSS 阅读器中的“查看演示”,链接变成了“http://codepen.io/chriscoyier/pen/sdeuk”。
很漂亮,但具有误导性。
“阶梯”中的步骤暗示了不存在的导航层次结构。
太棒了!!! 我喜欢 css-tricks :)
先生,您能推荐一些 CSS 和 Java Script 的电子书吗?
感谢您阅读我与主题无关的评论。
:)
您好,虽然我不是您要问这个问题的“先生”,但我还是想向您推荐一本我发现非常有用的 CSS3 资源书的书名
“CSS3: Visual QuickStart Guide”(作者:Jason Cranford Teague),我想在线上也可以找到这本书的电子书版本(在亚马逊或类似网站上查找更多信息)。
不过我无法帮您解决 javascript 的问题。 希望其他不在主题范围内的“先生”能回答您的问题;)
我也不是您要找的“先生”,但我确信我知道 Chris 看到您的评论后会写什么。 我认为您在找他的 书架。
非常棒的感觉,可能在某些适当的情况下会派上用场,现在它确实是一个很好的噱头;)玩起来很有趣,可以激发有代码意识的人的灵感。
在 HCI 中,这通常被称为鱼眼导航。 主要用于您有一个很大的列表时。 未选中的元素会比您预期的要小。 如果我从我的课堂上记得正确,这种导航已经取得了一些成功。
这将非常适合任何关于钢琴和相关事物的主题。 另外,我喜欢 Martin Antsy 的那句话“我也不会是您正在寻找的上下文“先生””。 评论界年度最佳台词。 :-D
如果
stairs设置为高于4的任何值,则在mouseleave时不会删除附加类(active-5、active-6等)。这是一个快速且肮脏的正则表达式,可以用来代替
如果正则表达式让你感到很脏,你也可以使用循环来构建那个类字符串。
那个正则表达式缺少一个
g标志。哦,多么希望我们可以在此处编辑评论……
你能帮我做吗,Chris?
这里有一个 分叉的笔,展示了一个使用上面正则表达式技巧的 6 级楼梯系统。
似乎第一个链接从未改变,除非你专门悬停在它上面。
不确定是什么导致了这个问题,但我只是想指出它,以便比我更聪明的人可以检查一下。
也许我应该花两秒钟的时间真正尝试一下东西,然后再发表评论。
看起来一个简单的更改就能解决问题。
在块中
如果你将
if (bef > 0)更改为if (bef >= 0),它就可以正常工作。我分叉了 codepen 并在这里进行了更改 here.
从“哦,漂亮”的角度来看很棒,但从可用性的角度来看,它可能非常成问题。 第一个很好,尽管定义一个链接有点过头,但其他链接却有问题。 问题是很难分辨出你实际上选择的是哪个链接。
用户可能会立刻问自己几个可能导致混淆的问题
“为什么当我选择其中一个链接时,其他链接会弹出来?”
“如果我点击这个链接,它会把我带到我真正想要去的页面吗?”
“那些弹出来的链接是有关联的吗,但我没有理解?”
最后,我可能会使用第一个例子,但我永远不会使用其他例子,因为它不有利于用户。
你可以给它一个更微妙的外观,就像我在这个笔中做的那样,使用
skew而不是scale。这个效果看起来更像是一个凸起而不是楼梯,所以它看起来不像其他链接正在弹出来那么多。
“有利于用户”不仅仅是一件事。 如果你想要用户选择一些东西,比如他们喜欢的蛋糕口味,并且你想最小化其他选择对他们对悬停的选择的反应的影响。
只是一些想法。 说到蛋糕……这里有一个分叉。
如果你从左侧用指针靠近按钮,translateX 变换会将元素从指针移开,从而触发 mouseLeave 事件函数。 我之前在我构建的东西上遇到过这个问题,我尝试了一些非常复杂的方法来解决它。
我想你可以将每个菜单项包装在一个元素中,并添加填充,而不是使用 translateX 变换。
我可以确认它表现得像那样,但这非常奇怪。 Translate 变换(实际上任何变换)都不应该真正“移动”元素。 元素实际占据的空间仍然是元素在流动中的空间。 我猜这可能是一个错误。
有趣的是,留下的空间应该被认为是元素的一部分,尽管行为相互矛盾。 我在几个当前的浏览器中检查了,并且它在每个浏览器中都发生了。 Joseph Silber 在 这条评论 中提出了一个很好的建议,我认为这不仅可以应用于这里,还可以应用于我遇到类似问题的许多其他情况。
他在离开父元素时触发了
mouseleave函数。 这比我通常用来平滑悬停函数的方式开始和停止计时器要简单得多。我认为这不是一个错误。 变换,就像相对定位的元素一样,不会改变页面布局,但事件只会触发元素实际绘制的位置。
参见此处了解相对定位的元素是如何工作的。 我认为这是变换的预期行为。
有道理。 因此,
mouseleave会触发,但:hover仍然会“起作用”(因此你可以点击链接)。@chris……这正是我没有说的。
事件和
hover伪类都不会被应用。 它所做的只是在文档流中保留一个洞,这样它就不会影响页面上的任何其他元素。 对于所有其他目的,它舒适地位于它的新位置。好的。 这里你可以看到一个浮动链接留下的洞 http://codepen.io/chriscoyier/pen/JLwqm,但你不能点击那里 - 响应任何事件的区域是它被转换到的红色方块。
该死的,这看起来不错。 希望我能够在项目中实际使用它。
对于所有那些抱怨从左侧靠近链接时闪烁效果的人:解决方案是简单地删除父元素的
onmouseleave上的active-*类。 然后你还需要在mouseenter开始时删除它们,所以 在这个笔中,我使用了一个函数来处理它。非常好! 简单而有效。
你可以完全删除
translateX(),就像我在 我的分叉 中做的那样。 我的强迫症认为它在美学上更令人愉悦。这是一个非常酷的想法,它展示了 jQuery 提供的另一种巧妙的技术; 然而,这不是我真正会使用的东西。
像往常一样,干得好。 请继续努力,做得很好。
另一个很棒的教程,Chris。 这些年来,我从你那里学到了很多……再次感谢,继续摇滚!
做得好。 我想调整它,并尝试使用无序列表。
我在这方面是新手,但是当我用 Sass 运行 SCSS 文件时,我在
@include transform(translate3d(0, 0, 0));上遇到了错误。行。 可能是其他每个
@include也都会出现错误。 我在这里做错了什么?这是一个 Compass 混合器,所以请确保你也在使用它。
啊哈! 我明白了,我还有很多东西要学……感谢您的提示。
好的,所以我安装了 Compass,我看到我必须导入函数 - transform 和 transition。 我将以下语句放在我的 main.scss 的顶部……
@import “compass/css3/transform”;
@import “compass/css3/transition”;
这似乎是文档中建议的。 然后在我的命令行(我使用的是 Win 7 PC)中,我运行……
sass-convert sass/main.scss sass/main.sass
并且它运行时没有错误。 然后……
compass compile sass/main.sass
并且,它运行没有错误,表明它“覆盖 stylesheets/main.css”。这听起来确实不错。不幸的是,文件“main.css”仍然是空的。我在这里遗漏了什么?
忘记之前的。我找到了问题所在。如果我遇到进一步的困难,我会回来的……谢谢
如何将 js 代码添加到我的 HTML 页面,我已经添加了,但它不起作用。请告诉我执行此操作的步骤。
@Ajay 和 @Chris……同样的问题。我弄清楚如何翻译 SCSS,但现在 JS 似乎没有做任何事情。我很高兴这不是只有我一个人。
有人知道如何在 Internet Explorer 中显示正常的菜单吗?
SCSS 似乎坏了……或者可能是 Codepen 的故障?
如果有人能回复并帮助我们这些想让这个菜单真正工作起来的可怜灵魂,我会很高兴。一定有什么地方出错……有人知道是什么吗?
嘿 #Neal,
我是这样做的
我跳到了 code pen,并将三个项目复制到三个单独的文件 stairway.html、stairway.js 和 stairway.scss 中(使用具有语法高亮显示功能的 sublime { 我今天才开始使用 sublime,我认为我要从我信赖的 notepad++ 转移到 sublime,这要感谢 Chris 的演示之一})
因为我从来没有深入研究 scss 或 sass,而且我没有在我的机器上运行 ruby,而是在 Windows 7 上。
我找到一个名为 scout app 的 GUI 编译器
http://mhs.github.com/scout-app/
然后我在 stairway.scss 文件的顶部添加了以下几行
@import “compass/css3”;
@import “compass/css3/transform-legacy”;
在 scout 的配置选项卡中,我选择相同的文件夹作为输入和输出,保存了我的 .scss 文件,一旦 scout app 运行,它就会自动编译 .css 文件。
因为我想在 wordpress 上测试它,并且我知道 wordpress 使用非冲突的 jQuery,所以我将 stairway.js 文件中的所有 $ 实例更改为 jQuery。
将标签添加到标题中,只需将 js 添加到两个标签之间,并将其包装在
jQuery(document).ready(function(){ //stairway.js 脚本在这里复制粘贴,替换这个 //});
它似乎对我来说或多或少有效(我认为可能需要稍微调整一下,但基本功能都在那里)……
如果你想下载,我这里有一个已编译的 scss 版本
http://buildawebdoctor[dot]com/wp-content/themes/twentytwelve/css/stairway.css
以及该网站前端的菜单工作版本。
祝你好运 Neal
如果你需要其他任何东西,请在这里留言,我会尽力帮助你。
感谢你的尝试。我试过 Scout,但它只给了我一个 Adobe Air 3214 错误。但我已经能够完成翻译,而且我无法获得你的 CSS 文件。那个链接不起作用。看看我的网站 http://www.nealkoss.com/stair。文件是 index.html、js/stair.js 和 css/main.css。菜单显示出来了,但楼梯没有移动。
顺便说一句,你认为 Sublime 比 Dreamweaver 好吗?
我认为 sublime 比 dreamweaver 好!!
你调用了 jQuery 吗?请阅读以下内容
http://docs.jquery.com/Tutorials:How_jQuery_Works
chrome 开发者工具抛出了一个 $ 未定义的错误……
我的天啊!!总是令人惊奇的是,有些人(我!!!)会忘记最简单的事情。当然,这就是问题所在,现在它可以工作了。感谢你的链接,但我在阅读你的第一句话“……调用了 jQuery……”时意识到这一点。感谢你的坚持。
你需要在标签之间添加这个
http://docs.jquery.com/Tutorials:How_jQuery_Works