本期精读的文章是:best practices for modals overlays dialog windows。
1 引言
我为什么要选这篇文章呢?
- 前端工程师今天在外界是怎么定位的。很多人以为前端都应该讨论架构层面的问题,其实不仅仅在此,我们不应该忽视交互体验这件事。
- 对于用户体验的追求前端工程师从来没有停止过,而模态框在产品中的出现出现过很多争议,我想知道我们是怎么思考这件事的。
2 内容概要
来自 Wikipedia 的定义:模态框是一个定位于应用视窗定层的元素。它创造了一种模式让自身保持在一个最外层的子视察下显示,并让主视窗失效。用户必须在回到主视窗前在它上面做交互动作。
模态框用处
- 抓住用户的吸引力
- 需要用户输入
- 在上下文下显示额外的信息
- 不在上下文下显示额外的信息
不要用模态框显示错误、成功或警告的信息。保持它们在页面上。
模态框的组成
- 退出的方式。可以是模态框上的一个按钮,可以是键盘上的一个按键,也可以是模态框外的区域。
- 描述性的标题。标题其实给了用户一个上下文信息。让用户知道他现在在哪个位置作操作。
- 按钮的内容。它一定要是可行动的,可以理解的。不要试图让按钮的内容让用户迷惑,如果你尝试做一个取消动作,但框内有一个取消的按钮,那么我是要取消一个取消呢,还是继续我的取消。
- 大小与位置。模态框的大小不要太大或太小,不应该。模态框的位置建议在视窗中间偏上的位置,因为在移动端如果太低的话会失去很多信息。
- 焦点。模态框的出现一定要吸引你的注意力,建议键盘的焦点也切换到框内。
- 用户发起。不要对用户造成惊吓。用用户的动作,比如一个按钮的点击来触发模态框的出现。
模态框在移动端
模态框在移动端总是不是玩转得很好。其中一个原因是一般来说模态框都太大了,占用了太多空间。建议增加设备的按键或内置的滚动条来操作,用户可以左移或放大缩小来抓住模态框。
无障碍访问
- 快捷键。我们应该考虑在打开,移动,管理焦点和关闭时增加对模态框的快捷键。
- ARIA。在前端代码层面加上 aria 的标识,如 Role = “dialog” , aria-hidden, aria-label
3 精读
模态框定位
首先,Model 与 Toast、Notification、Message 以及 Popover 都会在某个时间点被触发弹出一个浮层,但与 Modal(模态框)还是有所不同的。定义上看,上述组件都不属于模态框,因为模态框有一个重要的特性,即阻塞原来主视窗下的操作,只能在框内作后续动作。也就是说模态框从界面上彻底打断了用户心流。
当然,这也是我们需要讨论的问题,如果只是一般的消息提醒,可以用信息条、小红点等交互形式,至少是不阻塞用户操作的。在原文末引用的 10 Guidelines to Consider when using Overlays 一文中,第 8 条强调了模态框不到万不得以不应该使用。这时我们应该思考什么情况下你非常希望他不要离开页面,来读框内的信息或作操作呢?
反过来说,模态框有什么优点呢?要知道比起页面跳转来说,模态框的体验还是要轻量的多。例如,用户在淘宝上看中了一款商品,想登陆购买,此时弹出登陆模态框的体验就要远远好于跳转到登陆页面,因为用户在模态框中登陆后,就可以直接购买了。其次,模态框的内容对于当前页面来说是一种衍生或补充,可以让用户更为专注去阅读或者填写一些内容。
也就是说,当我们设计好模态框出现的时机,流畅的弹出体验,必要的上下文信息,以及友好的退出反馈,还是完全可以提升体验的。模态框的目的在于吸引注意,但一定需要提供额外的信息,或是一个重要的用户操作,或是一份重要的协议确认。在本页面即可完成流程或信息告知。
合理的使用模态框
我们也总结了一些经验,更好地使用模态框。
- 内容是否相关。模态框是作为当前页面的一种衍生或补充,如果其内容与当前内容毫不相干,那么可以使用其他操作(如新页面跳转)来替代模态框;
- 模态框内部应该避免有过多的操作。模态框应该给用户一种看完即走,而且走的流畅潇洒的感觉,而不是利用繁杂的交互留住或牵制住用户;
- 避免出现一个以上的模态框。出现多个模态框会加深了产品的垂直深度,提高了视觉复杂度,而且会让用户烦躁起来;
- 不要突然打开或自动打开模态框,这个操作应该是用户主动触发的;
还有两种根据实际情况来定义:
- 大小。对于模态框的大小应该要有相对严格的限制,如果内容过多导致模态框或页面出现滚动条,一般来说这种体验很糟糕,但如果用于展示一些明细内容,我们可能还是会考虑使用滚动条来做;
- 开启或关闭动画。现在有非常多的设计倾向于用动画完成流畅的过渡,让 Modal 变得不再突兀,dribble 上有很多相关例子。但在一些围绕数据来做复杂处理的应用中,如 ERP、CRM 产品中用户通常关注点都在一个表单和围绕表单做的一系列操作,页面来回切换或复杂的看似酷炫的动画可能都会影响效率。用户需要的是直截了当的完成操作,这时候可能就不需要动画,用户想要的就是快捷的响应。
举两个例子,Facebook 在这方面给我们很好的 demo,它的分享模态框与主视窗是在同一个位置,给人非常流畅的体验。还看到一个细节,从主视窗到模态框焦点上的字体会变大。对比微博,它就把照片等分享形式直接展示出来,焦点在输入框上时也没有变化。
第二个例子是 Quora,Quora 主页呈现的是 Feed 流,点击标题就会打开一个模态框展示它回答的具体内容,内容里面是带有滚动条的,按 ESC 键就可以关闭。非常流畅的体验。相比较之下知乎首页想要快速看内容得来回切换。
可访问性的反思
Accessibility 翻译过来是『无障碍访问』,是对不同终端用户的体验完善。每一个模态框,都要有通过键盘关闭的功能,通常使用ESC键。似乎我们程序员多少总会把我们自我的惯性思维带进实现的产品,尤其是当我们敲着外置的键盘,用着 PC 的时候。
下面的这些问题都是对可访问性的反思:
- 用户可能没有鼠标,或者没有键盘,甚至可能既没有鼠标也没有键盘,只使用的是语音控制?你让这些用户如何退出
- 很多的 Windows PC 都已经获得了很好的触屏支持,而你的网页依旧只支持了键盘跟鼠标?
- 在没有苹果触摸板的地方,横向滚动条是不是一个逆天的设计?
- 在网页里,使用 Command(Ctrl) and /- 和使用触摸板的缩放事件是两个不同的表现?
- 如果你的终端用户没有好用的触摸板,但是他的确看不清你的网页上的内容。如果他用了前者,你能不能保证你的网页依然能够正常展示内容?
可访问性一直都是产品极其忽视的,在文章的最佳实践最后特别强调了它是怎么做的,对我们这些开发者是很好的督促。
模态框代码实现层面
前端开发还是少不了代码层面的实现,业务代码对于有状态或无状态模态框的使用方式存在普遍问题。
对有状态模态框来说,很多库会支持 .show
直接调用的方式,那么模态框内部渲染逻辑,会在此方法执行时执行,没有什么问题。不过现在流行无状态模态框(Stateless Modal),模态框的显示与否交由父级组件控制,我们只要将模态框代码预先写好,由外部控制是否显示。
这种无状态模态框的方式,在模态框需要显示复杂逻辑的场景中,会自然将初始化逻辑写在父级,当模态框出现在循环列表中,往往会引发首屏触发 2-30 次模态框初始化运算,而这些运算最佳状态是模态框显示时执行一次,由于模态框同一时间只会出现一个,最次也是首屏初始化一次,但下面看似没问题的代码往往会引发性能危机:
代码语言:javascript复制const TdElement = data.map(item => {
return (
<Td>
<Button>详情</Button>
<Modal show={item.show} />
</Td>
)
});
上面代码初始化执行了 N 个模态框初始化代码,显然不合适。对于 table 操作列中触发的模态框,所有行都对应一个模态框,通过父级中一个状态变量来控制展示的内容:
代码语言:javascript复制class Table extends Component {
static state = {
activeItem: null,
};
render() {
const { activeItem } = this.state;
return (
<div>
<Modal show={!!activeItem} data={activeItem} />
</div>
);
}
}
这种方案减少了节点数,但是可能会带来的问题是,每次模态框被展示的时候,触发是会是模态框的更新 (componentDidUpdate) 而不是新增。当然结合 table 中操作的特点,我们可以这样优化:
代码语言:javascript复制{activeItem ? <Modal show={true} data={activeItem} /> : null}
补充阅读
总结
这篇讲的是最佳实践,而且是 UX 层面的。但我们还是看到一些同学提出了相反的意见,我总结下就是不同的产品或不同的用户带给我们不同的认识。这时候是不是要死守着『最佳实践』呢?这时候,对于产品而言,我们可以采集用户研究的方法去判断,用数据结论代替感官上的结论。
另外,可访问性在这两年时不时会在一些文章中看到,但非常少。这是典型的长尾需求,很多研发在做产品只考虑 90% 的用户,不清楚我们放弃的一部分用户的需求。这是从产品到研发整体的思考的缺失。