从掘金首页列表一个小 Bug 说起

1,476 阅读5分钟

在任何场景下,公开的指出别人的 Bug 都难免会显得情商有点太低。但相信掘金这样优秀且开放的平台是容得下不同声音的,并且这个点我觉得值得和大家分享,所以多有得罪,还请见谅。

Bug点

掘金首页的列表出现了 <a/> 标签的多级嵌套。虽然有在 JS 里面处理父级跳转的逻辑,但这个在我的认知里好想是不太符合规范的。

比如我们直接在 VSCode 里面写一个嵌套的 <a/> ,会发现在 Chrome 中这个嵌套的逻辑会被尝试拍平成两个同级的<a/> 标签。

掘金首页之所以依然保持了嵌套的逻辑,我猜测是因为主页的列表是用 JS 动态渲染的,然后略过了浏览器的修复逻辑。

不成熟小建议

只说别人不对,而不提出解决方案也是容易被打的。

如果我们去掉掘金首页的列表的一些多余的项,大概就会留下我们上图这样的基础结构。

这种数据列表是我们网页中非常常见的。通常为了增加用户体验,虽然已经有点击显示更多的链接,从视觉上我们仍然会让整个 <li> 的点击触发点击更多链接的逻辑。

所以添加一个包裹整个 <li/> 内部的元素的 <a/>链接,是一个比较符合直觉的处理逻辑。因为总不可能用 JS 去监听 <li/> 的点击事件然后去触发更多这个链接的跳转吧?

我这边能想到一个比较讨巧的方案是这样:

让我们的 <a/> 直接绝对定位盖在我们整个 <li/> 区域,这样我们既不需要修改 DOM 结构,也不用通过 JS 去添加联动事件,就能实现同样的效果。

当然这里有两个小问题需要注意:

  1. 更多链接 在作为遮罩的同时也遮住了后面的两个链接,这两个链接就没法通过鼠标触碰到。当然整个解决方案也比较简单,只需要给后面两个链接设定一个比 更多链接 更高的z-index就好。因为这里我并没有给更多链接设置 z-index 所以,我只需要让后续元素设置 position:relative 就好。

  1. 更多链接 的文案会显示在顶部,这里需要做一个隐藏处理。隐藏元素的方法有很多,这里推荐使用 text-indent 负值来解决。但是这里不推荐是用 opacity: 0; 来隐藏元素。因为 opacity会同样让 <a/> 元素在 focus 状态下的外发光消失。这个对于无障碍访问不友好。

拓展

对于这种创建空白遮罩的处理我们姑且称其为幽灵遮罩

幽灵遮罩 对于像上一节讲到的拓展用户可点击区域,又或者你想让用户看到元素,但又不想要用户可以操作的类似场景是有奇效的。

这里拿起点小说的阅读页举例,我整个页面想要点击除开书封和黄色区域以外的区域都触发工具栏。

如果用 JS 去判断,就要监听整个页面的点击事件,然后判断这个点击事件是否冒泡到书封或者是黄色区域。如果是则阻止这个点击事件,如果不是则呼出工具栏。并且如果这个页面后续的按钮增加,这边的 JS 逻辑就会随着增加,会有一种费力不讨好的感觉。

而此时如果采用 幽灵遮罩 的方案。很简单,你只需要创建一个幽灵遮罩, 将呼出工具栏的事件绑定到 幽灵遮罩 上。对于有其它点击逻辑的元素,只需要将其 z-index设定到超过 幽灵遮罩 的地方搞定。全局就一个JS 绑定事件,哪有啥冒泡不冒泡的逻辑。后续拓展也是只需要设置 z-index 简单又方便。

再比如,你有一个复杂的表单,你在点击了 submit 按钮,然后数据正在发送的过程中。你不想让用户操作其它的控件,或者多次点击 submit。你只需要在用户点击 submit 的时候,让这个 幽灵遮罩 出现,loading 完成之后让 幽灵遮罩 消失。

这样用户所有在 loading 状态下的点击都会被 幽灵遮罩 拦截,你甚至可以直接在用户点击 幽灵遮罩 的之后,弹出一个 toast 告诉用户当前正在 loading 当中。而这整个逻辑如果不用 幽灵遮罩 实现其实是满复杂的。

并且这整个过程用户是无感知的。因为,之所以叫 幽灵遮罩 就是用户看不到的遮罩。

one more thing

讲到 幽灵遮罩 又不得不提一个与之类似的属性 pointer-events: none;

  • 幽灵遮罩 是让用户看不到,但是又能碰到;
  • pointer-events: none; 是让用户看到,但是碰不到;

这个属性也同样适用于,你不想让 submit 按钮被用户多次点击触发多次submit的逻辑。只需要在用户点击了按钮之后,里面给按钮设定一个 pointer-events: none; 属性,在比如说 loading 完或者 500ms 之后再去掉这个属性。这样就能很方便的做到点击截流。

END

整个逻辑都属于个人经验之谈,有不正确的欢迎指正。

也欢迎大家分享使用 幽灵遮罩 解决棘手问题的类似场景。