在本教程中,我们将通过构建一些有用且有趣的东西来享受 javascript 鼠标事件的乐趣:缩略图,当鼠标悬停在上面时,会显示一系列不同的电子书。
这是我们要构建的演示(在每个缩略图上移动鼠标):
See the Pen How to Make Multi-Preview Thumbnails With JavaScript Mouse Events by Envato Tuts+ (@tutsplus) on CodePen.
每个缩略图显示隐藏在下面的相应电子书,具体取决于光标位置。这是预览可用内容的理想方式(在本例中位于Envato Tuts+ 电子书库)。
在我们构建它时,我们将使用 css 网格布局和 JavaScript mousemove 事件等。
1. html 标记
我们将使用的标记非常简单;一个普通的无序列表,每个列表项(卡片)内都有一些大小相同的图像。
<ul class="cards">
<li class="card">
<img src="IMG_SRC" alt="">
<!-- 4 more images here -->
</li>
<li class="card">
<img src="IMG_SRC" alt="">
<!-- 3 more images here -->
</li>
<li class="card">
<img src="IMG_SRC" alt="">
<!-- 2 more images here -->
</li>
<li class="card">
<img src="IMG_SRC" alt="">
<!-- 1 more image here -->
</li>
</ul>
2.CSS _
现在,关于 CSS 样式,有两件事很重要:
我们将无序列表定义为网格容器并给出列表项 width: 25%。除了 CSS 网格,您可以使用 flexbox 或您喜欢的布局方法。
除了第一个之外,我们在视觉上隐藏并绝对定位列表项中的所有图像。
我们应用于演示的 CSS 规则如下所示:
.cards {
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(4, 1fr);
}
.card {
position: relative;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.5);
}
.card:hover {
cursor: pointer;
}
.card img:not(:first-of-type) {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
opacity: 0;
}
.card img.is-visible {
opacity: 1;
}
使用其他几种重置样式(从无序列表中删除项目符号,为主体提供背景颜色等),我们最终得到以下结果:
See the Pen Mouse hover step 2 by Envato Tuts+ (@tutsplus) on CodePen.
3. javaScript
让我们添加以下两行,以便当所有页面资源准备好时, init 执行该函数。每次调整浏览器窗口大小时,我们也会触发它。
window.addeventListener("load", init);
window.addEventListener("resize", init);
在这个函数内部,发生了很多事情;首先我们遍历卡片。
function init() {
const cards = document.queryselectorAll(".card");
cards.forEach(el => {
// actions here
});
}
循环子图像
接下来,对于每张卡片,我们在不考虑第一张图像的情况下检索子图像的数量。
注意:默认情况下,第一个图像是可见的,而不是绝对定位的。
const numOfChildImages = el.querySelectorAll("img:not(:first-of-type)").length;
如果至少有一个子图像,我们会执行以下操作:
计算卡片的宽度(第一张图片的宽度)和...
...通过将卡片的宽度除以子图像的数量将卡片分成相等的部分。
if (numOfChildImages > 0) {
const { width } = el.getBoundingClientRect();
const parts = width / numOfChildImages;
}
为了更好地理解这一点,我们假设我们的第一张卡片是 235px 宽。这张卡片包含四张图片(记住我们忽略了第一张图片),所以除法 235px/4 会给我们 58.75px。那么这个号码的作用是什么?好吧,它创建了我们的范围,所以在卡片悬停时,我们跟踪鼠标 X 位置,检查它的范围,最后显示适当的图像。
对于我们的简单示例,以下是生成的范围:
鼠标 X 位置 | 要显示的图像 |
---|---|
0<X≤58.75px | 第1 |
58.75px<X≤117.5px | 第2 |
117.5px<X≤176.25px | 第3 |
176.25px<X≤235px | 第 4 |
请注意,“要显示的图像”列显示了应该从四个子图像池中显示的图像(我们再次排除了第一个可见图像)。
现在我们知道了需求,让我们将它们转换为代码。仍然在循环中,我们监听mousemove事件。
// hover cards
el.addEventListener("mousemove", e => {
// do stuff here
});
当此事件触发时,我们执行以下操作:
获取鼠标指针相对于“悬停”卡片而不是相对于浏览器窗口的 X 坐标。
is-visible从所有卡片图像中删除类。
根据鼠标位置显示适当的图像(参见上表的示例)。
实现上述行为的部分代码如下:
el.addEventListener("mousemove", e => {
//1
const xPos = e.pageX - el.offsetLeft;
//2
removeIsVisibleClass();
//3
switch (numOfChildImages) {
case 1:
if (xPos > 0 && xPos <= parts) {
addClass(el, "img:nth-child(2)");
}
break;
case 2:
if (xPos > 0 && xPos <= parts) {
addClass(el, "img:nth-child(2)");
} else if (xPos > parts && xPos <= parts * 2) {
addClass(el, "img:nth-child(3)");
}
break;
// more cases below
}
});
如您所见,有两个自定义函数。首先,removeIsVisibleClass负责is-visible从相应图像中删除类的函数。其次, addClass 负责将is-visible类添加到目标图像的更通用的功能。
这是他们的签名:
function removeIsVisibleClass() {
if (document.querySelector("img.is-visible")) {
document.querySelector("img.is-visible").classList.remove("is-visible");
}
}
function addClass(parent, child, className = "is-visible") {
parent.querySelector(child).classList.add(className);
}
到目前为止,我们已经看到每次将鼠标悬停在卡片上时会发生什么。现在让我们讨论相反的情况。换句话说,如果我们停止将鼠标悬停在一张卡片上会发生什么。在这种情况下,应显示第一个初始图像:
// inside cards loop
el.addEventListener("mouseleave", () => {
removeIsVisibleClass();
});
See the Pen How to Make Multi-Preview Thumbnails With JavaScript Mouse Events by Envato Tuts+ (@tutsplus) on CodePen.
4. 浏览器支持
我们的演示应该在大多数桌面浏览器中运行良好。不过有几点注意事项:
该演示使用了并非所有浏览器都支持的 CSS Grid和foreach循环。如果您更喜欢使用回退,则两种情况都有替代解决方案。
该演示在所有屏幕/设备上的工作方式相似,并未针对小屏幕/触摸设备进行优化。对于我们的简单演示,这很好,但在实际项目中,您可能希望将此实现仅限于更大的屏幕(或非触摸设备)。
最后,像往常一样,我们使用 Babel 将 ES6 代码编译为 ES5。
结论
在本教程中,我们设法利用 JavaScript 鼠标事件构建了一个有趣的悬停效果。希望你已经受到足够的启发来创造一些令人惊叹的东西。